编译器无法转换受约束的generics类型

我有一个通用类型为“G”的类

在我的class级模型中我有

public class DetailElement : ElementDefinition 

假设我有这样的方法

  public void DoSomething(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被约束为DetailElementwhere G : DetailElement ),则可以继续将G为ElementDefinition,即“ (ElementDefinition) generic ”。 但是因为在运行时G可能是除DetailElement之外的DetailElement另一个子类,所以在编译时它不允许它在类型未知且无法validation的情况下。

在第3行中,你所使用的类型是一个ElementDefinition所以你所做的就是上传 。 编译器不知道它是否会在运行时成为一个succcesful演员,但它会信任你。 编译器不太相信generics。

第5行中的as运算符也可能返回null,并且编译器不会静态检查类型以查看在这种情况下它是否安全。 您可以使用任何类型,而不仅仅是与ElementDefinition兼容的ElementDefinition

我可以转换为通用类型参数吗? 在MSDN上:

编译器只允许您将generics类型参数隐式转换为对象或约束指定的类型。

这种隐式转换当然是类型安全的,因为在编译时发现任何不兼容性。

编译器将允许您将generics类型参数显式转换为任何接口,但不能转发给类:

  interface ISomeInterface {...} class SomeClass {...} class MyClass { void SomeMethod(T t) { ISomeInterface obj1 = (ISomeInterface)t;//Compiles SomeClass obj2 = (SomeClass)t; //Does not compile } } 

但是,您可以使用临时对象变量强制从generics类型参数转换为任何其他类型

  void SomeMethod(T t) { object temp = t; MyOtherClass obj = (MyOtherClass)temp; } 

毋庸置疑,这种显式转换很危险,因为如果使用的具体类型而不是generics类型参数不是从您显式转换为的类型派生的,那么它可能会在运行时抛出exception。

更好的方法是使用isas运算符,而不是冒出一个转换exception的风险。 如果generics类型参数是查询类型,则is运算符返回true,如果类型兼容则执行强制转换,否则返回null。

 public void SomeMethod(T t) { if(t is int) {...} string str = t as string; if(str != null) {...} } 

通常,向上转换是代码气味。 您可以通过方法重载来避免它。 试试这个:

 public void DoSomething(DetailElement detailElement) { // do DetailElement specific stuff } public void DoSomething(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子句不应该是“G:DetailElement”吗?

在您编写的代码中,DetailElement是ElementDefinition,但ElementDefinition不一定是DetailElement。 所以隐式转换是非法的。

是否有其他类型的ElementDefinition可以传递给此方法? 如果是这样,当您尝试将它们转换为DetailElement实例时,它们将抛出exception。

编辑:

好的,现在您已经更改了代码清单,我可以看到您在输入该代码块之前检查类型以确保它确实是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 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; 

它有效,但forms可能是最好的。