C# 编译器无法转换受约束的泛型类型

C# 编译器无法转换受约束的泛型类型,c#,generics,casting,constraints,C#,Generics,Casting,Constraints,我有一个类的泛型类型为“G” 在我的班级模式中,我有 public class DetailElement : ElementDefinition 假设我有一个这样的方法 public void DoSomething<G>(G generic) where G : ElementDefinition { if (generic is DetailElement) {

我有一个类的泛型类型为“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
            }
        }
但是第三行很好用。 我可以通过执行第5行中编写的代码来解决这个问题

我想知道的是,为什么编译器报告第1行的错误而不是第3行的错误,因为据我所知,它们是相同的。

编辑:我担心我可能遗漏了框架逻辑的一些重要部分


edit2:虽然编译器错误的解决方案很重要,但我的问题是为什么编译器在第1行报告错误而不是在第3行报告错误。

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
}

更好的是,为什么不使用多态性来允许每种元素定义定义自己的DoSomething方法,并让CLR为您负责类型检查和方法调用呢?

如果
G
被限制为
DetailElement
其中G:DetailElement
)然后您可以继续并将
G
转换为ElementDefinition,即“
(ElementDefinition)generic
”。但是,因为在运行时,
G
可能是
ElementDefinition
的另一个子类,而不是
DetailElement
,所以在类型未知且无法验证的编译时,它将不允许它

在第3行中,您从中强制转换的类型是已知的
元素定义
,因此您所做的只是向上强制转换
。编译器不知道它在运行时是否会成功转换,但它会信任您。编译器不太信任泛型

第5行中的
as
运算符也可能返回null,并且编译器不会静态检查类型,以确定在这种情况下是否安全。您可以将
作为
用于任何类型,而不仅仅是与
元素定义
兼容的类型

从MSDN开始:

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

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

编译器将允许您显式地将泛型类型参数强制转换为任何接口,而不是类:

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

如果您有很多担心的元素定义,那么这将导致更多的代码,但可能是您将得到的最精巧的、不涉及的代码,这将被视为胡说八道

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

它是有效的,但as形式可能是最好的

通常,向上投射是一种代码气味。可以通过方法重载来避免这种情况。试试这个:

public void DoSomething(DetailElement detailElement)
{
    // do DetailElement specific stuff
}

public void DoSomething<G>(G elementDefinition)
    where G : ElementDefinition
{
    // do generic ElementDefinition stuff
}

为了简单起见,我没有把所有的代码都放在这里。我会把它编辑成正确的
public void SomeMethod(T t)
 {
   if(t is int) {...}

   string str = t as string;
   if(str != null) {...}
 }
    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