【C#学习】delegate(委托) 和 event(事件)

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。在C#中方法不能作为参数直接传递,必须使用委托(用来委托方法)。delegate(委托)是一种特殊的引用类型,它将方法也作为特殊的对象封装起来,从而将方法作为变量、参数或者返回值传递。委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。使用一个委托有三个步骤: 

定义委托
实例化委托
将指定的方法添加到委托对象中
例子:
        delegate int plus(int x, int y); // 1. 定义委托
 
        static void Main(string[] args)
        {
            plus del_p;  // 2. 实例化委托
 
            del_p = new plus(addition);  // 3. 将方法添加到实例化委托对象中
 
            int n = del_p(1, 2);
            Console.Write(n);
        }
 
        static int addition(int x, int y)
        {
            return x + y;  
        }

C#允许直接把方法名赋给委托,所以下面的三种写法也正确:

            plus del_p = new plus(addition);
            plus del_p2 = addition;
            del_p = addition; 

委托完全可以被当做普通类型对待,比如可以加减、赋值、构建数组。
委托数组
委托可以构建数组,意味着一组同样返回类型和参数类型的方法。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc[] fs = new MathFunc[]
            {
                substract, 
                addition, 
                product,
                division
            };
 
            int n = fs[0](1, 2); // -1
        }
 
        static int division( int x, int y)
        {
            return x / y;
        }
 
        static int substract(int x, int y)
        {
            return x - y; 
        }
 
        static int addition(int x, int y)
        {
            return x + y;  
        }
 
        static int product(int x, int y)
        {
            return x * y;
        }

委托的加减法/委托的多播(Multicasting of a Delegate)
一个委托对象可以封装多个方法,通过委托对象的合并(加法)实现。被合并的方法必须有相同的返回类型和参数类型。使用合并后的委托时,会依次将实参传入被合并的方法,最后的返回值以最后一个方法为准。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f;
            f = addition;
            f += substract;
            f += product;
            int n = f(1, 2); // 2
        }
 
        static int division( int x, int y)
        {
            Console.WriteLine("division");
            return x / y;
        }
 
        static int substract(int x, int y)
        {
            Console.WriteLine("substraction");
            return x - y; 
        }
 
        static int addition(int x, int y)
        {
            Console.WriteLine("addition");
            return x + y;  
        }
 
        static int product(int x, int y)
        {
            Console.WriteLine("product");
            return x * y;
        }
上面的代码会打印出:

addition 
substraction 
product 

委托减法是加法的逆运算。如果被减委托中不含有减数委托,则不会对被减数造成任何影响,被减数减去自身为null,调用空委托会引发异常。总结下来委托加减法的几个结论:
委托加减null没有任何效果
委托减自身为null
减数如果没有包含在被减数中,则没有任何效果
在使用委托之前,应该判断委托是否为null。

传递委托
委托可以被作为以下方式传递
变量
方法参数
方法返回值
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f = getAddFunc();
            int n = f(2, 3); // 5
            int n2 = add(f, 3, 4); // 7
        }
 
        static int add(MathFunc f, int x, int y)
        {
            return f(x,  y);
        }
 
        static MathFunc getAddFunc()
        {
            return addition;
            // or return new MathFunc(addition);
        }

匿名方法(delegate方法)
使用 delegate 关键字直接定义方法,和实例化委托不同,不使用 new 关键字来创建委托对象,而是直接定义方法参数和方法体。匿名方法并不是真的没有名称,而是指程序员无需命名。C#在编译时会给方法生成一个方法定义(包含方法名和参数列表),该方法实际上是当前类型的一个私有静态方法。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f = delegate (int x, int y)
            {
                return x + y;
            };
            int n = f(1, 2); // 3
        }
Lambda表达式
从C# 3.0 开始,可以用 lambda表达式替换匿名方法表达式,直接将一个 Lambda表达式赋给委托对象。注意Lambda表达式不能作为语句,必须为右值。Lambda表达式的参数和返回类型必须和委托一致。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f;
            f = (int x, int y) =>
            {
                return x + y;
            };
            int n = f(1, 2); // 3
        }
如果lambda表达式只有一个参数,则可以省略圆括号。Lambda表达式允许多种写法,以下写法等价:
            something = (int x, int y) => { return x + y; };
            something = (int x, int y) => x + y;
            something = (x, y) => x + y;
            something = (x, y) => { return x + y; }; 
Lambda表达式中没有用到的参数可以用 _ 代替。比如
something = (x, _) => x * 2; 

在Lambda表达式和匿名delegate方法内部都可以访问主调函数的外部变量,比如:
            MathFunc f;
            string str = "str";
            f = (x, y) =>
            {
                Console.Write(str); // 访问了外部的str
                return x + y;
            };
如果Lambda表达式或者匿名delegate方法的参数名和外部变量冲突,则无法通过编译。
委托发布和订阅
基于委托多播的特性,可以实现 “发布者/订阅者”模式。发布者对象有一个委托成员,保存一系列别的类的实例(订阅者)的方法,一旦发布者触发(调用)了委托,就会把所有注册过的方法都依次调用依次,通知订阅者。比如有一个红绿灯,一旦红绿灯变色,就会通知所有车辆和行人,此时可以把车辆和行人的响应方法注册到红绿灯对象的一个 public 委托成员 OnColorChange 里,当红绿灯调用 OnColorChange 时,就会通知所有车辆,调用他们的响应函数。由于订阅者的类型和响应方法千奇百怪,可以高度定制针对一个事件(比如红绿灯变色)的不同对象的不同响应函数。
由于委托成员 OnColorChange 是公有的,一旦别的程序将委托置为空,或其他值,委托先前的修改就前功尽弃了。为了防止这种事情发生,C# 提供了 event 来修饰(声明)委托成员。经过 event 修饰后,委托成员只能被 += 和 -= 修改,而不能用 = 修改。
--------------------- 
作者:csdn_chai 
来源:CSDN 
原文:https://blog.csdn.net/csdn_chai/article/details/77429538 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/GarfieldEr007/article/details/85002607