zoukankan      html  css  js  c++  java
  • (28)C#委托,匿名函数,lambda表达式,事件

    一、委托

    委托是一种用于封装命名和匿名方法的引用类型。

    把方法当参数,传给另一个方法(这么说好理解,但实际上方法不能当参数,传入的是委托类型),委托是一种引用类型,委托里包含很多方法的引用

    创建的方法和声明的委托返回值类型相同,参数个数相同,参数类型相同时。 这个方法就满足属于这个委托(创建的方法是 public 或者是static 都没影响,只要前三项满足即可)

    用普通方式创建一个委托

    //声明委托(声明委托的位置也可以在class内部)
        public delegate string del(double a, double b);
        class Program
        {
            //定义一个求和方法
            public string sumResult(double a, double b)
            {
                double sum = a + b;
                string str1 = Convert.ToString(sum);
                string str2 = "结果等于" + str1;
                return str2;
            }
            //定义一个乘积方法
            public string multiplyResult(double a, double b)
            {
                double sum = a * b;
                string str1 = Convert.ToString(sum);
                string str2 = "结果等于" + str1;
                return str2;
            }
            static void Main(string[] args)
            {
                Program p = new Program();
           //加载一个相加方法
                del sr = new del(p.sumResult); 
    //可以简便方式加载一个方法 del mr = p.multiplyResult; //调用委托对象传入参数,用string类型接收结果 string str1 = sr(2, 3); string str2 = mr(2, 3); Console.WriteLine(str1); Console.WriteLine(str2); Console.ReadKey(); } }

    引用静态方法的方式

    //声明委托
            public delegate string del(double a, double b);
            //定义一个静态方法
            public static string sumResult(double a, double b) 
            {
                double sum = a + b;
                string str1 = Convert.ToString(sum);
                string str2 = "结果等于" + str1;
                return str2;
            }
            static void Main(string[] args)
            {
              //用普通方式创建一个委托对象
                del sr = sumResult;
              
              //用string类型接收结果
                string str = sr(2.5, 3.5);
                Console.WriteLine(str);
                Console.ReadKey();
            }
    

     把多个方法存到一个数组,利用循环来调用不同方法

       //定义个委托,传入一个double参数,返回一个double。
        delegate int delInt(int a);
        class Program
        {
            static void Main(string[] args)
            {
                //定义并初始化一个数组。数组的类型为委托类型 --没想到还以用委托当类型
                delInt[] delArray = { sumMethod.addTwo, sumMethod.XTwo};
                for (int i = 0; i < delArray.Length; i++)
                {
                    Console.WriteLine("循环{0}",i);
                    display(delArray[i], 5);
                    display(delArray[i], 10);
    
                }
                Console.ReadKey();
            }
            //显示的方法
            static void display(delInt del,int a)
            {
                int result = del(a);
                Console.WriteLine("传入{0},计算结果为{1}", a, result);
            }
        }
        //计算类
        class sumMethod
        {
            //加2
            public static int addTwo(int a)
            {
                return a + 2;
            }
            //乘2
            public static int XTwo(int a)
            {
                return a * 2;
            }
        }

    结果

    到此为止以上的例子不能说明委托的意义,这些例子都能在不适用委托的情况下编写。

     代表委托用法的例子

    ...

    ...

    ...

    二、委托的多播

    包含多个方法的委托叫做多播。

     委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。

    调用多播可以按顺序连续调用多个方法,委托的返回类型必须是void,否则只能得到委托调用最后一个方法的结果。

          delegate void del(int sum);//声明一个委托
          class MyClass
          {
            public void shuchuB(int x)
            {
                Console.WriteLine(10 * x);
                Console.ReadLine();
            }
          }
        class Program
        {
            static void shuchuA(int x)
            {
                Console.WriteLine(20 * x);
                Console.ReadLine();
            }
            static void Main(string[] args)
            {
                MyClass mc = new MyClass();
                del a = shuchuA;        //创建一个委托实例,持有一个静态方法
                del b = mc.shuchuB;     //持有一个实例方法            
                del c = a + b;          //持有以上两个方法          
                c(5);               
            }
        }
    

     

    如果

    del c = b+a+b;  

     还有自加自的方式,如果一个del里有两个相同的方法,他会优先删除后进的。(后进先出)

     del b += a; 
     del b -= a; 

    多播委托可能遇到的问题

    如果委托调用的其中一个方法抛出异常,整个迭代就会停止

            static void Main(string[] args)
            {
                //当一个委托没有参数也没有返回值时,就可以直接使用Action委托,无需声明
                Action action = one;
                action += two;
                try
                {
                    action();
                }
                catch (Exception)
                {
                    Console.WriteLine("exception");
                }          
                Console.ReadKey();
            }
            static void one()
            {
                Console.WriteLine("one");
                throw new Exception("Error");
            }
            static void two()
            {
                Console.WriteLine("two");
            }

    为避免此问题,需要迭代方法列表

            static void Main(string[] args)
            {
                //当一个委托没有参数也没有返回值时,就可以直接使用Action委托,无需声明
                Action action = one;
                action += two;
                //委托的GetInvocationList方法会按照加入顺序返回一个委托数组
                Delegate[] dels = action.GetInvocationList();
                foreach (Action act in dels)
                {
                    try
                    {
                        act();
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("exception");
                    }
                }
                Console.ReadKey();
            }

     

    三、泛型委托

    1

    2.

    Action 简化了delegate的写法

    Action返回类型必须是void,可以1-16任意参数类型<参数类型1,参数类型2,..>

    也可以传不同参数类型

    用var快速定义

    3.有返回类型用Func

    最后一个参数类型为返回类型

     三、匿名函数

    匿名方法有两种:匿名方法、Lambda表达式

    在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。 

    C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式.

    1.匿名方法

    匿名方法比普通的委托写起来简练易读,在只一次调用方法时使用起来比较方便

    语法:在创建委托实例的同时紧接着用花括号写入方法

    public delegate string del(参数1,参数2,...);
    A a =new A(参数1,参数2,...)
    {
      .....
      .....
      return  string类型;
    };

    注意几点:

    (1)A 是一个委托

    (2)创建委托实例时参数的个数和类型都必须与声明委托时一致

    (3)花括号里的返回类型必须要与声明的返回类型一致

    (4)末尾别忘加 ;

    用匿名方式创建一个委托对象

    class Program
        {
            //也可以外部声明
            public delegate string del(double a, double b);
            static void Main(string[] args)
            {
                Program p =new Program();
              //用匿名方式创建一个委托对象
                del sr = delegate(double a, double b)
                {
                    double sum = a + b;
                    string str1 = Convert.ToString(sum);
                    string str2 = "结果等于" + str1;
                    return str2;
                };       
              //用string类型接收结果
                string str = sr(2.5, 3.5);
                Console.WriteLine(str);
                Console.ReadKey();
            }
        }
    

    2、用lambda方法实现

     通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数。 Lambda 表达式对于编写 LINQ 查询表达式特别有用。

    调用时声明的方法,用一次就没。不用再单独声明个方法

    格式:

     若要创建 lambda 表达式,需要在 Lambda 运算符 =>左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。

      例如,lambda 表达式 x => x * x 指定名为 x 的参数并返回 x 的平方值

     单行lambda 表达式

        class Program
        {        public delegate double del(double a, double b);
            static void Main(string[] args)
            {
                Program p = new Program();
                //用lambda表达式的方法
                del sr = (a, b) => a + b;
                //传入多个参数要加括号,如果是一个参数可以写成
                //del sr = a => a * a;
    
                //开头注意声明的类型和下面这个接收的变量都是double
                double d = sr(2.5, 3.5);
                Console.WriteLine(d);
                Console.ReadKey();
            }
        }

     多行lambda 表达式

    单行表达式省略了return、花括号和分号。用多行表达式时都要补充上。

                Program p = new Program();
                del sr = (a, b) =>
                {
                    return a + b;
                };

     泛型Lambda,可以重用

     语法糖可省略 lambda参数类型

    语法糖 不需要再创建实例,省略了 new Func<int,int,int>

    这个是最常用的方式

     进阶

    泛型委托类型推断,省略了 <int>

     3.闭包

    。。。。。

     

    委托隐式异步调用 

     

    使用接口取代委托减少复杂性和安全性(java没有委托,就是用接口的方式)

     

    四、事件

    事件是一种类似于订阅/发布的机制。

    把订阅当成一个方法,每次一订阅就会存一个方法进入委托中。等到发布的时候,一次性的发消息给哪些订阅。

    例如:

    日本丰田公司2017年生产了三款新车,(汉兰达2018,凯美瑞2018,雷凌2018)。

    家住天津王兰庄附近的‘大刘’和‘小明’从网上看到消息后给王兰庄广汽丰田4S点打电话了解这三款车

    4S点销售员告诉他们新车还没到货,让他们留下联系方式,等车到店了给你们发短信通知他们来店里看实车。

    创建一个控制台程序

      再新建一个事件发布类:ReleaseEventArgs

           再新建一个事件监听类:Consumer

       

        class ReleaseEventArgs : EventArgs
        {
            public ReleaseEventArgs(string car)
            {
                this.Car = car;
            }
            public string Car { get; set; }
    
            public class Shop
            {
                //
                public event EventHandler<ReleaseEventArgs> NewCarClass;
                public void NewCar(string car)
                {
                    Console.WriteLine("{0}",car);
                    raise(car);
                }
                protected virtual void raise(string car)
                {
                    EventHandler<ReleaseEventArgs> newCarClass = NewCarClass;
                    if (newCarClass != null)
                    {
                        newCarClass(this, new ReleaseEventArgs(car));
                    }
                }
            }
        }
        class Consumer
        {
            private string name;
    
            public Consumer(string name)
            {
                this.name = name;
            }
            public void CarArriving(object sender, ReleaseEventArgs e)
            {
                Console.WriteLine("尊敬的{0},王兰庄4S点提示您:{1}到货了",name,e.Car);
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                var shop = new ReleaseEventArgs.Shop();
                var daliu = new Consumer("大刘");
                var xiaoming = new Consumer("小明");
                shop.NewCarClass += daliu.CarArriving;
                shop.NewCarClass += xiaoming.CarArriving;
                shop.NewCar("汉兰达2018");
                shop.NewCarClass -= daliu.CarArriving;
                shop.NewCar("凯美瑞2018");
                var zhoujie = new Consumer("周杰");
                shop.NewCarClass += zhoujie.CarArriving;
                shop.NewCar("雷凌2018");
    
                Console.ReadKey();
            }
        }

    https://www.cnblogs.com/gq0324/p/8177799.html

     事件

    using System;
    
    namespace ConsoleApp4
    {
        class Program
        {
            static void Main(string[] args)
            {
                Business bus = new Business();
                bus.Price = 11;
                bus.Changed += Test_Changed;
    
                bus.Price = 22;
                bus.Price = 33;
                Console.WriteLine("Hello World!");
    
                void Test_Changed(object sender, ChangeEventArgs e)
                {
                    if ((e.newPrice - e.oldPrice) > 10)
                    {
                        Console.WriteLine("大于10");
                    }
                }
            }
        }
    
    
        //标准事件模式
        //一个继承事件类的子类
        public class ChangeEventArgs : EventArgs
        {
           public int oldPrice;
            public int newPrice;
            //ctor + tab
            public ChangeEventArgs(int oldPrice, int newPrice)
            {
                this.oldPrice = oldPrice;
                this.newPrice = newPrice;
            }
        }
    
    
        public class Business
        {
            int price;
    
            public event EventHandler<ChangeEventArgs> Changed;
    
            protected virtual void OnChange(ChangeEventArgs e)
            {
                Changed?.Invoke(this, e);
            }
    
            public int Price 
            {
                get { return price; }
                set 
                {
                    if (price == value) return;
                    int oldPrice = price;
                    price = value;
                    OnChange(new ChangeEventArgs(oldPrice, price));
                }
            }
        }
    }
    View Code

    ------------------------------------------------

    为什么要用委托?

    //定义    
    public delegate void del(int value);

    实际上相当于:定义了一个类

        public class del:System.MulticastDelegate
        {
            //构造函数
            public del(Object @object, IntPtr method);
            //
            public virtual void Invoke(Int32 value);
           //回调方法的异步回调
            public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object @object);
            //回调方法的异步回调
            public virtual void EndInvoke(IAsyncResult result);
        }

     

    异步调用完成时执行回调方法

    异步启动委托, 参数与要执行的方法的参数相同,另加两个可选参数

    第一个参数是一个 AsyncCallback 委托,此委托引用在异步调用完成时要调用的方法

    第二个参数是一个用户定义的对象,该对象将信息传递到回调方法

    BeginInvoke 将立即返回,而不会等待异步调用完成。

    BeginInvoke 返回可用于监视异步调用的进度的 IAsyncResult。

    委托对象.BeginInvoke();

    EndInvoke 方法用于检索异步调用的结果,它可以在调用 BeginInvoke之后的任意时间调用

    如果异步调用尚未完成,那么 EndInvoke 将阻止调用线程,直到完成异步调用

     ---------------------------------------

  • 相关阅读:
    isa与hasa的关系
    两道笔试题的解法
    无耻的WoW打钱工作室
    我正处于恶性循环中——读wowar上的一篇帖子有感
    snippets from ObjectOriented Thought Process (1)
    使用C#获取当前Windows所设定的时区
    用ASP.NET模拟Windows Service来实现定时提醒之类的功能
    乱说 缓存
    认识数据库连接
    网站sql注入的技巧与防范
  • 原文地址:https://www.cnblogs.com/buchizaodian/p/6367133.html
Copyright © 2011-2022 走看看