Linq ToDictionary不会隐式地将类转换为接口

时间:2014-08-05 10:02:03

标签: c# 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 个答案:

答案 0 :(得分:3)

编译器不会自动将IEnumberable<X>转换为IEnumerable<Y>即使X : Y,因为IDictionary不是covariant。这里讨论的理由如下:IDictionary<,> contravariance?IDictionary<TKey, TValue> in .NET 4 not covariant

至于绕过它,就像你提到的那样,你必须施展:

使用Cast扩展方法:

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

明确演员:

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

<强>更新

由于IEnumerableIDictionary之间的混淆,只是为了扩展这一点。 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不协变。

答案 1 :(得分:1)

仅选择报告正在创建的内容而不是对象类型的方面。让选择通过as关键字了解您的使用情况。

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

理解intention开发人员不是编译器的工作,因为类可能表达许多接口。必须提供select语句的提示,最终使其与所需的返回对象一致。

否则,您可以过滤元素,并使用OfType IEnumerable扩展名返回界面的IEnumerable,以返回您的方法所需的内容。

.Select(v => new MyTargetClass(v))
.OfType<ITargetClass>()