2010-11-19 23 views
2

Ich habe die Vorschläge von this post verfolgt, um zu versuchen, Distinct() in meinem Code zu arbeiten, aber ich habe immer noch Probleme. Hier sind die beiden Objekte Ich arbeite mit:Wie erhalte ich Distinct(), um mit einer Sammlung von benutzerdefinierten Objekten zu arbeiten

public class InvoiceItem : IEqualityComparer<InvoiceItem> 
    { 
     public InvoiceItem(string userName, string invoiceNumber, string invoiceAmount) 
     { 
      this.UserName = userName; 
      this.InvoiceNumber= invoiceNumber; 
      this.InvoiceAmount= invoiceAmount; 
     } 
     public string UserName { get; set; } 
     public string InvoiceNumber { get; set; } 
     public double InvoiceAmount { get; set; } 

     public bool Equals(InvoiceItem left, InvoiceItem right) 
     { 
      if ((object)left.InvoiceNumber == null && (object)right.InvoiceNumber == null) { return true; } 
      if ((object)left.InvoiceNumber == null || (object)right.InvoiceNumber == null) { return false; } 
      return left.InvoiceNumber == right.InvoiceNumber; 
     } 


     public int GetHashCode(InvoiceItem item) 
     { 
      return item.InvoiceNumber == null ? 0 : item.InvoiceNumber.GetHashCode(); 
     } 
    } 


    public class InvoiceItems : List<InvoiceItem>{ } 

Mein Ziel ist es, ein InvoiceItems Objekt zu füllen (wir nennen es aBunchOfInvoiceItems) mit ein paar tausend InvoiceItem Objekte und dann tun:

InvoiceItems distinctItems = aBunchOfInvoiceItems.Distinct(); 

Wenn ich habe diesen Code und führen sie es aus, ich erhalte eine Fehlermeldung, die besagt,

kann nicht implizit Typ ‚System.Collections.Generic.IEnumerable‘ auf ‚InvoiceReader.Form1.Invo konvertieren iceItems ". Eine explizite Konvertierung existiert (fehlt Ihnen ein Cast?)

Ich verstehe nicht, wie das zu beheben ist. Sollte ich einen anderen Ansatz verfolgen? Irgendwelche Vorschläge werden sehr geschätzt.

Antwort

5

Distinct gibt einen generischen IEnumerable<T> zurück. Es tut nicht geben Sie eine InvoiceItems Instanz zurück. Hinter den Kulissen wird ein Proxy-Objekt zurückgegeben, das einen Iterator implementiert, auf den nur bei Bedarf zugegriffen wird (d. H. Wenn Sie darüber iterieren).

Sie können es explizit in eine List<> durch Aufruf .ToList() erzwingen. Sie immer noch müssen Sie es in Ihren benutzerdefinierten Listentyp konvertieren, obwohl. Der einfachste Weg ist wahrscheinlich einen geeigneten Konstruktor zu haben, und fordert, dass:

public class InvoiceItems : List<InvoiceItem> { 
    public InvoiceItems() { } 

    // Copy constructor 
    public InvoiceItems(IEnumerable<InvoiceItems> other) : base(other) { } 
} 

// … 

InvoiceItems distinctItems = new InvoiceItems(aBunchOfInvoiceItems.Distinct()); 
1

Der Fehler hat alles, was Sie mit Ihrer Klasse InvoiceItems, die von List<InvoiceItem> erbt zu tun.

Distinct gibt ein IEnumerable<InvoiceItem>: InvoiceItems ist eine sehr spezifische Art von IEnumerable<InvoiceItem>, aber jede IEnumerable<InvoiceItem> ist nicht unbedingt ein InvoiceItems.

Eine Lösung könnte darin eine implizite Konvertierung Operator zu verwenden, wenn es das ist, was Sie tun wollte: Doh, völlig vergessen Sie nicht zu/von Schnittstellen (dank Saed) umwandeln kann

public class InvoiceItems : List<InvoiceItem> 
{ 
    public InvoiceItems(IEnumerable<InvoiceItem> items) : base(items) { } 
} 

Andere Dinge zu beachten:

  • Vererbung von List<T> ist in der Regel schlecht. Implementieren Sie stattdessen IList<T>.
  • Die Verwendung einer Liste verwirft einen der großen Vorteile von LINQ, was eine faule Auswertung ist. Stellen Sie sicher, dass das Vorablesen der Ergebnisse tatsächlich das ist, was Sie tun möchten.
+0

+1 für "Implementieren IList stattdessen" –

+0

Oooh Ihr Code hat zu viele Kompilierfehler, wenn Sie sie beheben, können Sie nicht impliziten Operator über eine Schnittstelle verwenden ... –

3

Ganz einfach, aBunchOfInvoiceItems.Distinct() gibt ein IEnumerable<InvoiceItem> und Sie versuchen, das zu etwas vergeben, die kein IEnumerable<InvoiceItem> ist.

jedoch die Basisklasse von InvoiceItems einen Konstruktor hat, die eine solche Aufgabe übernimmt, so dass Sie diese verwenden können:

InvoiceItems distinctItems = new InvoiceItems(aBunchOfInvoiceItems.Distinct()); 

Als ob

ist, I:

public class InvoiceItems : List<InvoiceItem> 
{ 
    public InvoiceItems(IEnumerable<InvoiceItem> items) 
    base(items){} 
} 

Dann können Sie verwenden sehen viel Nutzen nicht von List<InvoiceItem> bei der Ableitung so würde ich wahrscheinlich lehnen mehr in Richtung:

List<InvoiceItem> distinctItems = aBunchOfInvoiceItems.Distinct().ToList(); 
4

Konrad Rudolphs Antwort sollte Ihre Probleme bei der Kompilierung angehen. Es gibt hier ein weiteres wichtiges semantisches Korrektheitsproblem, das übersehen wurde: keiner Ihrer Gleichheitslogik wird tatsächlich verwendet werden.

Wenn kein Vergleich zu Distinct bereitgestellt wird, wird EqualityComparer<T>.Default verwendet. Dies wird versuchen, die IEquatable<T> Schnittstelle zu verwenden, und wenn dies fehlt, fällt auf die einfache alte Equals(object other) Methode, die auf object deklariert wird. Für Hashing wird die GetHashCode()-Methode verwendet, die auch unter object deklariert ist. Da die Schnittstelle nicht von Ihrem Typ implementiert wurde und keine der oben genannten Methoden überschrieben wurde, gibt es ein großes Problem: Distinct wird einfach auf Referenzgleichheit zurückgreifen, was nicht das ist, was Sie wollen. Die IEqualityComparer<T> Schnittstelle wird normalerweise verwendet, wenn ein Gleichheitsvergleicher geschrieben werden soll, der vom Typ selbst entkoppelt ist. Auf der anderen Seite, wenn ein Typ in der Lage sein will, eine Instanz von sich selbst mit einer anderen zu vergleichen; Es implementiert normalerweise IEquatable<T>. Ich schlage vor, eine von:

  1. InvoiceItem Get IEquatable<InvoiceItem> statt zu implementieren.
  2. Bewegen Sie die Vergleichslogik zu einem separaten InvoiceItemComparer : IEqualityComparer<InvoiceItem> Typ, und dann invoiceItems.Distinct(new InvoiceItemComparer());
  3. ruft Wenn Sie eine schnelle Hack mit Ihrem vorhandenen Code mögen, können Sie invoiceItems.Distinct(new InvoiceItem());
0

Abgesehen von der benutzerdefinierten Klasse vs IEnumerable Problem tun, dass Bei den anderen Antworten gibt es ein großes Problem mit Ihrem Code. Ihre Klasse implementiert IEqualityComparer anstelle von IEquatable. Wenn Sie Distinct verwenden, müssen die zu filternden Elemente entweder IEquatable selbst implementieren, oder Sie müssen die Überladung verwenden, die einen IEqualityComparer-Parameter verwendet. So wie es jetzt aussieht, wird Ihr Aufruf von Distinct die Elemente nicht nach den von Ihnen bereitgestellten Methoden IEqualityComparer Equals und GetHashCode filtern.

IEqualityComparer sollte von einer anderen Klasse als der verglichen werden, die verglichen wird. Wenn eine Klasse weiß, wie man sich selbst vergleicht, wie Ihre Klasse InvoiceItem, sollte sie IEquatable implementieren.