委托 / delegate (Action, Func<TResult>, Predicate<T>)、Lambda 表达式、匿名方法

创建时间:
2016-02-06 23:15
最近更新:
2018-10-11 17:36

Note: 截至 2017-12-08 "Lambda" 相关均集中在此

Resource - MSDN - 委托

  1. 委托 (C# 参考) - delegate 是一种可用于封装命名方法或匿名方法的引用类型。委托类似于 C++ 中的函数指针;但是,委托是类型安全和可靠的。
  2. 委托 (C# 编程指南) - 委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。在方法重载的上下文中,方法的签名不包括返回值。但在委托的上下文中,签名包括返回值。
  3. C# 中委托的发展 - 本文的一部分 - 在 C# 1.0 中,通过使用在代码中其他位置定义的方法显式初始化委托来创建委托的实例。C# 2.0 引入了匿名方法的概念,作为一种编写可在委托调用中执行的未命名内联语句块的方式。C# 3.0 引入了 Lambda 表达式,这种表达式与匿名方法的概念类似,但更具表现力并且更简练。这两个功能统称为匿名函数。通常,面向 .NET Framework 3.5 及更高版本的应用程序应使用 Lambda 表达式。
  4. 使用委托 (C# 编程指南) - 委托会将对其进行的方法调用传递到该方法,调用方传递到委托的参数将传递到该方法,并且委托会将方法的返回值 (如果有) 返回到调用方,这被称为调用委托。
  1. 带有命名方法的委托与带有匿名方法的委托 (C# 编程指南)
  2. 在编译时比较分配的两个不同类型的委托将导致编译错误 - 本文的一部分 - d0 == d1
  3. 泛型委托 (C# 编程指南) - public delegate void Del<T>(T item);
  1. System.Delegate 类 - 委托类型的基类。表示委托,委托是一种数据结构,它引用静态方法或引用类实例及该类的实例方法。
  2. System.Delegate 类
  3. Action 委托 - 封装一个方法,该方法不具有参数且不返回值。
  4. Func<TResult> 委托 - 封装一个方法,该方法不具有参数,且返回由 TResult 参数指定的类型的值。
  5. Predicate<T> 委托 - 表示一种方法,该方法定义一组条件并确定指定对象是否符合这些条件。public delegate bool Predicate<in T>(T obj)

Multicast Delegate (多播委托)

  1. System.MulticastDelegate 类 - public abstract class MulticastDelegate : Delegate
  2. 多播 - 本文的一部分 - 调用时,委托可以调用多个方法。 这被称为多播。注意语法 dMulti = d1 + d2dMulti = d1 - d2dMulti += d3dMulti -= d3int invocationCount = dMulti.GetInvocationList().GetLength(0);
  3. 如何: 合并委托 (多路广播委托) (C# 编程指南) - 委托对象可通过使用 + 运算符 将多个对象分配到一个委托实例。多播委托包含已分配委托列表。此多播委托被调用时会依次调用列表中的委托。仅可合并类型相同的委托。- 运算符 可用于从多播委托中删除组件委托。

匿名函数 = Lambda 表达式 & 匿名方法 (Anonymous Method)

  1. 匿名函数 (C# 编程指南) - 匿名函数是一个 "内联" 语句或表达式,可在需要委托类型的任何地方使用。共有两种 匿名函数: Lambda 表达式 (可以绑定到表达式树,也可以绑定到委托)、匿名方法。
  2. 匿名方法 (C# 编程指南) - 示例: button1.Click += delegate(Object o, EventArgs e) { MessageBox.Show("Click!"); };

Lambda 表达式

  1. Lambda 表达式 (C# 编程指南) - "Lambda 表达式" 是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。Lambda 在基于方法的 LINQ 查询中用作标准查询运算符方法 (如 Where) 的参数。使用基于方法的语法: 在 System.Linq.Enumerable 类中调用 Where 方法时 (像在 LINQ to Objects 和 LINQ to XML 中那样),参数是委托类型 System.Func<T, TResult>; 在 System.Linq.Queryable 类中调用相同的方法时 (像在 LINQ to SQL 中那样),则参数类型是 System.Linq.Expressions.Expression<Func>,其中 Func 是包含至多十六个输入参数的任何 Func 委托。
  2. => 运算符 (C# 参考)
  3. 如何: 在查询中使用 Lambda 表达式(C# 编程指南)
  4. 如何: 在 LINQ 外部使用 Lambda 表达式 (C# 编程指南)

变体 - 协变 (Covariance/Covariant) & 逆变 (Contravariance/Contravariant)

2017-11-30 Tony 的理解 (尚不确定正确性)

  • 协变 指 返回值 允许使用 比声明 更抽象的类型。
  • 逆变 指 参数值 允许使用 比声明 更具体的类型。

Resource - MSDN

  1. 协变和逆变 (C#) - Entrance
  2. 使用委托中的变体 (C#) - 向委托分配方法时,协变和逆变为匹配委托类型和方法签名提供了灵活性。
  3. 泛型中的协变和逆变
  4. 委托中的协变和逆变 (C# 编程指南)
  5. in (泛型修饰符) (C# 参考)
  6. out (泛型修饰符) (C# 参考)

Resource

  1. 逆变与协变详解

静态方法 versus 实例方法

结论

静态方法 可以直接 赋给委托,实例方法 必须以 "类的实例.实例方法" 的形式 才能 赋给委托。

官方解释

MSDN 文档 System.Delegate 类 中有:

  • Represents a delegate, which is a data structure that refers to a static method or to a class instance and an instance method of that class.
  • The following example shows how to define a delegate named myMethodDelegate. Instances of this delegate are created for an instance method and a static method of the nested mySampleClass class. The delegate for the instance method requires an instance of mySampleClass. The mySampleClass instance is saved in a variable named mySC.
var mySC = new mySampleClass();
var myD1 = new myMethodDelegate(mySC.myStringMethod);           //instance method
var myD2 = new myMethodDelegate(mySampleClass.mySignMethod);    //static method

如何: 声明、实例化和使用委托

本节内容摘自 如何: 声明、实例化和使用委托 (C# 编程指南)

在 C# 1.0 和更高版本中,可以如下面的示例所示声明委托:

// Declare a delegate.
delegate void Del(string str);

// Declare a method with the same signature as the delegate.
static void Notify(string name)
{
    Console.WriteLine("Notification received for: {0}", name);
}

// Create an instance of the delegate.
Del del1 = new Del(Notify);

C# 2.0 提供了更简单的方法来编写前面的声明:

Del del2 = Notify;

在 C# 2.0 和更高版本中,还可以使用匿名方法来声明和初始化委托:

Del del3 = delegate(string name) {
    Console.WriteLine("Notification received for: {0}", name);
};

在 C# 3.0 和更高版本中,还可以通过使用 Lambda 表达式声明和实例化委托:

Del del4 = name => {
    Console.WriteLine("Notification received for: {0}", name);
};

Lambda 语法示例

static void Main(string[] args)
{
    Func<string, int> GetLength;

    //使用 C# 2.0 中的匿名方法获取字符串长度
    GetLength = delegate(string str) { return str.Length; };
    Call(GetLength);

    //使用 Lambda 表达式
    //``(显式类型参数列表) => {语句}`` Lambda 表达式最冗长版本
    GetLength = (string str) => { return str.Length; };
    Call(GetLength);

    //单一表达式作为主体
    //``(显式类型参数列表) => 表达式
    GetLength = (string str) => str.Length;
    Call(GetLength);

    //隐式类型的参数列表
    //``(隐式类型参数列表)`` => 表达式
    GetLength = (str) => str.Length;
    Call(GetLength);

    //单一参数的快捷语法
    //参数名 => 表达式
    GetLength = str => str.Length;
    Call(GetLength);
}

public static Call(Func<string, int> lambda)
{
    int result = lambda("Hello World!");
    Console.WriteLine(result);
}