简体   繁体   English

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

[英]Compiler fails converting a constrained generic type

I have a class that has a Generic type "G" 我有一个通用类型为“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: 编译器报告第1行中的一个错误:

Cannot convert type 'G' to 'DetailElement'

But line 3 works fine. 但第3行工作正常。 I can workaround this issue by doing the code written in line 5. 我可以通过执行第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. 我想知道的是,为什么编译器报告第1行中的错误而不是第3行中的错误,因为据我所知,它们是相同的。

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

If G was constrained to be a DetailElement ( where G : DetailElement ) then you can go ahead and cast G to ElementDefinition, ie, " (ElementDefinition) generic ". 如果G被约束为DetailElementwhere G : DetailElement ),那么你可以继续将G为ElementDefinition,即“ (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. 但由于G可能是另一个子ElementDefinition比其他DetailElement在运行时也不会允许它在编译时其中类型是未知的,无法证实。

In line 3 the type you cast from is known to be an ElementDefinition so all you're doing is an up-cast . 在第3行,你从称为投射的类型是一个ElementDefinition因此,所有你正在做的是上投 The compiler doesn't know if it will be a succcesful cast at run-time but it will trust you there. 编译器不知道它是否会在运行时成为一个succcesful演员,但它会相信你。 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. 第5行中的as运算符也可能返回null,并且编译器不会静态检查类型以查看在这种情况下它是否安全。 You can use as with any type, not just ones that are compatible with ElementDefinition . 您可以使用as任何类型 ,而不仅仅是那些可以兼容ElementDefinition

From Can I Cast to and from Generic Type Parameters? 我可以转换为通用类型参数吗? on MSDN: 在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. 而不是冒着铸造异常的风险,更好的方法是使用isas运算符。 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. is ,如果泛型类型参数是被查询类型的运算符返回true,并as如果类型兼容将执行强制类型转换,并且将空否则返回。

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

Shouldn't your where clause be "where G : DetailElement"? 你的where子句不应该是“G:DetailElement”吗?

In the code you've written, a DetailElement is an ElementDefinition, but an ElementDefinition is not necessarily a DetailElement. 在您编写的代码中,DetailElement是ElementDefinition,但ElementDefinition不一定是DetailElement。 So the implicit conversion is illegal. 所以隐式转换是非法的。

Are there other types of ElementDefinition that you might pass into this method? 是否有其他类型的ElementDefinition可以传递给此方法? If so, they'll throw an exception when you try to cast them into DetailElement instances. 如果是这样,当您尝试将它们转换为DetailElement实例时,它们将抛出异常。

EDIT: 编辑:

Okay, so now that you've changed your code listing, I can see that you're checking the type to make sure it really is a DetailElement before entering that block of code. 好的,现在您已经更改了代码清单,我可以看到您在输入该代码块之前检查类型以确保它确实是DetailElement。 Unfortunately, the fact of the matter is that you can't implicitly downcast, even if you've already checked the types yourself. 不幸的是,事实是即使你已经自己检查了这些类型,你也不能暗中贬低。 I think you really ought to use the "as" keyword at the beginning of your block: 我认为你真的应该在你的块的开头使用“as”关键字:

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

Better yet, why not use polymorphism to allow each kind of ElementDefinition to define its own DoSomething method, and let the CLR take care of type-checking and method invocation for you? 更好的是,为什么不使用多态来允许每种ElementDefinition定义自己的DoSomething方法,让CLR为您处理类型检查和方法调用?

Generally, upcasting is a code smell. 通常,向上转换是代码气味。 You can avoid it by method overloading. 您可以通过方法重载来避免它。 Try this: 尝试这个:

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

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

You can then take advantage of method overloading by using this code: 然后,您可以使用以下代码来利用方法重载:

DetailElement foo = new DetailElement();

DoSomething(foo); // calls the non-generic method
DoSomething((ElementDefinition) foo); // calls the generic method

This will lead to a bit more code if you have a lot of ElementDefinitions you are worried about, but is probably the slickest you will get that doesn't involve is then as nonsense. 如果你有很多你担心的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
        }
    }

Another possible solution that I have used when I needed such information, in loo of a temporary object variable. 我在需要这些信息时使用的另一种可能的解决方案,在临时对象变量的厕所中。

DetailElement detail = (DetailElement)(object)generic;

It works, but the as form is probably the best. 它有效,但形式可能是最好的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM