2013-02-14 52 views
14

我的类实现了两次IEnumerable<T>我怎样才能得到LINQ工作,而不是每次铸造hashtable当执行IEnumerable时,LINQ会感到困惑<T>两次


我写我自己的协变散列表实施方案也从.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'的定义,也没有找到接受类型'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

您是否尝试过手动指定'Select'的[generic arguments](http://msdn.microsoft.com/zh-CN/library/bb548891.aspx)?听起来像是无法推断它们。特别是,你需要手动指定'TSource',但是一旦你做了一个,你就需要做我想的。不幸的是,这会阻止你返回匿名类型而没有一些烦人的解决方法。 – 2013-02-14 15:39:29

+0

您可以包含'HashTable '的继承/接口的其余部分吗? – 2013-02-14 15:40:01

+1

那么它告诉你该怎么做,至少在第一个错误。您必须明确说明'x'是什么类型:from IAssociation x in hashtable select x; – Stonehead 2013-02-14 15:41:04

回答

24

当涉及到使用表达式作为方法调用的参数时,编译器没有“主要”和“次要”接口实现的概念是很重要的。就类型而言,您的类型同样适用于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<>并键入并保留其他属性。

+0

我很喜欢这个答案。 – 2013-02-14 15:43:14

+0

是否没有其他方式通过直接实现它们来满足您的编译器? – antonijn 2013-02-14 15:49:35

+1

@Antonijn:是的,您可以通过明确指定类型参数来满足编译器的要求...... – 2013-02-14 15:57:37