为什么我不能将此接口转换为具体类?

时间:2014-11-27 22:39:30

标签: c# generics

我有一个界面IApiDataWithProperties。名为Event的类实现了此接口。

通常我可以将IApiDataWithProperties的对象强制转换为Event(假设它是一个),编译器让我这样做没问题。

在这种情况下,该类型实际上是通用TApiData,其对接口where具有IApiDataWithProperties限制。但是,即使有类型限制,我也无法将TApiData投射到Event。我得到Cannot convert type 'TApiData' to 'Event'

这是为什么?我错过了什么吗?

public class Event : IApiDataWithProperties, IXmlSerializable
{
    // ...
}


public abstract class AbstractBatchPropertyProcessor<TApiData> : AbstractBatchProcessor<TApiData>, IBatchProcessor
    where TApiData : IApiDataWithProperties
{
    protected virtual string Build(ConcurrentBag<TApiData> batch)
    {
        foreach (var newItem in batch)
        {   
           if(newItem is Event)
           {
              // This cast fails: Cannot convert type 'TApiData' to 'Event'
              ((Event)newItem).Log();
           }
        }

        // ...  
    }
}

修改

我只是想知道为什么这是编译错误。

我知道这是一个奇怪的设计,无论如何你通常不会在通用方法中投射。当我想在测试期间添加一些快速记录信息时,我遇到了这种情况,这是努力最少的路径。

5 个答案:

答案 0 :(得分:3)

原因是newItem可以是实现IApiDataWithProperties的任何类型,因此编译器无法保证其类型可转换为Event。即使您使用is运算符进行检查,它对编译器也没有任何意义。作为解决方法,您可以使用 double cast

((Event)(object)newItem).Log();

尽管这有效,但并不意味着你应该使用它。您不应该检查通用方法中的类型。而是尝试使用多态,将Log方法添加到IApiDataWithProperties或其他一些接口,并在您的类型中实现它。然后为该接口设置另一个约束,然后您可以在不需要强制转换的情况下调用该方法。

答案 1 :(得分:2)

简短版 - 您可以使用:

(newItem as Event).Log();

而不是你的演员。

长版本 - 您的TApiData对象实现了IApiDataWithProperties,因此可以Event,但它可以是实现它的所有其他内容。这称为向下转换,必须在as / is运营商的帮助下在运行时完成。编译器在编译期间不知道泛型类型的newItem是否真的是Event,因此它无法保证这样的强制转换。

答案 2 :(得分:1)

因为 where限制承诺TApiDataIApiDataWithProperties;它不保证IApiDataWithProperties实现的类型。

更多

每个Child都是Parent的实例,但反之亦然。想想这个模型:

interface I { void InterfaceNethod(); }

class A : I {
    void InterfaceMethod() { }
    void AMethod() { }
}

class B : I {
    void InterfaceMethod() { }
    void BMethod() { }
}

现在,让我们获取一些实例,并调用他们的方法:

I i = new A();
i.InterfaceMethod(); // it works
i.AMethod(); // it doesn't work, cause I has not a method named AMethod

现在,铸件:

A a = new A();
a.InterfaceMethod(); // exists
a.AMethod(); // exists

I i = (I)a; // correct
i.InterfaceMethod(); // exists

B b = (B)i;
// if this be correct, then we should be able to call B's methods on b, right?
// While b hasn't any of B's members.
// I mean calling this:
b.BMethod();
// is logically incorrect. right? because, following the object's reference in memory
// would tell us that b is pointing to an A instance actually. Am I right?
// so the cast will fail. Because compiler knows about logic :) a little bit at least. cheers

答案 3 :(得分:0)

您可以在接口IApiDataWithProperties中声明Log方法。 在Event类中实现Log方法,然后在获得异常的位置将newItem转换为IApiDataWithProperties而不是Event。

((IApiDataWithProperties)newItem).Log

或者实际上考虑一下你可能不需要演员而且可以做

 newItem.Log

答案 4 :(得分:-1)

您的设计不是最好的开始,因为它假定特定类的实现。但万一你想要这样做......

使用as会起作用,但您可能希望它也限制为引用类型。

 where TApiData : IApiDataWithProperties, class

...

 (newItem as Event).Log();

虽然这不是完全安全的,因为它可能导致null,你可能也应该检查

 var item = newItem as Event;
 if(item != null)
     item.Log();

您也可以删除行if(newItem is Event),因为它不是必需的。