当实现IEnumerable <t>两次</t>时,LINQ会感到困惑

时间:2013-02-14 15:36:28

标签: c# linq generics collections ienumerable

我的班级实施了IEnumerable<T>两次。 如何在不每次投射hashtable的情况下让LINQ工作?


我编写了自己的协变哈希表实现,它也继承自.NET的IDictionary<TKey, TValue>。最终,它为IEnumerable<T>实现T两次不同类型。我隐式地实现了主要的可枚举接口,并且明确地实现了另一个。像这样的东西(伪代码):

class HashTable<TKey, TValue> :
    ...
    IEnumerable<out IAssociation<out TKey, out TValue>>,
    IEnumerable<out KeyValuePair<TKey, TValue>>
{
    // Primary:
    public IEnumerator<IAssociation<TKey, TValue>> GetEnumerator();
    // Secondary:
    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator();
}

当我foreach哈希表时,主要可枚举需要as expected

using System;
using System.Collections.Generic;
using System.Linq;

var hashtable = new HashTable<string, int>();
foreach (var kv in hashtable)
{
    // kv is IAssociation<string, int>
}

现在我希望它在LINQ中做同样的事情,但是它在我身上抛弃了编译器错误,因为它不知道为扩展方法选择哪个接口:

var xs1 = from x in hashtable          // <-- 1
          select x;

var xs2 = hashtable.Select(x => x);    // <-- 2
  

错误1:无法找到源类型“HashTable”的查询模式的实现。找不到“选择”。考虑明确指定范围变量'x'的类型。

     

错误2:'HashTable'不包含'Select'的定义,也没有扩展方法'Select'接受类型为'HashTable'的第一个参数'(你是否缺少using指令或汇编引用?)

也许有一些我不知道的接口或继承技巧?


对于那些问过的人,这里是完整的接口树:

using SCG = System.Collections.Generic;

public class HashTable<TKey, TValue>
    : IKeyedCollection<TKey, TValue>, SCG.IDictionary<TKey, TValue>

public interface IKeyedCollection<out TKey, out TValue>
    : ICollection<IAssociation<TKey, TValue>>

public interface ICollection<out T> : SCG.IEnumerable<T>

public interface IAssociation<out TKey, out TValue>

// .NET Framework:
public interface IDictionary<TKey, TValue>
    : ICollection<KeyValuePair<TKey, TValue>>

public interface ICollection<T>
    : IEnumerable<T>

现在您可以看到为什么我无法使KeyValuePair<TKey, TValue>IAssociation<TKey, TValue>相同。

1 个答案:

答案 0 :(得分:25)

重要的是要理解,当使用表达式作为方法调用的参数时,编译器没有“主要”和“次要”接口实现的概念。对于这些类型的转换,您的类型同样可以同时实现IEnumerable<IAssociation<...>>IEnumerable<KeyValuePair<...>>。这就是编译器需要更多信息的原因。

最简单的方法(IMO)将引入两个新属性:

public IEnumerable<IAssociation<TKey, TValue>> Associations { get { return this; } }
public IEnumerable<KeyValuePair<TKey, TValue>> KeyValuePairs { get { return this; } }

这意味着你可以很容易地具体化:

var query = from x in table.Associations
            ...;

var query = from x in table.KeyValuePairs
            ...;

这不仅有助于让编译器满意 - 它还可以帮助任何尝试阅读代码的人。如果您发现使用其中一个,则可以始终使HashTable仅实现一个IEumerable<>并键入并保留其他属性。