Linq ToDictionary не будет неявно преобразовывать класс в интерфейс

У меня есть следующий код

public class TestAdaptor
{
    private interface ITargetClass
    {
        Guid Id { get; }

        string Name { get; }
    }

    private class MyTargetClass : ITargetClass
    {
        public Guid Id { get; private set; }

        public string Name { get; private set; }

        public MyTargetClass(MySourceClass source)
        {
        }
    }

    private class MySourceClass
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
    }

    private Dictionary<Guid, IEnumerable<ITargetClass>> ConvertItems(Dictionary<Guid, IEnumerable<MySourceClass>> source)
    {
        return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Select(v => new MyTargetClass(v)));
    }
}

Однако это не скомпилируется, так как строка ToDictionary вызывает следующую ошибку

Cannot implicitly convert type
'System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.IEnumerable<TestAdaptor.TestAdaptor.MyTargetClass>>'
to
'System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.IEnumerable<TestAdaptor.TestAdaptor.ITargetClass>>'   ...\TestAdaptor.cs  38  20

Теперь очевидно, что MyTargetClass реализует ITargetClass, но компилятор этого не понимает.

На данный момент я конвертирую (ITargetClass) новый MyTargetClass(v)

Но почему это происходит в первую очередь, и есть ли лучший способ решить эту проблему?

2 ответа

Решение

Компилятор не будет автоматически конвертировать IEnumberable<X> в IEnumerable<Y> даже если X : Y так как IDictionary не является ковариантным. Обоснование этого обсуждается здесь: IDictionary<,> contravariance? и IDictionary в.NET 4 не ковариантен

Что касается того, чтобы обойти это, как вы упомянули, вам придется разыграть:

С методом расширения Cast:

 kvp => kvp.Value.Select(v => new MyTargetClass(v)).Cast<ITargetClass>()

Явное приведение:

 kvp => kvp.Value.Select(v => (ITargetClass) new MyTargetClass(v))

Обновление:

Просто чтобы расширить это из-за путаницы между IEnumerable а также IDictionary, IEnumerable является ковариантным. IDictionary нет

Это просто отлично

 IEnumerable<ITargetClass> list = new List<MyTargetClass>();

Это не:

 IDictionary<object, IEnumerable<ITargetClass> dict = 
     new Dictionary<object, List<MyTargetClass>;

IDictionary наследуется от IEnumerable<KeyValuePair<TKey, TValue>>, Речь идет о KeyValuePair который не является ковариантным, что делает IDictionary не ковариантный.

Выберите только отчеты о том, что создается, а не аспект типа объекта. Пусть select знает, что вы используете через ключевое слово as.

Select(v => new MyTargetClass(v) as ITargetClass));

Это не работа компилятора, чтобы понять intention разработчика, для класса может выражать много интерфейсов. Нужно дать подсказки select заявление, которое в конечном итоге приводит его в соответствие с требуемым объектом возврата.

В противном случае вы можете фильтровать элементы и возвращать IEnumerable интерфейса, используя OfType IEnumerable расширение, чтобы вернуть то, что требуется вашим методом.

.Select(v => new MyTargetClass(v))
.OfType<ITargetClass>()
Другие вопросы по тегам