Linq ToDictionary不会隐式地将类转换为接口
本文关键字:转换 接口 ToDictionary Linq | 更新日期: 2023-09-27 18:16:38
我有以下代码
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)
但是为什么会发生这种情况,有没有更好的方法来解决这个问题?
由于IDictionary
不协变,即使X : Y
,编译器也不会自动将IEnumberable<X>
转换为IEnumerable<Y>
。这里讨论了其基本原理:dictionary <;逆变性?和字典<TKey,>.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
is not.
这很好:
IEnumerable<ITargetClass> list = new List<MyTargetClass>();
这不是:
IDictionary<object, IEnumerable<ITargetClass>> dict =
new Dictionary<object, List<MyTargetClass>>();
IDictionary
继承自IEnumerable<KeyValuePair<TKey, TValue>>
。问题是KeyValuePair
不是协变的,这使得IDictionary
不是协变的。
Select只报告正在创建的内容,而不报告对象类型的一个方面。让select通过as关键字知道你正在使用什么。
Select(v => new MyTargetClass(v) as ITargetClass));
理解开发者的intention
不是编译器的工作,因为一个类可以表达许多接口。必须为select
语句提供提示,最终使其与所需的返回对象一致。
否则,您可以过滤元素,并使用OfType IEnumerable
扩展返回接口的IEnumerable以返回您的方法所需的内容。
.Select(v => new MyTargetClass(v))
.OfType<ITargetClass>()