• C#编程 委托 Lambda表达式和事件


    委托

    如果我们要把方法当做参数来传递的话,就要用到委托。简单来说委托是一个类型,这个类型可以赋值一个方法的引用。

    声明委托

    在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方法组成的,然后使用这个类实例化对象。在我们使用委托的时候,也需要经过这两个阶段,首先定义委托,告诉编译器我们这个委托可以指向哪些类型的方法,然后,创建该委托的实例。

    定义委托的语法如下:     

    delegate void IntMethodInvoker(int x); 

    定义了一个委托叫做IntMethodInvoker,  这个方法要带有一个int类型的参数,并且方法的返回值是void的。 定义一个委托要定义方法的参数和返回值,使用关键字delegate定义。

    使用委托

    private delegate string GetAString();
    
    static void Main(){
    	int x = 40;
    	GetAString firstStringMethod = new GetAString(x.ToString);
    	Console.WriteLine(firstStringMethod());
    }

    在这里我们首先使用GetAString委托声明了一个类型叫做fristStringMethod,接下来使用new 对它进行初始化,使它引用到x中的ToString方法上,这样firstStringMethod就相当于x.ToString,我们通过firstStringMethod()执行方法就相当于x.ToString()

    通过委托示例调用方法有两种方式

    fristStringMethod();
    firstStringMethod.Invoke();
    

    委托的赋值

    只需要把方法名给一个委托的构造方法就可以了

    GetAString firstStringMethod = new GetAString(x.ToString);

    也可以把方法名直接给委托的实例

    GetAString firstStringMethod = x.ToString;

    Action委托和Func委托

    除了我们自己定义的委托之外,系统还给我们提供过来一个内置的委托类型,Action和Func

    Action委托引用了一个void返回类型的方法,T表示方法参数

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _005_Action委托 {
        class Program {
            static void PrintString()
            {
                Console.WriteLine("hello world.");
            }
    
            static void PrintInt(int i)
            {
                Console.WriteLine(i);
            }
    
            static void PrintString(string str)
            {
                Console.WriteLine(str);
            }
    
            static void PrintDoubleInt(int i1, int i2)
            {
                Console.WriteLine(i1+i2);
            }
            static void Main(string[] args)
            {
                //Action a = PrintString;//action是系统内置(预定义)的一个委托类型,它可以指向一个没有返回值,没有参数的方法
                //Action<int> a=PrintInt;//定义了一个委托类型,这个类型可以指向一个没有返回值,有一个int参数的方法
                //Action<string> a = PrintString;//定义了一个委托类型,这个类型可以指向一个没有返回值,有一个string参数的方法 在这里系统会自动寻找匹配的方法
                Action<int, int> a = PrintDoubleInt;
                a(34, 23);
                Console.ReadKey();
                //action可以后面通过泛型去指定action指向的方法的多个参数的类型 ,参数的类型跟action后面声明的委托类型是对应着的
               
            }
        }
    }
    

    Func引用了一个带有一个返回值的方法,它可以传递0或者多到16个参数类型,和一个返回类型

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _006_Func委托 {
        class Program {
            static int Test1()
            {
                return 1;
            }
    
            static int Test2(string str)
            {
                Console.WriteLine(str);
                return 100;
            }
    
            static int Test3(int i, int j)
            {
                return i + j;
            }
            static void Main(string[] args)
            {
                //Func<int> a = Test1;//func中的泛型类型制定的是 方法的返回值类型
                //Console.WriteLine(a());
                //Func<string, int> a = Test2;//func后面可以跟很多类型,最后一个类型是返回值类型,前面的类型是参数类型,参数类型必须跟指向的方法的参数类型按照顺序对应
                Func<int, int, int> a = Test3;//func后面必须指定一个返回值类型,参数类型可以有0-16个,先写参数类型,最后一个是返回值类型
                int res = a(1, 5);
                Console.WriteLine(res);
                Console.ReadKey();
            }
        }
    }
    

    多播委托

    前面使用的委托都只包含一个方法的调用,但是委托也可以包含多个方法,这种委托叫做多播委托。使用多播委托就可以按照顺序调用多个方法,多播委托只能得到调用的最后一个方法的结果,一般我们把多播委托的返回类型声明为void。     
     

    Action action1 = Test1;     
    
    action2+=Test2;    
    
    action2-=Test1; 

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

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _008_多播委托 {
        class Program {
            static void Test1()
            {
                Console.WriteLine("test1");
                //throw new Exception();
            }
    
            static void Test2()
            {
                Console.WriteLine("test2");
            }
            static void Main(string[] args) {
                //多播委托
                Action a = Test1;
                a += Test2;//表示添加一个委托的引用 
                //a -= Test1;
                //a -= Test2;
                //if(a!=null)
                //    a();//当一个委托没有指向任何方法的时候,调用的话会出现异常null
    
                // 取得多播委托中所有方法的委托
                Delegate[] delegates = a.GetInvocationList();
                foreach (Delegate de in delegates)
                {
                    de.DynamicInvoke();
                }
                Console.ReadKey();
            }
        }
    }
    

    匿名方法

    到目前为止,使用委托,都是先定义一个方法,然后把方法给委托的实例。但还有另外一种使用委托的方式,不用去定义一个方法,应该说是使用匿名方法(方法没有名字)。

    Func<int,int,int> plus = delegate (int a,int b){     
        int temp = a+b;     
        return temp; 
    }; 
    int res = plus(34,34); 
    Console.WriteLine(res); 

    在这里相当于直接把要引用的方法直接写在了后面,优点是减少了要编写的代码,减少代码的复杂性

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _009_匿名方法 {
        class Program {
            static int Test1(int arg1, int arg2)
            {
                return arg1 + arg2;
            }
            static void Main(string[] args)
            {
                //Func<int, int, int> plus = Test1;
                //修改成匿名方法的形式
                Func<int, int, int> plus = delegate(int arg1, int arg2)
                {
                    return arg1 + arg2;
                };
                //匿名方法 本质上是一个方法,只是没有名字,任何使用委托变量的地方都可以使用匿名方法赋值
    
    
                Console.ReadKey();
            }
        }
    }
    

    Lambda表达式

    从C#3.0开始,可以使用Lambda表达式代替匿名方法。只要有委托参数类型的地方就可以使用Lambda表达式。刚刚的例子可以修改为

    Func<int,int,int> plus = (a,b)=>{ int temp= a+b;return temp; };
    int res = plus(34,34);
    Console.WriteLine(res);

    Lambda运算符“=>”的左边列出了需要的参数,如果是一个参数可以直接写  a=>(参数名自己定义),如果多个参数就使用括号括起来,参数之间以,间隔。

    如果Lambda表达式只有一条语句,在方法快内就不需要花括号和return语句,编译器会自动添加return语句。如果Lambda表达式的实现代码中需要多条语句,就必须添加花括号和return语句。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _010_Lambda表达式 {
        class Program {
            static void Main(string[] args) {
                //lambda表达式用来代替匿名方法,所以一个lambda表达式也是定义了一个方法
                //Func<int, int, int> plus = delegate(int arg1, int arg2) {
                //    return arg1 + arg2;
                //};
                //Func<int, int, int> plus = (arg1, arg2) =>// lambda表达式的参数是不需要声明类型的
                //{
                //    return arg1 + arg2;
                //};
                //Console.WriteLine(plus(90,60));
    
                Func<int, int> test2 = a => a+1;//lambda表示的参数只有一个的时候,可以不加上括号  当函数体的语句只有一句的时候,我们可以不加上大括号 也可以不加上return语句
                Func<int, int> test3 = (a) =>
                {
                    return a + 1;
                };
                Console.WriteLine(test2(34));
                Console.WriteLine(test3(34));
                Console.ReadKey();
            }
        }
    }
    

    通过Lambda表达式可以访问Lambda表达式块外部的变量。这是一个非常好的功能,但如果不能正确使用,也会非常危险。示例:

    int somVal = 5;
    Func<int,int> f = x=>x+somVal;
    Console.WriteLine(f(3));//8
    somVal = 7;
    Console.WriteLine(f(3));//10

    这个方法的结果,不但受到参数的控制,还受到somVal变量的控制,结果不可控,容易出现编程问题,用的时候要谨慎。


    事件

    事件(event)基于委托,为委托提供了一个发布/订阅机制,我们可以说事件是一种具有特殊签名的委托。

    比如:我们按键点击,这就是一个事件,在其它地方接收到这个事件,触发相应。

    下面使用猫和老鼠的实例来演示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _012_观察者设计模式_猫捉老鼠 {
        /// <summary>
        /// 猫类
        /// </summary>
        class Cat
        {
            private string name;
            private string color;
    
            public Cat(string name, string color)
            {
                this.name = name;
                this.color = color;
            }
    
            /// <summary>
            /// 猫进屋(猫的状态发生改变)(被观察者的状态发生改变)
            /// </summary>
            public void CatComing()
            {
                Console.WriteLine(color+"的猫"+name+"过来了,喵喵喵 ...");
    
                if(catCome!=null)
                    catCome();
            }
    
            public event Action catCome;//声明一个事件 发布了一个消息
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _012_观察者设计模式_猫捉老鼠 {
        /// <summary>
        /// 观察者类:老鼠
        /// </summary>
        class Mouse
        {
            private string name;
            private string color;
    
            public Mouse(string name, string color,Cat cat)
            {
                this.name = name;
                this.color = color;
                cat.catCome += this.RunAway;//把自身的逃跑方法 注册进 猫里面  订阅消息
            }
            /// <summary>
            /// 逃跑功能
            /// </summary>
            public void RunAway()
            {
                Console.WriteLine(color+"的老鼠"+name+"说: 老猫来, 赶紧跑, 我跑, 我使劲跑,我加速使劲跑 ...");
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _012_观察者设计模式_猫捉老鼠 {
        class Program {
            static void Main(string[] args) {
                Cat cat = new Cat("加菲猫","黄色");
                Mouse mouse1 = new Mouse("米奇","黑色",cat);
                Mouse mouse2 = new Mouse("水", "红色",cat);
                cat.CatComing();
                //cat.catCome();//事件不能再类的外部触发,只能在类的内部触发
                Console.ReadKey();
            }
        }
    }
    
  • 相关阅读:
    PHP读写XML文件的四种方法
    如何在linux系统中设置静态ip地址
    Memcached和Memcache安装(64位win7)
    Mysql存储过程和函数区别介绍
    随笔
    Vue 中使用axios传参数,后端收不到数据
    vs2019创建mvc连接mysql
    dapper多表查询
    Java并发编程:volatile关键字解析
    Mysql Innodb 间隙锁浅析
  • 原文地址:https://www.cnblogs.com/lmx282110xxx/p/10798683.html
Copyright © 2020-2023  润新知