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
的方法,常用于 Array
和 List<T>
的元素搜索方法。
public delegate bool Predicate<T>(T obj);
与 Func 委托的关系:其实是返回类型为 bool
的 Func 委托的再次封装,与下面这句等价:
public delegate bool Func<T,bool>(T arg);
§5 总结
干就完了。