编译器无法转换受约束的泛型类型
-
06-07-2019 - |
题
我有一个具有通用类型“G”
的类在我的班级模特中我有
public class DetailElement : ElementDefinition
假设我有一个像这样的方法
public void DoSomething<G>(G generic)
where G : ElementDefinition
{
if (generic is DetailElement)
{
((DetailElement)generic).DescEN = "Hello people"; //line 1
//////
ElementDefinition element = generic;
((DetailElement)element).DescEN = "Hello again"; //line 3
//////
(generic as DetailElement).DescEN = "Howdy"; //line 5
}
else
{
//do other stuff
}
}
编译器报告第1行中的一个错误:
Cannot convert type 'G' to 'DetailElement'
但第3行工作正常。 我可以通过执行第5行编写的代码解决此问题。
我想知道的是,为什么编译器报告第1行中的错误而不是第3行中的错误,因为据我所知,它们是相同的。
编辑:恐怕我可能会遗漏一些重要的框架逻辑
edit2:虽然编译器错误的解决方案很重要,但我的问题是编译器为什么在第1行而不是第3行报告错误。
解决方案
如果 G
被约束为 DetailElement
(,其中G:DetailElement
),那么你可以继续演员 G
到ElementDefinition,即“(ElementDefinition)generic
”。但是因为 G
可能是运行时 DetailElement
以外的 ElementDefinition
的另一个子类,所以它不会在编译时允许它的类型是未知和无法证实的。
在第3行中,您从投射的类型已知为 ElementDefinition
,因此您所做的就是向上投射。编译器不知道它是否会在运行时成为一个succcesful演员,但它会相信你。编译器不太相信泛型。
第5行中的 as
运算符也可能返回null,并且编译器不会静态检查类型以查看在这种情况下它是否安全。您可以将用作
与任何类型,而不仅仅是与 ElementDefinition
兼容的类型。
其他提示
通常,向上转换是一种代码气味。您可以通过方法重载来避免它。试试这个:
public void DoSomething(DetailElement detailElement)
{
// do DetailElement specific stuff
}
public void DoSomething<G>(G elementDefinition)
where G : ElementDefinition
{
// do generic ElementDefinition stuff
}
然后,您可以使用此代码来利用方法重载:
DetailElement foo = new DetailElement();
DoSomething(foo); // calls the non-generic method
DoSomething((ElementDefinition) foo); // calls the generic method
你的where子句不应该是“where G:DetailElement”吗?
在您编写的代码中,DetailElement是ElementDefinition,但ElementDefinition不一定是DetailElement。所以隐式转换是非法的。
您是否可以将其他类型的ElementDefinition传递给此方法?如果是这样,当您尝试将它们转换为DetailElement实例时,它们将抛出异常。
编辑:
好的,现在您已经更改了代码清单,我可以看到您在输入该代码块之前检查类型以确保它确实是DetailElement。不幸的是,事实是即使你已经自己检查了这些类型,你也不能暗中贬低。我认为你真的应该使用“as”;块开头的关键字:
DetailElement detail = generic as DetailElement;
if (detail == null) {
// process other types of ElementDefinition
} else {
// process DetailElement objects
}
更好的是,为什么不使用多态来允许每种ElementDefinition定义自己的DoSomething方法,让CLR为你处理类型检查和方法调用?
如果你有很多你担心的ElementDefinitions,这会导致更多的代码,但是你可能得到的最简单的代码就是废话。
public void DoSomething<G>(G generic)
where G : ElementDefinition
{
DetailElement detail = generic as DetailElement;
if (detail != null)
{
detail.DescEN = "Hello people";
}
else
{
//do other stuff
}
}
我在需要此类信息时使用的另一种可能的解决方案,在临时对象变量的loo中。
DetailElement detail = (DetailElement)(object)generic;
它有效,但形式可能是最好的。