C#委托/Lambda表达式/事件

1.委托基本定义

委托用delegate定义,指定返回值和参数列表的函数类型,不包括数据,这些方法是不区分静态或者非静态的,可以引用一个委托实例也可以引用多个(广播)。

可以任意修饰符,可以防止在类内部,也可以在类外部,不可以在函数内部定义。


给委托对象赋值时候需要对委托构造函数传递一个参数(具体方法的引用),或者将定义的方法直接赋值给委托对象这些方法不用传递参数先真正调用时候委托对象时候才需要将参数传入,函数指针是可以运行时绑定的(迟绑定技术)。

2.委托泛型

除了自定义委托, 还可以用委托模板,Action<in T1, in T2,...>可以声明为带0到8个泛型类型参数,没有返回类型的方法
Func<in T, out TResult>可以定义0到16个泛型类型参数,有一个返回类型的方法
// 多态的委托是委托的重要作用之一,事件委托也是委托的重要作用
class BubbleSorter
    {
        static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
        {
            bool swapped = true;
            do
            {
                swapped = false;
                for (int i = 0; i < sortArray.Count - 1; i++)
                {
                    if (comparison(sortArray[i+1], sortArray[i]))
                    {
                        T temp = sortArray[i];
                        sortArray[i] = sortArray[i + 1];
                        sortArray[i + 1] = temp;
                        swapped = true;
                    }
                }
            } while (swapped);
        }
    }

// 委托实例方法
   public static bool CompareSalary(Employee e1, Employee e2)
        {
            return e1.Salary < e2.Salary;
        }

// 调用端
static void Main()
        {
            Employee[] employees =
            {
                new Employee("Bugs Bunny", 20000),
                new Employee("Elmer Fudd", 10000),
                new Employee("Daffy Duck", 25000),
                new Employee("Wile Coyote", 1000000.38m),
                new Employee("Foghorn Leghorn", 23000),
                new Employee("RoadRunner", 50000)
            };
            BubbleSorter.Sort(employees, Employee.CompareSalary);
            foreach (var employee in employees)
            {
                Console.WriteLine(employee);
            }
        }

3.委托多播+-,和使用GetInvocationList()返回委托实例

单个委托和委托数组都是单播委托
如果要定义多播委托就要使用 + += - -=这些运算符,因为委托都是System.MulticastDelegate的类,System.MulticastDelegate派生自System.Delegate,System.MulticastDelegate可以把多个 委托加减链接运算符生成一个委托实例列表。

多播委托建议返回类型是void,如果有其它返回类型,那么返回类型是最后一个委托实例的返回类型

    static void Main()
        {
            // 返回void相加的委托实例
            Action<double> operations = MathOperations.MultiplyByTwo;
            // 返回void求平方的委托实例
            operations += MathOperations.Square;

            ProcessAndDisplayNumber(operations, 2.0);
            ProcessAndDisplayNumber(operations, 7.94);
            ProcessAndDisplayNumber(operations, 1.414);
            Console.WriteLine();
        }

        static void ProcessAndDisplayNumber(Action<double> action, double value)
        {
            Console.WriteLine();
            Console.WriteLine("ProcessAndDisplayNumber called with value = {0}", value);
            // 一个委托实例里面包含了多个多播实例,那么会在当前参数下,连续执行多个多播实例
            action(value);
        }

多播委托有一个缺点就是一个抛出异常后面不会再执行了,为了避免抛出异常终止,可以用GetInvocationList()方法返回委托对象来解决。

Action d1 = One;
 d1 += Two;
 Delegate[] delegates = d1.GetInvocationList();
 foreach (Action d in delegates)
 {
  }

4.匿名委托

委托对象 =delegate(参数){委托实例代码块;}来赋值给委托对象。

static void Main()
        {
            string mid = ", middle part,";
            // 委托对象 用匿名的委托实例初始化,delegate(委托输入参数) {委托实例代码,返回委托输出类型}
            Func<string, string> anonDel = delegate(string param)
            {
                param += mid;
                param += " and this was added to the string.";
                return param;
            };
            // 用的时候还是用委托对象就可以了,调用的时候传入真正的参数
            Console.WriteLine(anonDel("Start of string"));
        }
匿名委托优点是减少编写的代码,特别是减少了很多委托实例的命名;如果需要多次调用同一个方法那么不要用匿名委托。
注意是匿名委托代码块不能中间跳转到代码块外部,也不能从外部直接跳转到匿名委托代码块中。
匿名代码块不能访问不安全的代码,不能访问匿名方法外部使用ref/out修饰的参数,其它外部变量可以用,因为委托调用时候才使用,用了外部变量需要注意意外的结果。

5.Lambda表达式

Lambda表达式用于类型是委托时简化匿名委托方法的书写,或者类型是Expression或Expression<T>时创建表达式树。
delegate deObj = param => expression;
delegate void SimpleTest();
        static void Main()
        {
            // 多个参数就()里面写,多行语句就用{},有返回值用return
            SimpleTest simpleObj = () => { Console.WriteLine("Hello Lambda world."); return; };
            simpleObj();

            int someVal = 5;
            // 这里只是定义委托方法,如果调用了外部变量,调用委托方法时外部变量被改变了会导致意外的结果
            Func<int, int> f = x => x + someVal;
            someVal = 7;
            Console.WriteLine(f(3));
        }

6.事件

事件其实就是函数指针实现,发布程序先注册事件处理函数(委托实例也叫监听器),当事件发生时候(windows wndproc会不停的检查键盘鼠标线程等事件)那么会调用发布程序调用委托实例。

1)强类型的事件,一个发布程序对应一个监听器时候建议使用:

EventsSample实例:

CarDealer.cs

using System;

namespace Wrox.ProCSharp.Delegates
{
    // 提供了事件参数,所有侦听器都可以使用该参数
    public class CarInfoEventArgs : EventArgs
    {
        public CarInfoEventArgs(string car)
        {
            this.Car = car;
        }

        public string Car { get; private set; }
    }

    public class CarDealer
    {
        public event EventHandler<CarInfoEventArgs> NewCarInfo;

        // 事件发布者,也就是委托对象定义和事件发生回调调用的地方。
        public void NewCar(string car)
        {
            Console.WriteLine("CarDealer, new car {0}", car);
            if (NewCarInfo != null)
            {
                // CarInfoEventArgs事件参数来自于发布者,函数由订阅者提供
                // public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
                NewCarInfo(this, new CarInfoEventArgs(car));
            }
        }
    }
}

Consumer.cs

using System;

namespace Wrox.ProCSharp.Delegates
{
    // 事件侦听器,调用了发布者事件的参数,提供委托(函数指针)实例给委托(函数指针)对象
    public class Consumer
    {
        private string name;

        public Consumer(string name)
        {
            this.name = name;
        }
        // public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
        // CarInfoEventArgs 事件参数来自于发布者,函数由订阅者提供
        public void NewCarIsHere(object sender, CarInfoEventArgs e)
        {
            Console.WriteLine("{0}: car {1} is new", name, e.Car);
        }
    }
}

Program.cs

namespace Wrox.ProCSharp.Delegates
{
    class Program
    {
        static void Main()
        {
            var dealer = new CarDealer();

            var michael = new Consumer("Michael");
            // 将委托实例赋值给了委托对象
            dealer.NewCarInfo += michael.NewCarIsHere;
            // 事件发生,发布者调用委托对象,发布给订阅者
            dealer.NewCar("Mercedes");

            var nick = new Consumer("Nick");
            // 增加订阅者
            dealer.NewCarInfo += nick.NewCarIsHere;

            // 发布者多播给多个订阅者
            dealer.NewCar("Ferrari");

            // 减少订阅者,如但是发布者会仍然有一个引用而无法垃圾回收订阅者资源,需要一个弱引用管理器来管理。
            dealer.NewCarInfo -= michael.NewCarIsHere;

            // 发布者发布给订阅者
            dealer.NewCar("Toyota");
        }
    }
}

WeakEventsSample实例:

CarDealer.cs

using System;

namespace Wrox.ProCSharp.Delegates
{
    public class CarInfoEventArgs : EventArgs
    {
        public CarInfoEventArgs(string car)
        {
            this.Car = car;
        }

        public string Car { get; private set; }
    }

    public class CarDealer
    {
        public event EventHandler<CarInfoEventArgs> NewCarInfo;

        public CarDealer()
        {
        }

        public void NewCar(string car)
        {
            Console.WriteLine("CarDealer, new car {0}", car);
            if (NewCarInfo != null)
            {
                NewCarInfo(this, new CarInfoEventArgs(car));
            }
        }
    }
}

Consumer.cs

using System;
using System.Windows;

namespace Wrox.ProCSharp.Delegates
{
    // 继承IWeakEventListener 为了将监听器作为 WeakEventManager管理器类的传入参数:
    // public static void AddListener(object source, IWeakEventListener listener);
    // public static void RemoveListener(object source, IWeakEventListener listener);
    public class Consumer : IWeakEventListener
    {
        private string name;

        public Consumer(string name)
        {
            this.name = name;
        }

        // 处理事件的真正逻辑
        public void NewCarIsHere(object sender, CarInfoEventArgs e)
        {
            Console.WriteLine("{0}: car {1} is new", name, e.Car);
        }

        // IWeakEventListener函数,接受事件时候的处理接口方法,在发布者真正调用时候才会进来
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            NewCarIsHere(sender, e as CarInfoEventArgs);
            return true;
        }
    }
}

WeakCarInfoEventManager.cs

using System.Windows;

namespace Wrox.ProCSharp.Delegates
{
    // 使用委托弱关联管理器,需要实现函数:
    // public static void AddListener(object source, IWeakEventListener listener);
    // protected override void StartListening(object source);

    // public static void RemoveListener(object source, IWeakEventListener listener);
    // protected override void StopListening(object source);

    public class WeakCarInfoEventManager : WeakEventManager
    {
        /**********************管理器对外接口****************************/
        public static void AddListener(object source, IWeakEventListener listener)
        {
            // 调用单例对象
            CurrentManager.ProtectedAddListener(source, listener);
        }

        public static void RemoveListener(object source, IWeakEventListener listener)
        {
            // 调用单例对象
            CurrentManager.ProtectedRemoveListener(source, listener);
        }

      /**********************管理器内部实现逻辑****************************/
        protected static WeakCarInfoEventManager CurrentManager
        {
            get
            {
                // 父类WeakEventManager定义static WeakEventManager GetCurrentManager(Type managerType)
                WeakCarInfoEventManager manager = GetCurrentManager(typeof(WeakCarInfoEventManager)) as WeakCarInfoEventManager;
                // 单例设计模式,只返回一个对象
                if (manager == null)
                {
                    manager = new WeakCarInfoEventManager();
                    // 父类定义static void SetCurrentManager(Type managerType, WeakEventManager manager);
                    SetCurrentManager(typeof(WeakCarInfoEventManager), manager);
                }
                return manager;
            }
        }

        // 外部真正事件回调时候进入这里调用,例如:dealer.NewCar("xxx");
        protected void CarDealer_NewCarInfo(object sender, CarInfoEventArgs e)
        {
            // 传递事件,WeakEventManager函数
            // 这里的CarDealer_NewCarInfo和传入委托实例之间的关系,类似map映射一一对应的关系。调用委托时候也对应过来。
            DeliverEvent(sender, e);
        }

        // AddListener时候会进入,告诉发布者委托对象加上,传入的委托实例间接转换为CarDealer_NewCarInfo的实例
        // 这里的CarDealer_NewCarInfo和传入委托实例之间的关系,类似map映射一一对应的关系。调用委托时候也对应过来。
        protected override void StartListening(object source)
        {
            (source as CarDealer).NewCarInfo += CarDealer_NewCarInfo;
        }

        // RemoveListener时候会进入,告诉发布者委托对象减去,传入的委托实例间接转换为CarDealer_NewCarInfo的实例
        protected override void StopListening(object source)
        {
            (source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo;
        }
    }
}

Program.cs

namespace Wrox.ProCSharp.Delegates
{
    class Program
    {
        static void Main()
        {
            var dealer = new CarDealer();
            var michael = new Consumer("Michael");
            // 添加到管理器,其中分发者会关联到一个管理器间接的委托实例,
            // 该间接的委托实例和真正委托实例之间有一一对应关系。
            WeakCarInfoEventManager.AddListener(dealer, michael);
            // 发布者产生事件,回调WeakCarInfoEventManager的处理函数,其中该处理函数是间接的
            // 会分发转换到真正的michael委托实例,实现间接的真正回调侦听器方法。
            dealer.NewCar("Mercedes");

            var nick = new Consumer("Nick");
            WeakCarInfoEventManager.AddListener(dealer, nick);
            dealer.NewCar("Ferrari");

            // 作用: 发布者和监听器之间就不是强类型关系,而是弱类型关系,移除监听器后不再使用就会被垃圾回收器回收
            WeakCarInfoEventManager.RemoveListener(dealer, michael);
            dealer.NewCar("Toyota");
        }
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值