C#委托、lambda表达式和事件

引用方法

委托是寻址方法的.NET版本。
在C++中,函数指针只不过是一个指向内存位置的指针,它不是类型安全的。
我们无法判断这个指针指向声明,参数和返回类型就更无从可知了。
而.NET的委托完全不同,委托是安全类型,它定义了返回类型和参数的类型。
委托不仅包含对方法的引用,也可以包括对多个方法的引用。
lambda表达式与委托直接相关,当委托是参数类型时,就可以用lambda表达式实现委托的引用方法。

委托

当要把方法传递给其他方法时,就需要使用委托。

int i = int.Parse("777");//将字符串777转换为数字777

上面的示例是将数据作为参数传递给方法,
如果要将方法传递给另一个方法需要将方法作为参数进行传递。

C#是面向对象编程的,几乎没有方法是孤立的存在,在调用方法前通常需要与类实例相关联。
所以在C#中,如果要传递方法,就必须将方法的细节封装到一种新的对象类型中,
即委托,委托是一种特殊类型的对象,其特殊之处在于委托只包含一个或多个方法的地址,而不包含数据。

声明委托

在C#中使用类时,通常需要先定义这个类,然后实例化该类的一个对象,除非类中只包含静态方法。
对于委托,同样需要先定义,对于委托定义就是告诉编译器这种类型的委托表示哪种类型的方法,然后必须创建该委托的一个或多个实例。编译器在后台将创建表示该委托的一个类。

声明委托使用关键字 delegate

delegate void X(int x);

上面的示例声明了一个委托X,并指定该委托的每个实例都可以包含一个方法的引用,该方法带有 int参数,并返回void。
在定义委托时必须给出它所表示的方法的签名(所有参数类型和方法名)和返回类型的等全部细节。

假定定义一个委托 ,该委托表示的方法有两个long型参数,返回类型为double。

delegate double X(long x1,long x2);

或要定义一个委托,表示的方法不带参数,返回一个string类型

delegate string Str();

其语法类似于方法的定义,但是没有方法主体,且定义的前面要加上关键字 delegate .
因为定义委托基本上就是定义一个新类(在定义委托后,编译器其会创建表示该委托的一个类),
所以可以在定义类的任何相同地方定义委托。
可以在类的内部定义,也可以在类的外部定义,还可以在名称空间中把委托定义为顶层对象。
可以在委托上添加访问修饰符:public,private, protected等。

public delegate string Str();

使用委托

using System;
using static System.Console;

namespace ConsoleApp20
{
    class Program
    {
        public delegate string Str();
        static void Main(string[] args)
        {
            int i = 777;//整形数字777
            WriteLine(i.ToString() + "asdw");//输出i的字符串格式,进行字符串拼接判断
            Str a = new Str(i.ToString);//对委托实例化,传入方法名称
            WriteLine(a() + "qwer");//使用委托,
            ReadKey();
        }
    }
}

在C#中,委托的实例化总是接受一个参数的构造函数,这个参数就是委托的引用方法,这个方法必须匹配最初定义委托时的签名。这里tostring返回字符串,委托返回字符串。
在任何代码中,使用都需要提供委托实例的名称,后面的括号内应该包含调用该委托中的方法时需要使用的任何等效参数。 a() ,a是委托的实例名称,括号为tostring需要的参数。

为了减少输入量,在需要委托实例的每个位置可以只传送地址的名称,这称为 委托推断 ,只要编译器可以把委托实例解析为特定的类型,这个C#特性就是有效的。

public delegate string Str();
Str a = i.ToString;
Str b = new Str(i.ToString);

编译器会检查a需要的委托类型,因此会创建一个Str委托类型的一个实例。用对象i将方法的地址传给构造函数。
注意,为什么这里的 ToString 不添加括号,输入括号会调用方法,而调用tostring方法会返回一个不能被赋予委托变量的字符串对象。
委托推断可以用于需要委托实例的任何地方使用,委托推断也可以用于事件,因为事件基于委托。

委托的一个特征是类型安全,可以确保被调用的签名是正确的,
委托不关心什么类型的对象上调用该方法,甚至不考虑方法是静态方法还是实例方法。
给定委托的实例可以引用任何类型的任意对象上的实例方法或静态方法,只要方法的签名匹配委托的签名即可。

简单的委托示例

定义一个类包含两个静态方法,翻倍和平方

        public class MyMath
        {
            public static int Doubling(int x)//翻倍
            {
                return x * 2;
            }
            public static int Square(int x)//平方
            {
                return x * x;
            }
        }

然后声明委托

public delegate int DMyMath(int x);

对委托实例化

DMyMath[] s = { MyMath.Doubling, MyMath.Square };

调用委托

using System;
using static System.Console;

namespace ConsoleApp20
{
    class Program
    {
        public class MyMath//自定义的数学计算类
        {
            public static int Doubling(int x)//静态方法翻倍
            {
                return x * 2;
            }
            public static int Square(int x)//静态方法平方
            {
                return x * x;
            }
        }
        public delegate int DMyMath(int x);//声明委托
        static void F(DMyMath s, int x)//声明一个静态方法,参数为委托实例,整形数字
        {
            int  result = s(x);//调用委托
            WriteLine(result);
        }
        static void Main(string[] args)
        {
            DMyMath[] s = { MyMath.Doubling, MyMath.Square };//委托推断生成委托的实例化
            WriteLine(s[0](10));//20  调用委托方法,参数放在括号内
            WriteLine(s[1](7));//49
            ReadKey();
        }
    }
}

这里实例化一个委托实例s数组,该数组的每个元素都初始化为指向MyMath类实现的不同操作。(一旦定义了委托类,基本上既可以实例化它的实例,就像处理一般类那样,所以把委托的实例放在数组中是可行的)
s[0]表示委托数组的第一个方法。
s0表示实际上调用这个方法,参数放在括号内。

特殊的,将委托实例作为参数传入方法 F ,在方法中调用委托,将返回结果存储在result。

Action<T>和Func<T>委托

除了为参数和返回类型定义一个新委托类型之外,还可以使用Action<T>和Func<T>委托。
泛型 Action<T> 委托表示引用一个 void 返回类型的方法,这个委托存在不同的变体,可以传递至多16种不同参数类型。
没有泛型参数的 Action 类可以调用没有参数的方法, Action<int T>调用带有一个参数的方法, Action<int T1,int T2>调用带有两个参数的方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355