C#委托不可重载,为何Func与Action却有16种重载形式?
嘿,这个问题问得特别到位,我刚摸C#委托的时候也卡过这个点😉
首先得澄清一个容易混淆的误区:委托本身并不支持方法那种“重载”——咱们说的方法重载,是同一个方法名下,靠不同的参数列表(数量、类型、顺序)让编译器区分调用;但委托是一种引用类型,每个委托类型的唯一性是由它的返回值类型和**参数列表(数量、类型、顺序)**共同决定的。也就是说,两个签名不一样的委托,哪怕名字相同,本质也是完全不同的类型,这和方法重载不是一回事。
那Func和Action的16种“重载”到底是怎么来的?答案很简单:它们是.NET框架预先定义好的不同泛型委托版本,只是共用了Func/Action这个名字而已。
拿Action举例:
Action是无参数、无返回值的委托类型Action<T>是带1个T类型参数、无返回值的委托类型Action<T1, T2>是带2个参数、无返回值的委托类型- ...以此类推,直到带16个参数的
Action<T1, T2, ..., T16>
Func也是一样的逻辑,只是Func会带一个返回值类型参数:
Func<TResult>是无参数、返回TResult类型的委托Func<T, TResult>是带1个参数、返回TResult的委托- ...直到带16个参数的版本
这些不同参数数量的泛型委托,都是独立的类型——泛型的不同类型参数(包括参数的数量)会生成完全不同的类型实例,所以它们看起来像是“重载”,但本质是不同的委托类型,只是名字相同罢了。
再回到你看到的资料说“委托无法像方法那样重载”,这句话的意思是:你不能给同一个委托类型定义不同的签名版本(比如不能让MyDelegate同时支持int参数和string参数),因为委托类型的身份完全由签名决定。而Func/Action的情况是,框架帮你预先定义了16个不同签名的委托类型,只是都叫Func/Action而已,这和方法的重载逻辑不一样。
举个自定义委托的例子你就懂了:
// 这是两个完全不同的委托类型,只是名字相同 public delegate void MyDelegate(int num); public delegate void MyDelegate(string str); public static void PrintInt(int x) => Console.WriteLine(x); public static void PrintString(string x) => Console.WriteLine(x); // 这里的两个MyDelegate其实是不同的类型,编译器能区分,但这种写法很容易混淆 MyDelegate intDel = PrintInt; MyDelegate strDel = PrintString;
这种自定义同名不同签名的委托写法很少用,太容易搞混,但Func/Action是官方预定义的,用泛型参数数量来明确区分,所以看起来像是“重载”,实则是不同的泛型委托类型。
内容的提问来源于stack exchange,提问作者Arad

