泛型和类型约束的规则

时间:2013-02-15 14:08:03

标签: c# .net generics

出于好奇,为什么编译器对待一个不受约束的泛型类型的方式与它的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对象中,毕竟是catch-all,运行时类型可以是盒装值或任何类型的对象。所以我没有看到编译器区分这两种情况的逻辑原因。我能做的最好就是“程序员希望编译器使用泛型类型进行类型检查,但不能使用对象”。 :)这就是它的全部吗?

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

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

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

2 个答案:

答案 0 :(得分:4)

这里的重要性是object。如果第一个示例是object 以外的任何内容,则其行为相同。基本上,你现在在说什么:

(Bar)thing

是:“将T转换为Bar”;在一般情况下,这远远不合法。添加object就可以了:

(Bar)(object)thing

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

例如:假设TDateTime ...

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

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

答案 1 :(得分: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();
    }
}