编译器无法转换受限通用类型 [英] Compiler fails converting a constrained generic type

查看:120
本文介绍了编译器无法转换受限通用类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类具有泛型类型G



在我的类模型中有

  public class DetailElement:ElementDefinition 

像这样

  public void DoSomething< G>(G generic)
其中G:ElementDefinition
{
if(generic是DetailElement)
{
((DetailElement)generic).DescEN =Hello people; // line 1
//////
ElementDefinition element = generic;
((DetailElement)element).DescEN =Hello again; // line 3
//////
(generic作为DetailElement).DescEN =Howdy; // line 5
}
else
{
// do other stuff
}
}

编译器报告第1行中的一个错误:

 将类型'G'转换为'DetailElement'

但第3行工作正常。
我可以通过执行第5行中写的代码来解决这个问题。



我想知道的是编译器报告错误的原因在第1行,而不是第3行,假设我知道,他们是相同的。



编辑:恐怕我可能缺少框架逻辑的一些重要部分



edit2:虽然编译器错误的解决方案很重要,但我的问题是为什么编译器报告第1行的错误,如果 G 被限制为

code> DetailElement (其中G:DetailElement ),那么你可以继续投入 G 到ElementDefinition,即(ElementDefinition)generic 。但因为 G 可能是 ElementDefinition 的另一个子类,而不是 DetailElement



在第3行中,你从<>类型转换为 已知是 ElementDefinition ,因此您所做的只是一个展开。编译器不知道它是否会在运行时成功执行,但它会信任你。编译器不太信任泛型。



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



我可以在MSDN上转换和从常规类型参数转换


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



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



编译器将允许您将通用类型参数显式转换到任何接口,而不是类:

  interface ISomeInterface {...} 
class SomeClass {...}
class MyClass< T>
{
void SomeMethod(T t)
{
ISomeInterface obj1 =(ISomeInterface)t; //编译
SomeClass obj2 =(SomeClass)t; //不编译
}
}

使用临时对象变量从泛型类型参数转换为任何其他类型

  void SomeMethod< T& $ b {object temp = t; 
MyOtherClass obj =(MyOtherClass)temp;
}

不用说,这种显式转换是危险的,因为它可能会抛出异常运行时,如果使用的具体类型而不是通用类型参数不是从显式转换的类型派生。



一个更好的方法是使用 as 运算符。如果通用类型参数是查询类型,运算符则返回true,如果 类型是兼容的,否则返回null。

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

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



I have a class that has a Generic type "G"

In my class model i have

public class DetailElement : ElementDefinition

Let's say i have a method like this

        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
            }
        }

Compiler reports one error in line 1:

Cannot convert type 'G' to 'DetailElement'

But line 3 works fine. I can workaround this issue by doing the code written in line 5.

What i would like to know is why does the compiler reports the error in line 1 and not the one in line 3, given that, as far as i know, they are identical.

edit: I am afraid i might be missing some important piece of the framework logic

edit2: Although solutions for the compiler error are important, my question is about why the compiler reports an error on line 1 and not in line 3.

解决方案

If G was constrained to be a DetailElement (where G : DetailElement) then you can go ahead and cast G to ElementDefinition, i.e., "(ElementDefinition) generic". But because G might be another subclass of ElementDefinition other than DetailElement at run-time it won't allow it at compile-time where the type is unknown and unverifiable.

In line 3 the type you cast from is known to be an ElementDefinition so all you're doing is an up-cast. The compiler doesn't know if it will be a succcesful cast at run-time but it will trust you there. The compiler is not so trusting for generics.

The as operator in line 5 might also return null and the compiler doesn't statically check the type to see if it's safe in that case. You can use as with any type, not just ones that are compatible with ElementDefinition.

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

The compiler will only let you implicitly cast generic type parameters to object, or to constraint-specified types.

Such implicit casting is of course type safe, because any incompatibility is discovered at compile-time.

The compiler will let you explicitly cast generic type parameters to any interface, but not to a class:

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

However, you can force a cast from a generic type parameter to any other type using a temporary object variable

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

Needless to say, such explicit casting is dangerous because it may throw an exception at run-time if the concrete type used instead of the generic type parameter does not derive from the type you explicitly cast to.

Instead of risking a casting exception, a better approach is to use the is or as operators. The is operator returns true if the generic type parameter is of the queried type, and as will perform a cast if the types are compatible, and will return null otherwise.

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

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

这篇关于编译器无法转换受限通用类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆