具有继承和通用约束的两步方法解析

时间:2011-11-22 19:12:08

标签: c# generics overloading

在使用带继承的通用约束时,我遇到了一些非常令人惊讶的事情。我有一个重载的方法Foo与参数不同 - 基本或派生类实例。在这两种情况下,它通常只是将实例传递给第二对重载方法 - Bar

当我用基类实例调用Foo时,会调用基类的Bar重载。当我使用派生类实例调用Foo时,将调用派生类的Bar重载。这是明确的和预期的。

但是当我尝试将Foo方法合并到使用泛型和约束的单个GenericFoo时,方法的解析方式不同 - T正确解析,但只有{{1}的基类重载被称为。

Bar

测试代码 - 非通用旧方法的两个第一种情况,新通用方法的两种情况。

public class Animal { }
public class Cat : Animal { }

public class AnimalProcessor
{
    public static void Foo(Animal obj)
    {
        Console.WriteLine("Foo(Animal)");
        Bar(obj);
    }

    public static void Foo(Cat obj)
    {
        Console.WriteLine("Foo(Cat)");
        Bar(obj);
    }

    // new generic method to replace the two above
    public static void GenericFoo<T>(T obj)
        where T : Animal
    {
        Console.WriteLine("Foo(generic)");
        Bar(obj);
    }

    public static void Bar(Animal obj)
    {
        Console.WriteLine("Bar(Animal)");
    }

    public static void Bar(Cat obj)
    {
        Console.WriteLine("Bar(Cat)");
    }
}

结果 - 注意Console.WriteLine("Animal()"); AnimalProcessor.Foo(new Animal()); Console.WriteLine(); Console.WriteLine("Cat()"); AnimalProcessor.Foo(new Cat()); Console.WriteLine(); Console.WriteLine("Animal()"); AnimalProcessor.GenericFoo(new Animal()); Console.WriteLine(); Console.WriteLine("Cat()"); AnimalProcessor.GenericFoo(new Cat()); Console.ReadLine(); 中解决的类型差异:

Bar

看起来编译器将所有来自Animal() Foo(Animal) Bar(Animal) Cat() Foo(Cat) Bar(Cat) Animal() Foo(generic) Bar(Animal) Cat() Foo(generic) Bar(Animal) 的调用绑定到最不具体的重载,即使在编译时已知所有更具体类型的调用。为什么会这样,这种行为的原因是什么?规范的哪一部分定义了这个?

2 个答案:

答案 0 :(得分:3)

根据OP的要求,评论重新发布为答案:

泛型不是模板。通用方法编译一次,它们的行为是针对“最通用”的情况(在本例中为Animal)。这与C ++样式模板不同,其中模板是按类型为每个特化分别编译的。

答案 1 :(得分:2)

通用C#方法被编译为通用IL方法。在IL中,您必须明确指定要调用的重载。所以编译器没有简单的方法可以做到这一点。 (有一种复杂的方法:运行一个迷你编译器,动态选择此时的重载,这是dynamic所做的。)

如果您需要此行为,一个选项是在Bar()上使Animal成为虚拟方法。另一种选择是使用dynamic