编译器无法转换约束泛型类型

问题描述:

我已经有一个通用型的“G”编译器无法转换约束泛型类型

在我的类模型I类有

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被限制为一DetailElementwhere G : DetailElement),那么你可以继续投G到ElementDefinition,即“(ElementDefinition) generic”。但是因为G可能是运行时DetailElement以外的另一个ElementDefinition的子类,所以它不会在编译时允许它在类型未知和不可验证的情况下运行。

在3线从投类型为已知的ElementDefinition因此,所有你正在做的是上投。编译器不知道它在运行时是否会成功,但它会在那里信任你。编译器不那么信任泛型。

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

Can I Cast to and from Generic Type Parameters? MSDN上:

编译器将只让你隐式转换泛型类型参数为对象,或约束指定类型。

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

编译器将让您显式转换泛型类型参数的任何接口,而不是一类:

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

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

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

不用说,这种显式转换是危险的,因为它可能会引发在运行时异常,如果用来代替一般类型参数的具体类型不从您明确强制转换为类型派生。

更好的方法是使用isas运算符代替冒着抛出异常的风险。如果泛型类型参数为查询类型,则is运算符返回true,如果类型兼容,则as将执行强制转换,否则将返回null。

public void SomeMethod(T t) 
{ 
    if(t is int) {...} 

    string str = t as string; 
    if(str != null) {...} 
} 

不应该在where子句中“where G:DetailElement”?

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

您可能会传入此方法的其他类型的ElementDefinition?如果是这样,当您尝试将它们转换为DetailElement实例时,它们会抛出异常。

编辑:

好了,现在你已经改变了你的代码,我可以看到你正在检查的类型,以确保它确实是进入代码块之前DetailElement。不幸的是,事实是,即使你已经自己检查过类型,你也不能隐式地downcast。我觉得你真的应该在你块的开头使用“为”关键词:

DetailElement detail = generic as DetailElement; 
if (detail == null) { 
    // process other types of ElementDefinition 
} else { 
    // process DetailElement objects 
} 

更好的是,为什么不使用多态性允许各种ElementDefinition来定义自己的DoSomething的方法,并让CLR照顾你的类型检查和方法调用?

+0

为了简单起见,我没有把这里的所有代码。我会编辑它是正确的 – 2008-10-08 17:46:17

这将导致更多的代码,如果你有很多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 
     } 
    } 

另一个可能的解决方案,当我需要这样的信息时,我用了一个临时对象变量。

DetailElement detail = (DetailElement)(object)generic; 

它的作品,但作为形式可能是最好的。

通常情况下,上传是一种代码异味。您可以通过方法重载来避免它。试试这个:

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