14

我的班级实施IEnumerable<T>了两次。我怎样才能让 LINQ 工作而无需hashtable每次都进行强制转换?


我编写了自己的协变哈希表实现,它也继承自 .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使用哈希表时,它按预期采用了主要的可枚举:

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”的定义,并且找不到接受“HashTable”类型的第一个参数的扩展方法“Select”(您是否缺少 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>

4

1 回答 1

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<>并键入并保留另一个属性。

于 2013-02-14T15:41:40.947 回答