C# 泛型规则和类型约束
出于好奇,为什么编译器对待无约束泛型类型与对待typeof(object)有任何不同C# 泛型规则和类型约束,c#,.net,generics,C#,.net,Generics,出于好奇,为什么编译器对待无约束泛型类型与对待typeof(object)有任何不同 我只是想了解编译器为什么这样做…这里的意义是对象。如果第一个示例是除对象之外的任何对象,则其行为相同。基本上,你现在说的是: (Bar)thing 是:“将T转换为Bar”;这在一般情况下是不合法的。通过添加对象可以: (Bar)(object)thing 这是“将T转换为对象”..,这始终是合法的,因为对象是所有托管类型的根;请注意,这可能涉及一个框-“…然后将对象再次转换为条”;它在编译时总是合法的,并
我只是想了解编译器为什么这样做…这里的意义是
对象。如果第一个示例是除对象之外的任何对象
,则其行为相同。基本上,你现在说的是:
(Bar)thing
是:“将T
转换为Bar
”;这在一般情况下是不合法的。通过添加对象
可以:
(Bar)(object)thing
这是“将T
转换为对象”
..,这始终是合法的,因为对象是所有托管类型的根;请注意,这可能涉及一个框-“…然后将对象
再次转换为条
”;它在编译时总是合法的,并且在运行时涉及类型检查(“unbox any”)
例如:假设T
是DateTime
DateTime thing = ...
Bar bar = (Bar)(object)thing;
是完全有效的;当然,它在运行时会失败,但是:这是您需要记住的场景。它涉及到创建泛型的语义和目的。如果您有一个通用类型T,编译器将不允许您将其直接强制转换到任何其他对象。这是有意义的,因为T的目的是迫使程序员指定T实际上是什么类型。它不是“对象”,而是一种特定类型的对象。在编译时,编译器无法知道T中的内容,因此无法强制转换它
从对象强制转换的工作原理是,它是一个匿名对象,而不是在其用法中定义的已知对象类型
这可以通过“where”子句进行扩展。例如,您可以指定T必须为IBar类型
interface IBar { }
class Bar : IBar { }
class Foo<T>
where T : IBar
{
void foo(T thing)
{
((IBar)thing).ToString();
}
}
接口IBar{}
类栏:IBar{}
福班
其中T:IBar
{
空福(T物)
{
((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();
}
}
类栏{}
福班
T:酒吧在哪里
{
空福(T物)
{
//现在,编译器知道,您根本不需要强制转换
//确切的T类型(至少在父级)
thing.ToString();
}
}
在编译时将int
强制转换为Bar
合法吗?当您用int
填充该类型参数时,它是否会开始出现编译器错误?如果程序集不是您的,因此您看不到问题,该怎么办?T不是对象。这是一个非常具体的东西。你是否也知道你可以说class Foo where T:Bar
,以确保T
始终可以投射到Bar
?我肯定Eric Lippert在某个地方有一篇关于这一点的博客文章,但我找不到它……你的问题是你认为它没有任何意义,因为将装箱值类型强制转换为任何引用类型都会导致错误,所以真正的问题肯定是编译器没有(通用)方法来区分装箱值类型和引用类型;因为如果可以的话,即使在没有泛型的情况下,它也会从对象
为此类强制转换引发编译时错误。虽然正确,但我认为OP要求在选择后一种情况时在编译时公开错误,但在前一种情况下在运行时公开错误(假设这个东西不能转换为条形码
。你们两个都是(@Marc,@Ron)注意。我认为这是一个很好的解释。将对象强制转换到某个对象总是一个向下转换,到某个更具体的对象,但一般情况下,两者都可以,因此构造的类型不会为任何T编译-这是给出编译时错误的一个很好的理由!我很高兴将此标记为答案,尽管我发现您的推理相当于liTTE不仅仅是我最初的建议;它确实可以归结为“用户”(我的泛型类的用户,或者更确切地说是一个定义了T的构造类)的期望is,IMHO,superflous。任何对象都可以而且通常都是非常特定的对象。查看同一对象有不同的方式。我可能想支持任何类型,但如果类型是我所知道的类型,则以特定的方式进行操作,例如-泛型类或非泛型类。@Tag基于类型T更改泛型的行为通常不是一个好方法尽管有处理特定类型的有效案例,例如装箱或未装箱类型。在您的示例中,您尝试直接从T(默认情况下几乎肯定是一种特定类型)转到另一种特定类型Bar
。这将使您尝试执行(Foo)Bar
当没有关系或公共接口时。我仍然认为编译器警告是有效的
interface IBar { }
class Bar : IBar { }
class Foo<T>
where T : IBar
{
void foo(T thing)
{
((IBar)thing).ToString();
}
}
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();
}
}