webentwicklung-frage-antwort-db.com.de

Deutlich nicht mit LINQ zu Objekten arbeiten

class Program
{
    static void Main(string[] args)
    {
        List<Book> books = new List<Book> 
        {
            new Book
            {
                Name="C# in Depth",
                Authors = new List<Author>
                {
                    new Author 
                    {
                        FirstName = "Jon", LastName="Skeet"
                    },
                     new Author 
                    {
                        FirstName = "Jon", LastName="Skeet"
                    },                       
                }
            },
            new Book
            {
                Name="LINQ in Action",
                Authors = new List<Author>
                {
                    new Author 
                    {
                        FirstName = "Fabrice", LastName="Marguerie"
                    },
                     new Author 
                    {
                        FirstName = "Steve", LastName="Eichert"
                    },
                     new Author 
                    {
                        FirstName = "Jim", LastName="Wooley"
                    },
                }
            },
        };


        var temp = books.SelectMany(book => book.Authors).Distinct();
        foreach (var author in temp)
        {
            Console.WriteLine(author.FirstName + " " + author.LastName);
        }

        Console.Read();
    }

}
public class Book
{
    public string Name { get; set; }
    public List<Author> Authors { get; set; }
}
public class Author
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public override bool Equals(object obj)
    {
        return true;
        //if (obj.GetType() != typeof(Author)) return false;
        //else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
    }

}

Dies basiert auf einem Beispiel in "LINQ in Aktion". Listing 4.16.

Dies druckt Jon Skeet zweimal. Warum? Ich habe sogar versucht, die Equals-Methode in der Author-Klasse zu überschreiben. Trotzdem scheint Distinct nicht zu funktionieren. Was vermisse ich?

Edit: Ich habe == und! = Operatorüberladung auch hinzugefügt. Immer noch keine Hilfe.

 public static bool operator ==(Author a, Author b)
    {
        return true;
    }
    public static bool operator !=(Author a, Author b)
    {
        return false;
    }
108
Tanmoy

Bei benutzerdefinierten Objekten ist LINQ Distinct nicht besonders intelligent.

Alles, was es tut, ist einen Blick auf Ihre Liste zu werfen und zu sehen, dass sie zwei verschiedene Objekte hat (es ist egal, dass sie die gleichen Werte für die Mitgliedsfelder haben).

Eine Problemumgehung besteht darin, die IEquatable-Schnittstelle wie folgt zu implementieren: hier .

Wenn Sie Ihre Author-Klasse wie folgt ändern, sollte dies funktionieren.

public class Author : IEquatable<Author>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public bool Equals(Author other)
    {
        if (FirstName == other.FirstName && LastName == other.LastName)
            return true;

        return false;
    }

    public override int GetHashCode()
    {
        int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode();
        int hashLastName = LastName == null ? 0 : LastName.GetHashCode();

        return hashFirstName ^ hashLastName;
    }
}

Probieren Sie es als DotNetFiddle

143
skalb

Die Methode Distinct() überprüft die Referenzgleichheit auf Referenztypen. Das heißt, es wird im wahrsten Sinne des Wortes nach demselben Objekt gesucht, das dupliziert wurde, und nicht nach verschiedenen Objekten, die dieselben Werte enthalten.

Es gibt ein Überladung , das ein IEqualityComparer benötigt, sodass Sie eine andere Logik angeben können, um zu bestimmen, ob ein bestimmtes Objekt einem anderen Objekt entspricht.

Wenn Sie möchten, dass Author sich normalerweise wie ein normales Objekt verhält (dh nur die Referenzgleichheit), aber für die Zwecke der eindeutigen Überprüfung der Gleichheit anhand von Namenswerten, verwenden Sie einen IEqualityComparer . Wenn Author-Objekte immer anhand der Namen verglichen werden sollen, überschreiben Sie GetHashCode und Equals oder implementieren Sie IEquatable .

Die beiden Elemente in der Schnittstelle IEqualityComparer sind Equals und GetHashCode. Ihre Logik zur Bestimmung, ob zwei Author -Objekte gleich sind, scheint zu sein, wenn die Zeichenfolgen für den Vor- und Nachnamen identisch sind.

public class AuthorEquals : IEqualityComparer<Author>
{
    public bool Equals(Author left, Author right)
    {
        if((object)left == null && (object)right == null)
        {
            return true;
        }
        if((object)left == null || (object)right == null)
        {
            return false;
        }
        return left.FirstName == right.FirstName && left.LastName == right.LastName;
    }

    public int GetHashCode(Author author)
    {
        return (author.FirstName + author.LastName).GetHashCode();
    }
}
63
Rex M

Eine andere Lösung, ohne IEquatable, Equals und GetHashCode zu implementieren, besteht darin, die Methode LINQs GroupBy zu verwenden und das erste Element aus der IGrouping auszuwählen.

var temp = books.SelectMany(book => book.Authors)
                .GroupBy (y => y.FirstName + y.LastName )
                .Select (y => y.First ());

foreach (var author in temp){
  Console.WriteLine(author.FirstName + " " + author.LastName);
}
42
Jehof

Es gibt eine weitere Möglichkeit, eindeutige Werte aus der Liste der benutzerdefinierten Datentypen abzurufen:

YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();

Sicherlich wird es unterschiedliche Datensätze geben

24
Ashu_90

Distinct() führt den Standardgleichheitsvergleich für Objekte in der Aufzählung durch. Wenn Sie Equals() und GetHashCode() nicht überschrieben haben, wird die Standardimplementierung für object, die Referenzen vergleicht.

Die einfache Lösung besteht darin, eine korrekte Implementierung von Equals() und GetHashCode() für alle Klassen, die an dem zu vergleichenden Objektdiagramm teilnehmen (dh Buch und Autor).

Die Schnittstelle IEqualityComparer ist eine Annehmlichkeit, mit der Sie Equals() und implementieren können. GetHashCode() in einer separaten Klasse, wenn Sie keinen Zugriff auf die Interna der zu vergleichenden Klassen haben oder wenn Sie eine andere Vergleichsmethode verwenden.

20
AndyM

Sie haben Equals () überschrieben, aber stellen Sie sicher, dass Sie auch GetHashCode () überschreiben.

10
Eric King

Die obigen Antworten sind falsch !!! Unterschiedlich, wie in MSDN angegeben, gibt den Standardäquator zurück, der wie angegeben Die Default-Eigenschaft prüft, ob Typ T die System.IEquatable-Schnittstelle implementiert, und gibt in diesem Fall einen EqualityComparer zurück, der diese Implementierung verwendet. Andernfalls wird ein EqualityComparer zurückgegeben, der die Überschreibungen von Object.Equals und Object.GetHashCode verwendet, die von T bereitgestellt werden

Was bedeutet, solange Sie Equals überschreiben, geht es Ihnen gut.

Der Grund, warum Ihr Code nicht funktioniert, ist, dass Sie Vorname == Nachname überprüfen.

siehe https://msdn.Microsoft.com/library/bb348436 (v = vs.100) .aspx und https://msdn.Microsoft.com/en-us/library) /ms224763(v=vs.100).aspx

7
Alex