委托函数大集结——C# delegate/lambda/Action/Func/Predicate总结

C# 的委托类似于 C++ 的函数指针,将方法作为参数传递,但类型安全。
普通的委托需要经过声明类型、准备方法、实例化传入方法之后才能使用,流程比较繁琐。

public delegate void MyDelegate(string str); // 声明类型
public static void Hello(string name) // 准备方法
{ 
    Console.WriteLine("Hello " + name); 
} 
MyDelegate md = new MyDelegate(Hello); // 实例化传入方法
md("XiaoMing"); // 使用

// Output:
//
// Hello XiaoMing

因此产生了使用匿名方法表达式和 lambda 表达式简化的操作。
另外微软也在框架中预定义了三种委托函数 Action/Func/Predicate 进一步做了简化。
下面就简单总结下两种表达式和三种委托函数。

§1 匿名方法表达式和 lambda 表达式

先看下使用这两种表达式的简化效果,主要在于不需要准备方法,可以在实例化时直接传入。

public delegate void MyDelegate(string str);
static void Main(string[] args)
{
    // 匿名方法表达式
    MyDelegate md1 = delegate (string str) { Console.WriteLine("Hello " + str); };
    md1("XiaoMing");
    // lambda 表达式
    MyDelegate md2 = (str) => { Console.WriteLine("Hello " + str); };
    md2("XiaoMing");
}

1.1 两者的区别

通过以下例子说明两种表达式的区别,每一块第一行使用匿名方法表达式,第二行使用 lambda 表达式。虽然使用了还没介绍的 Func 委托和 Action 委托,但当前我们可以只关注 = 右边的部分。

// Comparison 1
Func<int, int, int> sum1 = delegate (int a, int b) { return a + b; };
Func<int, int, int> sum2 = (a, b) => { return a + b; };

// Comparison 2
Action<int, double> introduce1 = delegate (int a, double b) { Console.WriteLine("This is world!"); };
Action<int, double> introduce2 = (a, b) => { Console.WriteLine("This is world!"); };

// Comparison 3
Action<int, double> introduce3 = delegate { Console.WriteLine("This is world!"); };
Action<int, double> introduce4 = () => { Console.WriteLine("This is world!"); }; // 编译错误

可以看到使用 lambda 表达式的代码比使用匿名方法表达式更简洁。
而第三组中使用 lambda 表达式的编译会出错,因为参数与 Action 输入参数不符,但使用匿名方法表达式则无此问题。根据 MSDN 的文档可知:

That's the only functionality of anonymous methods that is not supported by lambda expressions. In all other cases, a lambda expression is a preferred way to write inline code.
这是lambda表达式不支持的匿名方法的唯一功能。在其他情况下,lambda表达式是编写内联代码的首选方法。

因此这里只讲一下 lambda 表达式。

1.2 lambda 表达式

(input parameters) => {statement;}
中间的 => 为 lambda 运算符,左边为输入参数,右边为表达式语句。
一般的表达式看起来是这样的(= 右边的部分):

Func<int, int, bool> isEqual = (int a, int b) => { return a == b; };
Func<int, int, bool> isEqual = (a, b) => { return a == b; }; // 简化
Func<int, int, bool> isEqual = (a, b) => a == b; // 再简化

Func<int, bool> isFive = (int a) => { return a == 5; };
Func<int, bool> isFive = (a) => { return a == 5; }; // 简化
Func<int, bool> isFive = a => a == 5; // 再简化

§2 Action 委托

封装有若干个参数且无返回值的方法,参数数量 0~16。

public delegate void Action();
public delegate void Action<T>(T obj);
public delegate void Action<T1,T2>(T1 arg1, T2 arg2);
public delegate void Action<T1,T2,T3>(T1 arg1, T2 arg2, T3 arg3);
......
public delegate void Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

用以下示例说明 Action 委托的用法,示例程序使用了两个不同参数的 Action,作用均是向 types 中添加元素。

List<Type> types = new List<Type>();
// 传入参数类型为 int
Action<int> add1 = (a) => types.Add(a.GetType());
// 传入参数类型为 float 和 double
Action<float, double> add2 = (a, b) =>
{ 
    types.Add(a.GetType()); 
    types.Add(b.GetType()); 
};
add1(1);
add2(2f, 3.0);
types.ForEach((i) => Console.WriteLine(i));

// Output:
//
// System.Int32
// System.Single
// System.Double

§3 Func 委托

封装有若干个参数且有返回值的方法,参数数量 0~16。

public delegate TResult Func<T,TResult>(T arg);
public delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3);
......
public delegate TResult Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

用以下示例说明 Func 委托的用法,示例程序使用了三个不同参数的 Func。

// 无传入参数,返回类型为 string
Func<string> getTypeString1 = () => { return "2020"; }; 
// 传入参数类型为 int,返回类型为 string
Func<int, string> getTypeString2 = (a) => { return a.GetType().ToString(); };
// 传入参数类型为 float 和 double,返回类型为 string
Func<float, double, string> getTypeString3 = (a, b) => 
{
    return a.GetType().ToString() + " " + b.GetType().ToString();
};
Console.WriteLine(getTypeString1());
Console.WriteLine(getTypeString2(1));
Console.WriteLine(getTypeString3(2f, 3.0));

// Output:
//
// 2020
// System.Int32
// System.Single System.Double

与 Action 委托的区别:Action 无返回值,Func 有返回值。

§4 Predicate 委托

封装一个参数且返回类型为 bool 的方法,常用于 ArrayList<T> 的元素搜索方法。

public delegate bool Predicate<T>(T obj);

与 Func 委托的关系:其实是返回类型为 bool 的 Func 委托的再次封装,与下面这句等价:

public delegate bool Func<T,bool>(T arg);

§5 总结

干就完了。

推荐阅读

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