2013-02-15 100 views
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提供“object thing”是编译器允许我做的事情,当然,我自己承担风险。

我不明白的是为什么。在.net对象中,所有内容都是全部捕获的,而运行时类型可能是盒装值或任何类型的对象。所以我看不出有什么合乎逻辑的理由让编译器区分这两种情况。我能做的最好的事情就像“程序员希望编译器使用泛型类型进行类型检查,但不使用对象”。 :)这一切都有它吗?

顺便说一句,我知道我还可以得到我的演员在美孚的情况下完成的,通过写

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

我只是想了解为什么编译器做到这一点...

+3

编译时间是否合法将'int'转换为'Bar'?当你用'int'填充那个类型参数时,它应该*然后*开始有编译器错误吗?如果装配不是你的,那你怎么看不出问题呢? T不是客体。这是非常具体的东西。 – 2013-02-15 14:12:09

+1

你是否也知道你可以说'Foo 其中T:Bar'来保证'T'总是可以投射到'Bar'? – Rawling 2013-02-15 14:12:12

+1

我确信Eric Lippert在这个地方有一篇博客文章,但我找不到它...... – 2013-02-15 14:13:57

回答

4

的这里的意义是object。如果第一个例子是其他任何非object它将表现相同。基本上,你在说什么,此刻在这里:

(Bar)thing 

是:“转换TBar”;这在一般情况下并不合法。通过添加object你把它:

(Bar)(object)thing 

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

例如:假设TDateTime ...

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

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

+0

虽然正确,但我认为操作系统要求设计决策背后的选择是在* compile *时间后面的情况下显示错误,而在*前面显示前者的时间(假设事物不能转换为“Bar”。 – 2013-02-15 14:17:44

+0

我认为这是一个很好的解释。将对象转换为某个东西通常是一种沮丧,更具体的东西,但一般情况下可以是,也可以是*构造的*类型不会编译任何T - 这是给编译时错误的一个很好的理由! – 2013-02-22 16:24:45

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(); 
    } 
} 
+0

我很高兴将此标记为答案,但我发现你的推理比我原来的建议多一点;它确实沸腾直到“用户”(我的泛型类的用户,或者确切地说是一个已经定义了T的构造类)的期望。要说“T不仅仅是一个对象,它是特定的”,恕我直言,任何物体都可以,通常是非常sp的东西ecific。有不同的方法来查看同一个对象。我可能想要支持任何类型,但是如果类型是我知道的类型,则以特定方式运行,例如 - 通用类或不是。 – 2013-02-22 16:22:23

+0

@TheDag尽管存在处理特定类型的有效情况,例如,装箱或非装箱类型,但基于类型T改变泛型的行为通常不是一个好主意。在你的例子中,你试图直接从T中获得,默认情况下,它几乎肯定会成为另一种特定类型“Bar”的特定类型。当没有关系或通用接口时,这将尝试尝试执行'(Foo)Bar'。我仍然认为编译器警告是有效的 – DiskJunky 2013-02-22 16:29:11