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
Что касается того, чтобы обойти это, как вы упомянули, вам придется разыграть:
С методом расширения 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>()