Почему я не могу привести этот интерфейс к конкретному классу?
У меня есть интерфейс 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 ответов
Причина в том, что newItem
может быть любого типа, который реализует IApiDataWithProperties
поэтому компилятор не может гарантировать, что его тип может быть преобразован в Event
, Даже если вы проверите это с помощью is
оператор это ничего не значит для компилятора. В качестве обходного пути вы можете использовать двойное приведение:
((Event)(object)newItem).Log();
Даже если это работает, это не значит, что вы должны его использовать. Вы не должны проверять тип в универсальном методе. Вместо этого попробуйте использовать полиморфизм, добавьте Log
метод для IApiDataWithProperties
или какой-то другой интерфейс и реализовать его в ваших типах. Затем установите другое ограничение для этого интерфейса, затем вы можете вызывать метод без необходимости приведения.
Короткая версия - вы можете использовать:
(newItem as Event).Log();
вместо твоего состава.
Длинная версия - ваша TApiData
объекты реализует IApiDataWithProperties
так может быть Event
, но это может быть все остальное, реализующее это. Это называется устареванием и должно выполняться во время выполнения с помощью as
/is
операторы. Компилятор не знает во время компиляции, если newItem
универсального типа действительно Event
поэтому он не может обеспечить такой актерский состав.
Поскольку 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
Вы можете объявить метод Log внутри интерфейса IApiDataWithProperties. Реализуйте метод Log в своем классе Event, а затем там, где вы получаете исключение, приведите newItem к IApiDataWithProperties, а не к Event.
((IApiDataWithProperties)newItem).Log
Или на самом деле, думая об этом, вам может не понадобиться актерский состав
newItem.Log
Ваш дизайн не лучший для начала, так как он предполагает реализацию определенного класса. Но если вы хотите, чтобы это было сделано так....
с помощью as
будет работать, но вы, вероятно, хотите, чтобы он был ограничен также ссылочными типами.
where TApiData : IApiDataWithProperties, class
...
(newItem as Event).Log();
Хотя это не совсем безопасно, так как это может привести к нулю, вы, вероятно, должны проверить это также
var item = newItem as Event;
if(item != null)
item.Log();
Вы также можете устранить линию if(newItem is Event)
как это не требуется.