泛型和类型约束的规则

本文关键字:规则 约束 类型 泛型 | 更新日期: 2023-09-27 17:59:24

只是出于好奇,为什么编译器对待不受约束的泛型类型与对待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,当然风险自负。

我不明白为什么。In.net对象毕竟是一个catch-all,运行时类型可以是一个装箱值或任何类型的对象。所以我不明白编译器区分这两种情况的逻辑原因是什么。我能做的最好的事情是"程序员希望编译器用泛型类型进行类型检查,但不要用对象"。:)这就是全部吗?

顺便说一句,我知道我仍然可以在Foo的情况下完成我的演员阵容,只需写

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

我只是想了解编译器为什么要这样做。。。

泛型和类型约束的规则

这里的意义是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;

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

它归结为创建泛型的语义和目的。如果您有一个通用类型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();
    }
}