Почему я не могу привести этот интерфейс к конкретному классу?

У меня есть интерфейс 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) как это не требуется.

Другие вопросы по тегам