委托定义了一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 一个方法(静态或实例),其参数列表和返回类型匹配可以分配给该类型的变量,然后直接调用(使用适当的参数)或作为参数本身传递给另一个方法,然后调用。 以下示例演示委托的使用。
using System;
using System.Linq;
public class Program
{
public delegate string Reverse(string s);
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Reverse rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
- 该
public delegate string Reverse(string s);
行创建采用字符串参数的方法的委托类型,然后返回字符串参数。 - 该方法
static string ReverseString(string s)
具有与定义的委托类型完全相同的参数列表和返回类型,可实现委托。 - 该
Reverse rev = ReverseString;
行显示,可以将方法分配给相应委托类型的变量。 - 该
Console.WriteLine(rev("a string"));
行演示如何使用委托类型的变量来调用委托。
为了简化开发过程,.NET 包含一组委托类型,程序员可以重复使用,无需创建新类型。 这些类型是 Func<>
, Action<>
Predicate<>
并且可以使用它们,而无需定义新的委托类型。 这三种类型之间存在一些差异,这些类型与预期使用的方式有关:
-
Action<>
用于需要使用委托的参数来执行某个操作时。 它封装的方法不返回值。 -
Func<>
通常用于现有转换的情况,也就是说需要将委托参数转换为其他结果时。 投影是一个很好的示例。 它封装的方法返回指定的值。 -
Predicate<>
用于在需要确定参数是否满足委托条件时。 也可以将其编写为一个Func<T, bool>
值,这意味着该方法返回布尔值。
现在可使用 Func<>
委托而非自定义类型重新编写上述示例。 程序将继续以完全相同的方式运行。
using System;
using System.Linq;
public class Program
{
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Func<string, string> rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
对于这个简单示例,在Main
方法之外定义另一个方法似乎有点多余。 .NET Framework 2.0 引入了 匿名委托的概念,这使你可以创建“内联”委托,而无需指定任何其他类型或方法。
在下面的示例中,匿名委托将列表筛选为只包含偶数,然后将它们打印到控制台。
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(
delegate (int no)
{
return (no % 2 == 0);
}
);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
如你所见,该委托的正文只是一组表达式,其他所有委托也是如此。 但它并非单独定义,而是在调用 方法时临时引入List<T>.FindAll。
但是,即使使用此方法,我们仍有很多代码可以丢弃。 这就是 lambda 表达式 发挥作用的地方。 作为语言集成查询(LINQ)的核心构建基块之一,在 C# 3.0 中引入了 Lambda 表达式,或只是“lambdas”。 这种表达式只是使用委托的更方便的语法。 它们将声明参数列表和方法正文,但在分配到委托之前没有自己的正式标识。 与委托不同,可将其作为事件注册的右侧内容或在各种 LINQ 子句和方法中直接分配。
由于 lambda 表达式只是指定委托的另一种方式,因此我们应该能够重写上述示例以使用 lambda 表达式而不是匿名委托。
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(i => i % 2 == 0);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
在前面的示例中,使用的 lambda 表达式是 i => i % 2 == 0
。 同样,这只是使用委托的便捷语法。 内部原理与匿名委托相似。
再次强调,lambda 只是委托,这意味着可将其顺利用作事件处理程序,如以下代码片段所示。
public MainWindow()
{
InitializeComponent();
Loaded += (o, e) =>
{
this.Title = "Loaded";
};
}
在这个上下文中,+=
运算符用于订阅事件。 有关详细信息,请参阅如何订阅和取消订阅事件。