part01.03 委托与 Lambda 表达式(二):匿名方法
匿名方法
在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。 C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式。 不过,本主题中有关匿名方法的信息同样也适用于 Lambda 表达式。 有一种情况下,匿名方法提供了 Lambda 表达式中所没有的功能。 就是可使用匿名方法来忽略参数列表。 这意味着匿名方法可转换为具有各种签名的委托。 这对于 Lambda 表达式来说是不可能的。
匿名方法的简要说明:
1.“匿名方法” 就是没有名称的方法,通常用于将代码块作为委托参数进行传递。
2.匿名函数是一个 “内联(inline)” 语句或表达式,可在需要委托类型的任何地方使用。
3.匿名函数可以使用匿名方法来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数。
使用匿名方法处理委托和使用 Lambda 表达式处理委托实例:相关代码说明
1 /// <summary> 2 /// 定义一个委托方法(类型) 3 /// </summary> 4 /// <param name="i">int 变量</param> 5 /// <param name="j">double 变量</param> 6 delegate void DelegateMethod(int i, double j); 7 8 /// <summary> 9 /// 定义一个普通的类 10 /// </summary> 11 class SomeClass 12 { 13 /// <summary> 14 /// 定义一个打印两个数字的方法 15 /// </summary> 16 /// <param name="m"></param> 17 /// <param name="n"></param> 18 public void PrintDigit(int m, double n) 19 { 20 Console.Write(m * n + " "); 21 } 22 } 23 24 class Program 25 { 26 static void Main(string[] args) 27 { 28 #region 实例化委托的传统方式 29 var a = new SomeClass(); 30 31 // 使用 SomeClass 的实例方法实例化委托 32 var b = new DelegateMethod(a.PrintDigit); 33 // 此时委托实例执行的是:a.PrintDigit 34 b(1, 2.5); 35 #endregion 36 37 #region 使用匿名方法处理委托 38 DelegateMethod c = delegate (int m, double n) 39 { 40 Console.Write(m + n + " "); 41 }; 42 c(1, 2.5); 43 #endregion 44 45 #region 使用 Lambda 表达式处理委托实例化 46 DelegateMethod d = (int m, double n) => { Console.Write(m - n + " "); }; 47 d(1, 2.5); 48 #endregion 49 50 Console.ReadKey(); 51 } 52 }
Func<> 和 Action<> 简介
public delegate TResult Func<in T, out TRsult>(T arg)
1. 类型参数:
In T : 此委托封装的方法的参数类型。该类型参数是可逆的,即可以使用指定的类型或派生程度更低的类型。
out TRsult : 此委托封装的方法的返回值类型。该类型参数是协变的,即可以使用指定的类型或派生类更高的类型。
2.参数 arg 类型
T : 此委托封装的方法的参数。
3.返回值类型
Tresult : 此委托封装的方法的返回值
可以使用此委托表示一种能以参数形式传递的方法,而不是显式声明自定义委托。封装的方法必须与此委托定义的方法签名相对应。也就是说,封装的方法必须具有一个通过值传递给它的参数,并且必须返回值。
若要引用具有一个参数并返回 void 的方法,请改用 Action<T> 委托。
// 带返回值的委托,实例化 Func<T1,T2,Tresult> 委托,不需要显式定义一个新委托并将命名方法分配给该委托 Func<int, double, double> a = (m, n) => { return m * n; }; Console.WriteLine(a); Console.WriteLine(a(10, 25.2)); // 不带返回值的委托 Action<int> b = x => { Console.WriteLine(x); }; b(100); Console.ReadKey();
要将代码块传递为委托参数,创建匿名方法则是唯一的方法。通过使用匿名方法,由于不必创建单独的方法,因此减少了实例化委托所需的编码系统开销。
// 为点击事件创建一个处理程序。 button1.Click += delegate(System.Object o, System.EventArgs e) { System.Windows.Forms.MessageBox.Show("Click!"); };
// 创建一个委托 delegate void Del(int x); // 使用匿名方法实例化委托 Del d = delegate(int k) { /* ... */ };
例如,如果创建方法所需的系统开销是不必要的,则指定代码块(而不是委托)可能非常有用。 启动新线程即是一个很好的示例。 无需为委托创建更多方法,线程类即可创建一个线程并且包含该线程执行的代码。
void StartThread() { System.Threading.Thread t1 = new System.Threading.Thread (delegate() { System.Console.Write("Hello, "); System.Console.WriteLine("World!"); }); t1.Start(); }
匿名方法的参数的范围是 “匿名方法块” 。
如果局部变量和参数的范围包含匿名方法声明,则该局部变量和参数称为该匿名方法的 “外部” 变量。
// n 是一个外部变量 int n = 0; Del d = delegate() { System.Console.WriteLine("Copy #:{0}", ++n); };
外部变量的引用 n
被认为是捕获在创建委托时。 与本地变量不同,捕获的变量的生存期内扩展,直到引用该匿名方法委托被垃圾回收。
匿名方法不能访问外部范围的 ref 或 out 参数。
在 “匿名方法块” 中不能访问任何不安全代码。
在 is 运算符的左侧不允许使用匿名方法。
实例化委托的两种方法:
-
使委托与匿名方法关联。
-
使委托与命名方法 (
DoWork
) 关联。
// 声明一个委托 delegate void Printer(string s); class TestClass { static void Main() { // 使用匿名方法实例化委托类型 Printer p = delegate (string j) { System.Console.WriteLine(j); }; // 匿名委托调用的结果 p("调用使用匿名方法的委托"); // 委托实例化使用命名方法“DoWork” p = new Printer(TestClass.DoWork); // 传统委托调用的结果 p("调用使用命名方法的委托"); } // 与命名委托关联的方法 static void DoWork(string k) { System.Console.WriteLine(k); } } /* 输出: 调用使用匿名方法的代理。 调用使用命名方法的委托。 */