zoukankan      html  css  js  c++  java
  • C# 高级编程(笔记4)

    第8章 委托、Lambda表达式和事件

    1.多播委托:一个委托中包含多个方法

       如果调用多播委托,就可以按顺序连续调用多个方法,为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果,前面调用的方法返回结果被后面调用的方法返回的结果给覆盖了。

       通过一个委托调用多个方法还可能导致一个大问题。多播委托包含一个逐个调用的委托集合。如果通过委托调用的其中一个方法抛出一个异常,整个迭代就会停止。

         static void One()
         {
             Console.WriteLine("One");
             throw new Exception("Error in one");
         }
    
         static void Two()
         {
             Console.WriteLine("Two");
         }
    
         Action d1 = One;
         d1 += Two;
         try
         {
            d1();
         }
         catch (Exception)
         {
            Console.WriteLine("Exception caught");
         }
      
         ----委托只调用了第一个方法。因为第一个方法抛出了一个异常,所以委托的迭代会停止,不再调Two()方法

    在这种情况下,为了避免这个问题,应自己迭代方法列表。Delegate类定义GetInvocationList()方法,它返回一个Delegate对象数组。现在可以使用这个委托调用与委托直接相关的方法,捕获异常,并继续下一次迭代。

         static void Main()
         {
             Action d1 = One;
             d1 += Two;
    
             Delegate[] delegates = d1.GetInvocationList();
    
             foreach (Action d in delegates)
             {
                 try
                 {
                     d();
                 }
                 catch (Exception)
                 {
                     Console.WriteLine("Exception caught");
                 }
             }
         }
    
        ----得到的结果是:
        one
        EXception caught
        TWo

    2.可以为每个参数以及返回类型自定义委托
       public delegate double deleOp(double s);

    3.还可以使用Action<T>和Func<T>委托

       Action<T>委托:只带void返回类型
    泛型Action<T>委托表示引用一个void返回类型的方法。这个委托类存在不同的变体,可以传递至多16种不同的参数类型。没有泛型参数的Action类可调用没有参数的方法。Action<in T>调用带一个参数的方法,Action<in T1, in T2>调用带两个参数的方法,Action<in T1, in T2,in T3, in T4,in T5, in T6,in T7, in T8>调用带8个参数的方法。

        Func<T>允许调用带返回类型的方法。与Action<T>类似,Func<T>也定义了不同的变体,至多也可以传递16个参数类型和一个返回类型。Func<out TResult>委托类型可以调用带返回类型且无参数的方法,Func<in T, out TResult>调用带一个参数和一返回值的方法,Func<in T1, in T2,in T3,in T4,out TResult>调用带4个参数和一返回值的方法

    4.匿名方法

       还可以通过匿名方法来使用委托,

          static void Main()
          {
              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"));
          }

    ----Func <string, string>委托接受一个字符串参数,返回一个字符串。anonDel是这种委托类型的变量。不是把方法名赋予这个变量,而是使用一段简单的代码:它前面是关键字de1egate,后面是一个字符串参数;

    ----在使用匿名方法时,必须遵循两条规则。在匿名方法中不能使用跳转语句(break、goto或continue)跳到该匿名方法的外部,反之亦然:匿名方法外部的跳转语句不能跳到该匿名方法的内部。在匿名方法内部不能访问不安全的代码。另外,也不能访问在匿名方法外部使用的ref和out参数。但可以使用在匿名方法外部定义的其他变量。

    ----从c#3.0开始,可以使用Lambda表达式替代匿名方法

    5.Lambda表达式

    static void Main()
    {
        string mid = ", middle part,";
    
        Func<string, string> lambda = param =>
        {
            param += mid;
            param += " and this was added to the string.";
            return param;
        };
    
        Console.WriteLine(lambda("Start of string"));
    }

    ----Lmbda运算符"=>"的左边列出了需要的参数。Lambda运算符的右边定义了赋予委托变量lambda的方法的实现代码[即"=>"的右边为方法体]。

    Lambda表达式有几种定义参数的方式:

    A.如果只有一个参数,只写出参数名就足够了。下面的Lambda表达式使用了参数s。因为委托类型定义了一个string参数,所以s的类型就是string。实现代码调用String.Format()方法来返回一个字符串,

    Func<string, string> oneParam = s => String.Format("change uppercase {0}", s.ToUpper());
    Console.WriteLine(oneParam("test"));

    B.如果委托使用多个参数,就把参数名放在括号中。这里参数x和y的类型是double,由Func<double,double,double>委托定义:

    Func<double, double, double> twoParams = (x, y) => x * y;
    Console.WriteLine(twoParams(3, 2));
    
    上面也可以写成:Func
    <double, double, double> twoParams = (x, y) => {return x * y;};

      ----如果Lambda表达式只有一条语句,在方法块内就不需要括号和return语句,因为编译器会添加一条隐式的return语句。

      ----但是,如果在Lambda表达式的实现代码中需要多条语句,就必须添加括号和return语句:

      ----为了方便,可以在括号中给变量名添加参数类型:

    Func<double, double, double> twoParams = (double x, double y) => x * y;

    6.事件

    首先,事件是基于委托的,也可以说事件变量其实就是一个委托类型的变量,如

    public event EventHandler<CarInfo> newCarEvent;//声明一个事件变量
    
    //EventHandler是一个委托类型,其原型为:
    public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
                                                 where TEventArgs : EVentArgs;//支持泛型委托
    //泛型TEventArgs是关于事件的一些附加信息,就是事件发布者要告诉订阅者的信息

      好,既然是委托,那在要用到该事件的地方就得定义与委托原型一样的方法,这个就是事件订阅者要做的事情,拿客户买汽车的为例:

      //以下是两个订阅者
      //宝马汽车客户
        public class BMWBuyer
        {
            public void Buy(Object o, CarInfo e)
            {
                Console.WriteLine("{0}:{1}", "BMWBuyer", e.GetCarInfo());
            }
        }
    
      //大众汽车客户
        public class AutoDasBuyer
        {
            public void Buy(Object o, CarInfo e)
            {
                Console.WriteLine("{0}:{1}", "AutoDasBuyer", e.GetCarInfo());
            }
        }

    ----CarInfo类就是委托原型中的TEventArgs类型,也就是发布者要告诉订阅者的信息类,必须继承自EVentArgs类
      //事件附加信息CarInfo是一个类,必须继承自EventArgs,这个类中包含了本次事件的所有详细信息
        public class CarInfo : EventArgs
        {
            private String carName;
            public CarInfo(String name)
            {
                this.carName = name;
            }
    
            public String GetCarInfo()
            {
                return carName;
            }
        }

    现在,事件订阅者有了,事件的详细信息也有了,还差一个事件发布者

        public class CarFactory
        {
            public event EventHandler<CarInfo> newCarEvent;
            public void StartEvent(String carName)
            {
                if (newCarEvent != null)
                {
                    newCarEvent(this, new CarInfo(carName));//触发事件
                }
            }
        }

    最后一步是把事件发布者与订阅者关联起来,通过“+=”,很简单

            static void Main(string[] args)
            {
                CarFactory factory = new CarFactory();
    
                //
                BMWBuyer bmwBuyer = new BMWBuyer();
                factory.newCarEvent += bmwBuyer.Buy;//关联发布者与订阅者
                factory.StartEvent("BMW");//触发事件
    
                //
                AutoDasBuyer autoBuyer = new AutoDasBuyer();
                factory.newCarEvent += autoBuyer.Buy;
                factory.StartEvent("AutoDaus");
    
                Console.ReadKey();
            }

    //通过将发布者与订阅者关联上,发布者只要发布消息,所有的订阅者都可以收到这个消息

  • 相关阅读:
    コナン純黒のナイトメア20180715
    コナン純黒のナイトメア20180630
    コナン純黒のナイトメア20180629
    コナン純黒のナイトメア20180623
    コナン純黒のナイトメア20180622
    コナン純黒のナイトメア20180616
    コナン純黒のナイトメア20180613
    コナン純黒のナイトメア20180611
    单词乱记 20180607
    五周突破日语能力考试 单词5-2
  • 原文地址:https://www.cnblogs.com/notebook2011/p/2970333.html
Copyright © 2011-2022 走看看