zoukankan      html  css  js  c++  java
  • 《C#高级编程》读书笔记(五):委托

    1,委托的概念

        当需要把方法传递给其他方法时,需要调用委托。委托只是一种特殊类型的对象,其特殊之处在于,我们之前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。

        委托的类型安全性非常高。在定义委托时,必须给出它所表示的方法的签名和返回类型等全部细节。

    2,使用委托的场景

        给一个方法传递的参数也是方法,而且编译时不知道第二个方法是什么,这个信息只能在运行时得到,所以把第二个方法作为参数传递给第一个方法。

    3,简单的例子

            public static string StrAddTwo(string x)
            {
                return x + 2;
            }
    
            public static string StrAddThree(string x)
            {
                return x + 3;
            }
    
            public delegate string StrAdd(string x);
    
            static void Main(string[] args)
            {
                StrAdd strAdd = StrAddTwo;
                Console.WriteLine(strAdd("Test"));
                strAdd = StrAddThree;
                Console.WriteLine(strAdd("Test"));
    
                Console.ReadKey();
            }

     4,更贴切点的例子

        然而,委托并不是为了这些简单的操作而设计的,所以,必须用一个足够复杂的例子才能体现委托的必要。

        首先,看一个给int数组进行冒泡排序的方法,代码如下:

            static void Main(string[] args)
            {
                int[] arr = new[] {5, 2, 4, 9, 15};
                Sort(arr);
    
                Console.ReadKey();
            }
    
            static void Sort(int[] sortArray)
            {
                bool swapped = true;
                do
                {
                    swapped = false;
                    for (int i = 0; i < sortArray.Length-1; i++)
                    {
                        if (sortArray[i]>sortArray[i+1])
                        {
                            int temp = sortArray[i];
                            sortArray[i] = sortArray[i + 1];
                            sortArray[i + 1] = temp;
                            swapped = true;
                        }
                    }
                } while (swapped);
    
                //查看排序结果
                foreach (var item in sortArray)
                {
                   Console.WriteLine(item);
                }
            }

         这个方法只适合于int类型,但我们希望Sort()方法能给任何对象排序,那么上面代码中的if (sortArray[i]>sortArray[i+1])就有问题了,因为它需要比较数组中的两个对象,看看哪一个更大。可以对int进行这样的比较,对如何对没有实现“<”运算符的新类进行比较?答案是能识别该类的客户端代码必须在委托中传递一个封装的方法,这个方法可以进行比较。

        public class BuddleSorter
        {
            public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
            {
                bool swapped;
                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);
            }
        }

        再定义一个类,并实现它的comparison方法:

    class Employee
        {
            public Employee(string name, decimal salary)
            {
                Name = name;
                Salary = salary;
            }
            public string Name { get; set; }
            public decimal Salary { get; set; }
    
            public override string ToString()
            {
                return string.Format($"{Name},{Salary:C}");
            }
    
            public static bool CompareSalary(Employee e1, Employee e2)
            {
                return e1.Salary < e2.Salary;
            }
        }

        测试一下:

            static void Main(string[] args)
            {
                Employee[] employees =
                 {
                    new Employee("Bugs Bunny",20000),
                    new Employee("Elmer Fudd",10000),
                    new Employee("Daffy",25000),
                    new Employee("Wile",100000.38m),
                    new Employee("Forhorn",23000),
                    new Employee("Road Runner",50000)
                };
                BuddleSorter.Sort(employees, Employee.CompareSalary);
                foreach (var employee in employees)
                {
                    Console.WriteLine(employee);
                }
    
                Console.ReadKey();
            }

        输出结果:

    Elmer Fudd,¥10,000.00
    Bugs Bunny,¥20,000.00
    Forhorn,¥23,000.00
    Daffy,¥25,000.00
    Road Runner,¥50,000.00
    Wile,¥100,000.38

    5,多播委托

        多播委托可以包含多个方法,这样的委托称为多播委托。如果调用多播委托,就可以按照顺序调用多个方法。示例代码:

    Action<double> operations = MathOperations.MultiplyByTwo;
                operations += MathOperations.Square;
                ProcessAndDispayNumber(operations, 2.0);
                ProcessAndDispayNumber(operations,7.94);
                ProcessAndDispayNumber(operations,1.414);
                Console.ReadLine();

        多播委托的缺点:如果其中一个方法,如果其中一个方法抛出异常,委托迭代将会终止,不会执行后面的方法。这时需要调用Delegate类定义的GetInvocationList()方法,它返回一个Delegate对象数组,现在可以使用这个委托调用与委托直接相关的方法,捕获异常,并继续下一次迭代。

    6,匿名方法

        匿名方法就是用作委托的参数的一段代码。如下所示:

    string mid = ",middle part,";
                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"));

     7,lambda表达式以及闭包

    double d = 4;
    Func<double,  double> oneParams = (x) => x * d;
    Console.WriteLine(oneParams(3));
    //输出12

     8,使用foreach语句的闭包

    var values = new List<int>() {1,2,3};
                var funcs = new List<Func<int>>();
    
                foreach (var value in values)
                {
                    funcs.Add(()=>value);
                }
    
                foreach (var f in funcs)
                {
                    Console.WriteLine(f());
                }

    输出:

    1

    2

    3

        这和c#4.0是不一样的,需要注意

    9,事件

        事件基于委托,为委托提供了一种发布/订阅机制。

        1)事件发布程序

    using System;
    
    namespace Demo.Delegates
    {
        public class CarInfoEventArgs : EventArgs
        {
            public CarInfoEventArgs(string car)
            {
                this.Car = car;
            }
    
            public string Car { get; set; }
        }
    
        public class CarDealer
        {
            public event EventHandler<CarInfoEventArgs> NewCarInfo;
    
            public void NewCar(string car)
            {
                Console.WriteLine("CarDealer,new car {0}",car);
                RaiseNewCarInfo(car);
            }
    
            protected virtual void RaiseNewCarInfo(string car)
            {
                EventHandler<CarInfoEventArgs> newCarInfo = NewCarInfo;
                if (newCarInfo != null)
                {
                    newCarInfo(this,new CarInfoEventArgs(car));
                }
            }
        }
    }

        2)事件侦听器

    using System;
    
    namespace Demo.Delegates
    {
        public class Consumer
        {
            private readonly string name;
    
            public Consumer(string name)
            {
                this.name = name;
            }
    
            public void NewCarIsHere(object sender, CarInfoEventArgs e)
            {
                Console.WriteLine($"{name}: car {e.Car} is new");
            }
        }
    }

        3)测试一下:

    static void Main(string[] args)
            {
                var dealer = new CarDealer();
    
                var michael = new Consumer("Michael");
                dealer.NewCarInfo += michael.NewCarIsHere;
    
                dealer.NewCar("Ferrari");
    
                var sebastian = new Consumer("Sebastian");
                dealer.NewCarInfo += sebastian.NewCarIsHere;
    
                dealer.NewCar("Mercedes");
    
                dealer.NewCarInfo -= michael.NewCarIsHere;
                
                dealer.NewCar("Red Bull Racing");
    
                Console.ReadKey();
            }

        输出结果:

    CarDealer,new car Ferrari
    Michael: car Ferrari is new
    CarDealer,new car Mercedes
    Michael: car Mercedes is new
    Sebastian: car Mercedes is new
    CarDealer,new car Red Bull Racing
    Sebastian: car Red Bull Racing is new

     10,弱事件

    using System.Windows;
    
    namespace Demo.Delegates
    {
        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);
            }
    
            public static WeakCarInfoEventManager CurrentManager
            {
                get
                {
                    var manager = GetCurrentManager(typeof(WeakCarInfoEventManager)) 
                        as WeakCarInfoEventManager;
                    if (manager == null)
                    {
                        manager = new WeakCarInfoEventManager();
                        SetCurrentManager(typeof(WeakCarInfoEventManager),manager);
                    }
                    return manager;
                }
            }
    
            void CarDealer_NewCarInfo(object sender, CarInfoEventArgs e)
            {
                DeliverEvent(sender,e);
            }
    
            protected override void StartListening(object source)
            {
                (source as CarDealer).NewCarInfo += CarDealer_NewCarInfo;
            }
    
            protected override void StopListening(object source)
            {
                (source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo;
            }
        }
    }

      修改事件监听器:

    using System;
    using System.Windows;
    
    namespace Demo.Delegates
    {
        public class Consumer:IWeakEventListener
        {
            private readonly string name;
    
            public Consumer(string name)
            {
                this.name = name;
            }
    
            public void NewCarIsHere(object sender, CarInfoEventArgs e)
            {
                Console.WriteLine($"{name}: car {e.Car} is new");
            }
    
            public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
            {
                NewCarIsHere(sender,e as CarInfoEventArgs);
                return true;
            }
        }
    }

        测试一下:

    static void Main(string[] args)
            {
                var dealer = new CarDealer();
    
                var michael = new Consumer("Michael");
                WeakCarInfoEventManager.AddListener(dealer,michael);
    
                dealer.NewCar("Ferrari");
    
                var sebastian = new Consumer("Sebastian");
                WeakCarInfoEventManager.AddListener(dealer, sebastian);
    
                dealer.NewCar("Mercedes");
    
               WeakCarInfoEventManager.RemoveListener(dealer,michael);
                
                dealer.NewCar("Red Bull Racing");
    
                Console.ReadKey();
            }

     11,泛型弱事件管理器

        使用这个类时,不再需要为每个事件实现一个自定义的弱事件管理器,也不需要让事件的消费者实现接口IWeakEventsListener。

    static void Main(string[] args)
            {
                var dealer = new CarDealer();
    
                var michael = new Consumer("Michael");
                WeakEventManager<CarDealer,CarInfoEventArgs>.AddHandler(dealer,
                    "NewCarInfo",michael.NewCarIsHere);
                
                dealer.NewCar("Ferrari");
    
                var sebastian = new Consumer("Sebastian");
                WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer,
                   "NewCarInfo", sebastian.NewCarIsHere);
    
                dealer.NewCar("Mercedes");
    
                WeakEventManager<CarDealer, CarInfoEventArgs>.RemoveHandler(dealer,
                   "NewCarInfo", sebastian.NewCarIsHere);
    
                dealer.NewCar("Red Bull Racing");
    
                Console.ReadKey();
            }
  • 相关阅读:
    洛谷P1661 扩散
    Vijos1056 图形面积
    Python爬取猪肉价格网并获取Json数据
    C#中巧用Lambda表达式实现对象list进行截取
    Winform中在ZedGraph中最多可以添加多少条曲线
    Nginx配置实例-动静分离实例:搭建静态资源服务器
    解决pip使用异常No module named 'pip'
    C#在循环中使用Random时生成的随机数相同的解决办法
    Winform中自定义ZedGraph右键复制成功后的提示
    C#中巧用Lambda进行数据的筛选查询等处理
  • 原文地址:https://www.cnblogs.com/khjian/p/5633140.html
Copyright © 2011-2022 走看看