C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
在 .NET 中委托提供后期绑定机制。 后期绑定意味着调用方在你所创建的算法中至少提供一个方法来实现算法的一部分。
比如,我们要实现一个多语言的问候。当我们没有使用委托的时候可以这样实现。示例:
private static void ChinessGreeting(string name) => Console.WriteLine("早上好," + name);
private static void EnglishGreeting(string name) => Console.WriteLine("Morning," + name);
private static void GreetPeople(string name, Language language)
{
switch (language)
{
case Language.Chinese:
ChinessGreeting(name);
break;
case Language.English:
EnglishGreeting(name);
break;
}
}
虽然这样解决了问题,但是很明显这样扩展性是很差的,当我们有另外一种语言时,我们还要更改GreetPeople方法,这显然违背了开闭原则。
此时,我们是不是可以直接改造GreetPeople方法,将language直接改为ChinessGreeting方法或者EnglishGreeting呢?如果可以,我们就需要一个统一的类型来表示ChinessGreeting方法和EnglishGreeting方法。此时,委托就应运而生了。
示例:
// 定义委托,它定义了可以代表的方法的类型
public delegate void GreetingDelegate(string name);
class Program
{
private static void ChinessGreeting(string name) => Console.WriteLine("早上好," + name);
private static void EnglishGreeting(string name) => Console.WriteLine("Morning," + name);
// 此方法使用定义的委托
private static void GreetPeople(string name, GreetingDelegate greeting) => greeting(name);
static void Main(string[] args)
{
// 将方法当作参数传递
GreetPeople("张三", ChinessGreeting);
GreetPeople("zhangsan", EnglishGreeting);
}
}
多播委托
在 C# 语言中多播委托是指在一个委托中注册多个方法,在注册方法时可以在委托中使用加号运算符或者减号运算符来实现添加或撤销方法。
比如,在上面的示例中,有人英语和中文都会的情况下,则可以这样实现:
static void Main(string[] args)
{
GreetingDelegate greeting = new GreetingDelegate(ChinessGreeting);
greeting += EnglishGreeting;
GreetPeople("张三", greeting);
}
强类型委托
前面的示例中,我们在使用委托时,都需要先使用delegate定义委托,当我们存在多个不同类型的或参数的委托时,我们就需要重新定义新的委托。这样就显得很麻烦,不过好在.Net Core框架包含几个在需要委托类型时可重用的类型。 这些是泛型定义,因此需要新的方法声明时可以声明自定义。
Actionnamespace System
{
public delegate void Action();
public delegate void Action(T obj);
public delegate void Action(T1 arg1, T2 arg2);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
public delegate void Action(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);
public delegate void Action(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);
public delegate void Action(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);
public delegate void Action(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);
}
上面的代码就是.Net Core中System部分的源代码。从代码中可以看出.Net Core定义了包含不同数量参数委托方法。
此时,上面的示例则可以改为:
class Program
{
private static void ChinessGreeting(string name) => Console.WriteLine("早上好," + name);
private static void EnglishGreeting(string name) => Console.WriteLine("Morning," + name);
// 此方法使用定义的委托
private static void GreetPeople(string name, Action greeting) => greeting(name);
static void Main(string[] args)
{
GreetPeople("张三", ChinessGreeting);
GreetPeople("zhangsan", EnglishGreeting);
}
}
// 或者
class Program
{
// 此方法使用定义的委托
private static void GreetPeople(string name, Action greeting) => greeting(name);
static void Main(string[] args)
{
GreetPeople("张三", (string name) => Console.WriteLine("早上好," + name));
GreetPeople("zhangsan", (string name) => Console.WriteLine("Morning," + name));
}
}
Func
在Action委托中,只有输入的参数并没有输出的参数。当我们需要使用到有输出参数的委托时,就可以使用Func委托。
namespace System
{
public delegate TResult Func();
public delegate TResult Func(T arg);
public delegate TResult Func(T1 arg1, T2 arg2);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
public delegate TResult Func(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);
public delegate TResult Func(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);
public delegate TResult Func(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);
public delegate TResult Func(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);
}
Predicate
一种专门返回单个值的委托类型。
public delegate bool Predicate(T obj);
如果对.Net Core中定义的委托类型感兴趣可以查看源码:
namespace System
{
public delegate void Action();
public delegate void Action(T obj);
public delegate void Action(T1 arg1, T2 arg2);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
public delegate void Action(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);
public delegate void Action(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);
public delegate void Action(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);
public delegate void Action(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);
// This should probably technically be T? rather than T to match `IComparer`. However, the use cases are generally different,
// with Comparison typically being used via a lambda, with the T inferred from the type of the collection being sorted,
// and forcing nullable warnings onto all such usage leads to many spurious warnings.
public delegate int Comparison(T x, T y);
public delegate TOutput Converter(TInput input);
public delegate bool Predicate(T obj);
}
namespace System.Buffers
{
public delegate void SpanAction(Span span, TArg arg);
public delegate void ReadOnlySpanAction(ReadOnlySpan span, TArg arg);
internal delegate TResult SpanFunc(Span span, T1 arg1, T2 arg2, T3 arg3);
}
参考:
Microsoft C# 委托
.Net Core 源码



