接收List <T>的C#泛型方法不会为实际类型的T调用重载方法(更喜欢通用类型)

Can*_*pus 3 c# generics overloading

我有这个例子C#代码:

class Stuff { }  // empty class

void Main()
{
    var list = new List<Stuff> {
        new Stuff(),
        new Stuff()
    };
    Fun(list);
}

void Fun<T>(List<T> a)
{
    Debug.Log("called List<T> Fun");
    foreach (T t in a) {
        Fun(t);
    }
}

void Fun(Stuff a)
{
    Debug.Log("called Stuff Fun");
}

void Fun<T>(T a)
{
    Debug.Log("called T Fun");
}
Run Code Online (Sandbox Code Playgroud)

调用Main()最终打印:

called List<T> Fun
called T Fun
called T Fun
Run Code Online (Sandbox Code Playgroud)

我不明白为什么编译器能够Fun<T>(List<T> a)按预期调用,但后来不知道调用Fun(Stuff a),哪个更具体Fun<T>(T a).在编译时是否确定T Stuff在这种情况下是肯定的?typeof(T)内部打印Fun<T>(List<T> a)按预期给出了"Stuff",但这并不能证明任何事情......

添加Fun(List<Stuff> a)方法是有效但不可取的(项目中的列表有很多不同的可能类型,并且所有这些行为应该是相同的).

我试过搜索这个问题,但无法用我能找到它的方式来表达它.对不起,如果之前有人问过这个问题(可能!).

Jon*_*eet 11

关键是要了解void Fun<T>(List<T> a)编译一次,重载解析执行一次.不是一次T,而是一次.

在编译此代码时考虑编译器的情况:

void Fun<T>(List<T> a)
{
    Debug.Log("called List<T> fun");
    foreach (T t in a) {
        Fun(t);
    }
}
Run Code Online (Sandbox Code Playgroud)

特别要考虑调用中的重载决策Fun(t).

编译器一无所知T,这是Fun(t)调用中参数的类型- 它可以是任何非指针类型.它必须在这些签名之间执行重载解析:

void Fun<T>(List<T> a)
void Fun(Stuff a)
void Fun<T>(T a)
Run Code Online (Sandbox Code Playgroud)

只有这些方法是适用的一个是最后一个-的T调用代码被用作类型参数T中我们调用的方法,并且它的罚款.另外两种方法不适用,因为没有转换TList<TList>(for any TList)或from Tto Stuff.

如果您希望在执行时执行重载决策,则可以使用动态类型:

foreach (dynamic d in a) {
    Fun(d);
}
Run Code Online (Sandbox Code Playgroud)

我个人并不喜欢这样做,但在这种情况下它可能会做你想要的.另一方面,对于嵌套列表,它可能会变得棘手 - 如果你T是一个List<int>,那么你会期望它调用Fun<List<int>>(list)Fun<int>(list)?老实说,我不记得规则,知道哪些是"更好"或是否含糊不清.