0

我正在与聊天中的某人讨论相关问题,我想出了这段代码,它的行为与我的预期不同。

class Program
{
    static void Main(string[] args)
    {
        new Test<SomeObject>();
        Console.ReadKey();
    }
}

class SomeObject
{
    public SomeObject() { }

    public new string ToString()
    {
        return "Hello world.";
    }
}

class Test<T> where T : new()
{
    public Test()
    {
        T t = new T();
        object t1 = t;
        Console.WriteLine(t.ToString());
        Console.WriteLine(t1.ToString());
    }
}

输出是:

<ProjectName>.SomeObject
<ProjectName>.SomeObject

因为第一行是从泛型类型编写的,所以我希望它使用在 SomeObject 中定义的 ToString() 方法,因为这就是该类型在运行时会变成的样子,不是吗?

4

3 回答 3

2

我相信 Ben Voigt 在他的评论中已经给了你答案。

new您可以通过指定将 hidden ( ) 方法实现声明为通用约束的类型来实现您期望的结果:

class Test<T> where T : SomeObject, new()
{
    public Test()
    {
        T t = new T();
        object t1 = t;
        Console.WriteLine(t.ToString());
        Console.WriteLine(t1.ToString());
    }
}

这输出:

Hello world.
Program.SomeObject

编辑:编译器根据泛型约束解析对泛型类型的任何成员调用。这在 MSDN C# Programming Guide on Constraints on Type Parameters中有所暗示:

通过约束类型参数,可以将允许的操作和方法调用的数量增加到约束类型及其继承层次结构中的所有类型所支持的数量。因此,当您设计泛型类或方法时,如果您要对泛型成员执行简单赋值以外的任何操作或调用 System.Object 不支持的任何方法,则必须对类型参数应用约束。

为了帮助澄清问题:假设您在Foo类中定义了一个新方法 , :

class SomeObject
{
    public SomeObject() { }

    public void Foo() { }
}

尝试调用Foo会导致编译时错误。编译器唯一知道泛型类型T的是它有一个无参数的构造函数——它不知道它可能定义的任何方法。

class Test<T> where T : new()
{
    public Test()
    {
        T t = new T();
        t.Foo();   // Error: 'T' does not contain a definition for 'Foo' 
                   //        and no extension method 'Foo' accepting a
                   //        first argument of type 'T' could be found
    }
}

但是,如果您限制T为 type SomeObject,那么编译器将知道FooSomeObject类中查找 的定义:

class Test<T> where T : SomeObject, new()
{
    public Test()
    {
        T t = new T();
        t.Foo();   // SomeObject.Foo gets called
    }
}

隐藏成员的推理非常相似。

于 2012-05-05T20:30:59.203 回答
1

Test<T>中,编译器不知道T实际上会是SomeObject,因为对 没有约束T。所以它只能假设那t是一个object,并且调用t.ToString()导致调用虚Object.ToString方法,而不是 SomeObject.ToString()

于 2012-05-05T20:39:02.787 回答
0

这与泛型完全无关,与您声明了一个名为 的方法ToString而不是覆盖现有方法这一事实有关。

如果您改写了该方法,它将被使用,这与早期绑定与后期绑定无关(如此处的其他答案所示。)

ToString调用引用与T引用没有区别的原因object是编译器无法验证这里可以使用的所有' 都定义 了那个新方法,因此它必须回退到从 object 继承的那个在所有情况下。TToString

请注意,编译器将生成一个方法,该方法将被 的所有变体使用T,因此它必须使用它所拥有的关于 T 的知识,并且您没有声明您从接口或类继承(尽管在这种情况下我怀疑它会有所作为)所以编译器无法知道ToString在这种情况下已被覆盖。

另一方面,如果您声明有问题T的 是 的后代SomeObject,编译器会知道它有一个 方法可以使用,但是编译器无法单独ToString使用该知识。T

于 2012-05-05T20:33:58.410 回答