我有一个界面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();
}
}
// ...
}
}
修改:
我只是想知道为什么这是编译错误。
我知道这是一个奇怪的设计,无论如何你通常不会在通用方法中投射。当我想在测试期间添加一些快速记录信息时,我遇到了这种情况,这是努力最少的路径。
答案 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
限制承诺TApiData
是IApiDataWithProperties
;它不保证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)
,因为它不是必需的。