Frage

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 Action". Listing 4.16.

Dieser druckt Jon Skeet zweimal. Warum? Ich habe sogar Equals-Methode in der Klasse Autor versucht, zu überschreiben. Noch Distinct scheint nicht zu arbeiten. Was bin ich?

Edit: Ich habe hinzugefügt == und! = Operator zu überlasten. Noch immer keine Hilfe.

 public static bool operator ==(Author a, Author b)
    {
        return true;
    }
    public static bool operator !=(Author a, Author b)
    {
        return false;
    }
War es hilfreich?

Lösung

LINQ Distinct ist nicht so schlau, wenn es um benutzerdefinierte Objekte kommt.

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

Eine Abhilfe ist, die IEquatable Schnittstelle zu implementieren, wie gezeigt hier .

Wenn Sie Ihren Autor Klasse wie ändern, so dass es funktionieren soll.

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

Versuchen Sie es als DotNetFiddle

Andere Tipps

Die Distinct() Methode überprüft Referenz Gleichheit für Referenztypen. Dies bedeutet, es wird buchstäblich das gleiche Objekt suchen dupliziert, nicht verschiedene Objekte, die die gleichen Werte enthalten.

Es gibt eine Überlastung , die eine IEqualityComparer , so dass Sie andere Logik zu bestimmen, ob ein bestimmtes Objekt gleich einem anderen angeben.

Wenn Sie möchten, Autor normalerweise wie ein normales Objekt verhalten (das heißt nur Referenz Gleichheit), aber für die Zwecke der Distinct Prüfung Gleichstellung von Namenswerte, verwenden Sie einen IEqualityComparer . Wenn Sie wollen immer Autor im Vergleich zu den Namenswerte werden Objekte basierend, dann überschreiben GetHashCode und Equals oder implementieren IEquatable .

Die beiden Elemente auf der IEqualityComparer Schnittstelle sind Equals und GetHashCode. Ihre Logik zu bestimmen, ob zwei Author Objekte sind gleich zu sein scheint, wenn der erste und Nachname Strings gleich 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();
    }
}

Eine andere Lösung ohne Umsetzung IEquatable, Equals und GetHashCode ist die LINQs GroupBy Methode zu verwenden, und das erste Element aus der IGrouping wä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);
}

Es gibt eine weitere Möglichkeit, verschiedene Werte aus der Liste des benutzerdefinierten Datentypen zu erhalten:

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

Sicherlich wird es unterschiedliche Menge von Daten geben

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top