2013-02-14 3 views
14

Meine Klasse implementiert IEnumerable<T> zweimal. Wie kann ich LINQ zu arbeiten, ohne hashtable jedes Mal zu werfen?LINQ verwirrt bei der Implementierung von IEnumerable <T> zweimal


Ich schrieb meine eigene covariant hashtable Implementierung, die auch von .NET ist erbt IDictionary<TKey, TValue>. Letztendlich implementiert es IEnumerable<T> zweimal mit verschiedenen Typen für T. Ich habe die primäre aufzählbare Schnittstelle implizit und die andere explizit implementiert. So etwas wie dieser (Pseudo-Code):

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> 
} 

Jetzt mag ich es die gleiche Sache in LINQ zu tun, aber es schleudert:

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(); 
} 

Wenn ich die Hash-Tabelle foreach, es as expected die primären enumerable nimmt Compiler-Fehler bei mir, weil er nicht weiß, welche Schnittstelle für die Erweiterungsmethoden zur Auswahl:

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

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

Fehler 1: Eine Implementierung des Abfragemusters für den Quelltyp 'HashTable' konnte nicht gefunden werden. 'Auswählen' nicht gefunden. Erwägen Sie explizit, den Typ der Bereichsvariablen 'x' anzugeben.

Fehler 2: 'HashTable' enthält keine Definition für 'Select' und keine Erweiterungsmethode 'Select', die ein erstes Argument des Typs 'HashTable' akzeptiert (fehlt eine using-Direktive oder eine Assembly-Referenz?)

Vielleicht gibt es einen Interface- oder Vererbungstrick, von dem ich nichts weiß?


Für diejenigen, die hier gestellt wird, lautet der vollständige Baum von Schnittstellen:

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> 

Jetzt können Sie sehen, warum ich nicht KeyValuePair<TKey, TValue> und IAssociation<TKey, TValue> das gleiche machen könnte.

+4

Haben Sie versucht, die [generischen Argumente] ('http://msdn.microsoft.com/de-de/library/bb548891.aspx) von' Select' manuell einzugeben? Hört sich an, als hätte es Schwierigkeiten, sie zu deuten. Insbesondere müssen Sie 'TSource' manuell angeben, aber sobald Sie einen tun, müssen Sie beides tun, denke ich. Leider verhindert dies, dass Sie anonyme Typen ohne lästige Problemumgehungen zurückgeben können. –

+0

Können Sie den Rest der Vererbung/Schnittstellen für 'HashTable '? –

+1

Nun, es sagt Ihnen, was zu tun ist, zumindest in Bezug auf den ersten Fehler. Sie müssen explizit sagen, welcher Typ 'x' ist: von IAssociation x in Hashtabelle wählen Sie x; – Stonehead

Antwort

24

Es ist wichtig zu verstehen, dass der Compiler kein Konzept der "primären" und "sekundären" Schnittstellenimplementierungen hat, wenn es darum geht, einen Ausdruck als Argument für einen Methodenaufruf zu verwenden. Ihr Typ implementiert sowohl IEnumerable<IAssociation<...>> als auch IEnumerable<KeyValuePair<...>> genauso gut, was die Konvertierung zu diesen Typen betrifft. Deshalb benötigt der Compiler mehr Informationen.

Der einfachste Ansatz (IMO) lassen sich auch zwei neue Eigenschaften einzuführen:

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

Das bedeutet, dass Sie wirklich leicht spezifisch sein können:

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

oder

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

Nicht Dies hilft nur, den Compiler glücklich zu machen - es wird auch jedem helfen, der versucht, den Code zu lesen.Wenn Sie eine dieser viel mehr als die andere finden, können Sie immer HashTable nur eine einzige IEumerable<> implementieren und die andere Eigenschaft eingeben und behalten.

+0

Ich mag diese Antwort sehr. –

+0

Gibt es keine andere Möglichkeit, Ihren Compiler zu erfüllen, indem Sie beide direkt implementieren. – antonijn

+1

@Antonijn: Ja, Sie können den Compiler erfüllen, indem Sie die Argumente des Typs explizit angeben ... –

Verwandte Themen