6

只是出于好奇,为什么编译器对待不受约束的泛型类型与 typeof(object) 有任何不同?

class Bar { }

class Foo
{
    void foo(object thing)
    {
        ((Bar)thing).ToString();
    }
}

class Foo<T>
{
    void foo(T thing)
    {
        ((Bar)thing).ToString();
    }
}

在上面,将“T thing”转换为 Bar 会导致编译器错误。然而,将“对象事物”转换为 Bar 是编译器允许我做的事情,当然风险自负。

我不明白为什么。毕竟在 .net 中的对象是一个包罗万象的对象,运行时类型可以是装箱的值或任何类型的对象。所以我看不出编译器有什么逻辑理由来区分这两种情况。我能做的最好的事情是“程序员希望编译器对泛型类型进行类型检查,而不是对对象进行类型检查”。:) 这就是它的全部内容吗?

顺便说一句,我知道我仍然可以在 Foo 案例中完成我的演员表,只需编写

((Bar)(object)thing).ToString();

我只是想了解为什么编译器会这样做......

4

2 回答 2

4

这里的意义在于object。如果第一个示例不是其他object示例,它的行为将相同。基本上,你现在在这里说的是:

(Bar)thing

是:“将a转换T为a Bar”;在一般情况下,这远不合法。通过添加object你使它:

(Bar)(object)thing

这是“将 a 转换Tobject...” - 这始终是合法的,因为object它是所有托管类型的根;并注意这可能会涉及一个框 - “......然后再次将其转换object为一个Bar”; 它在编译时总是合法的,并且在运行时涉及类型检查(“unbox-any”)。

例如:假设TDateTime...

DateTime thing = ...
Bar bar = (Bar)(object)thing;

完全有效;确定它会在运行时失败,但是:这是您需要牢记的场景。

于 2013-02-15T14:14:11.030 回答
4

它归结为创建泛型的语义和目的。如果你有一个通用类型 T,编译器不会让你随意将它直接转换为任何其他对象。这是有道理的,因为 T 的目的是强制程序员指定 T 实际上是什么类型。它不会是“对象”,而是特定类型的对象。在编译时,编译器无法知道 T 中的内容,因此无法转换它。

从对象进行强制转换是因为它是一个匿名对象 - 与在其使用中定义的 KNOWN 对象类型相反。

这可以通过“where”子句进行扩展。例如,您可以指定 T 必须是 IBar 类型;

interface IBar { }

class Bar : IBar { }

class Foo<T>
    where T : IBar
{
    void foo(T thing)
    {
        ((IBar)thing).ToString();
    }
}

继承也适用于 where 子句;

class Bar { }

class Foo<T>
    where T : Bar
{
    void foo(T thing)
    {
        // now you don't need to cast at all as the compiler knows
        // exactly what type T is (at a parent level at least)
        thing.ToString();
    }
}
于 2013-02-15T14:19:32.710 回答