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();
            }
  • 相关阅读:
    mysql 远程登陆不上
    hdu 5339 Untitled【搜索】
    SqlServer 书目
    passwordauthentication yes
    oracle 11g RAC ocfs2
    Oracle 11g RAC database on ASM, ACFS or OCFS2
    CentOS ips bonding
    Oracle 11g RAC features
    openStack 王者归来之 trivial matters
    openstack windows 2008 img
  • 原文地址:https://www.cnblogs.com/khjian/p/5633140.html
Copyright © 2011-2022 走看看