web-dev-qa-db-de.com

Ein praktisches Beispiel, bei dem Tuple in .NET 4.0 verwendet werden kann?

Ich habe das in .Net 4 eingeführte Tupel gesehen, aber ich kann mir nicht vorstellen, wo es verwendet werden kann. Wir können immer eine benutzerdefinierte Klasse oder eine Struktur erstellen.

94
Amitabh

Das ist der Punkt - es ist bequemer nicht , eine benutzerdefinierte Klasse oder Struktur zu erstellen. Es ist eine Verbesserung wie Action oder Func... Sie können diese Typen selbst erstellen, aber es ist praktisch, dass sie im Framework vorhanden sind.

82
tanascius

Mit Tupeln können Sie problemlos ein zweidimensionales Wörterbuch (oder n-dimensionale Wörter) implementieren. Beispielsweise können Sie ein solches Wörterbuch verwenden, um eine Währungsumrechnung zu implementieren:

var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);

decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58
73
MarioVW

Es gibt ein ausgezeichneter Artikel in der MSDN-Zeitschrift, in dem über Bauchschmerzen und Überlegungen zum Design gesprochen wird, die beim Hinzufügen von Tuple zum BCL erforderlich waren. Besonders interessant ist die Wahl zwischen einem Werttyp und einem Referenztyp.

Wie der Artikel deutlich macht, waren die treibenden Kräfte hinter Tuple so viele Gruppen innerhalb von Microsoft, die einen Nutzen dafür hatten, das F # -Team vorne. Obwohl nicht erwähnt, rechne ich damit, dass das neue "dynamische" Schlüsselwort in C # (und VB.NET) auch etwas damit zu tun hat.

Ansonsten ist es nicht besonders überlegen, ein eigenes Poco zu erstellen, zumindest können Sie den Mitgliedern einen besseren Namen geben.


UPDATE: wegen einer großen Überarbeitung in C # Version 7 jetzt viel mehr Syntaxliebe. Vorläufige Ankündigung in diesem Blogpost .

25
Hans Passant

Ich habe ein Tupel verwendet, um Problem 11 von Project Euler zu lösen: 

class Grid
{
    public static int[,] Cells = { { 08, 02, 22, // whole grid omitted

    public static IEnumerable<Tuple<int, int, int, int>> ToList()
    {
        // code converts grid to enumeration every possible set of 4 per rules
        // code omitted
    }
}

Jetzt kann ich das ganze Problem lösen mit:

class Program
{
    static void Main(string[] args)
    {
        int product = Grid.ToList().Max(t => t.Item1 * t.Item2 * t.Item3 * t.Item4);
        Console.WriteLine("Maximum product is {0}", product);
    }
}

Ich habe könnte einen benutzerdefinierten Typ dafür verwendet haben, hätte aber genau wie Tuple ausgesehen.

23
Craig Stuntz

Hier ein kleines Beispiel: Angenommen, Sie haben eine Methode, die das Handle und die E-Mail-Adresse eines Benutzers anhand einer Benutzer-ID nachschlagen muss. Sie können jederzeit eine benutzerdefinierte Klasse erstellen, die diese Daten enthält, oder einen ref/out-Parameter für diese Daten verwenden. Sie können auch nur einen Tuple und eine Nice-Methodensignatur zurückgeben, ohne ein neues POCO erstellen zu müssen. 

public static void Main(string[] args)
{
    int userId = 0;
    Tuple<string, string> userData = GetUserData(userId);
}

public static Tuple<string, string> GetUserData(int userId)
{
    return new Tuple<string, string>("Hello", "World");
}
22
Tejs

Die Tuple-Syntax von C # ist lächerlich sperrig, daher ist es schmerzhaft, Tupel zu erklären. Und es gibt keine Musterübereinstimmung, daher sind sie auch schmerzhaft in der Anwendung.

Gelegentlich möchten Sie jedoch nur eine Ad-hoc-Gruppierung von Objekten, ohne dafür eine Klasse zu erstellen. Angenommen, ich wollte eine Liste zusammenfassen, aber ich wollte zwei Werte anstelle von einem:

// sum and sum of squares at the same time
var x =
    Enumerable.Range(1, 100)
    .Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));

Anstatt eine Sammlung von Werten in einem einzigen Ergebnis zu kombinieren, erweitern wir ein einzelnes Ergebnis in eine Sammlung von Werten. Der einfachste Weg, diese Funktion zu schreiben, ist:

static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
    Tuple<T, State> res;
    while ((res = f(seed)) != null)
    {
        yield return res.Item1;
        seed = res.Item2;
    }
}

f konvertiert einen Status in einen Tuple. Wir geben den ersten Wert aus dem Tuple zurück und setzen unseren neuen Zustand auf den zweiten Wert. Dies ermöglicht es uns, den Zustand während der gesamten Berechnung beizubehalten.

Sie verwenden es als solches:

// return 0, 2, 3, 6, 8
var evens =
    Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
    .ToList();

// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
    Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
    .Take(10).ToList();

evens ist ziemlich einfach, aber fibs ist etwas schlauer. Sein state ist eigentlich ein Tuple, der fib (n-2) bzw. fib (n-1) hält.

16
Juliet

Ich mag den Missbrauch nicht, da sie Code erzeugen, der sich nicht von selbst erklärt, aber sie sind fantastisch für die Implementierung von On-the-Fly-Verbundschlüsseln, da sie IStructuralEquatable und IStructuralComparable implementieren (sowohl für die Suche als auch für die Bestellung Zwecke). 

Und sie kombinieren alle Hashcodes ihrer Elemente intern. Hier ist zum Beispiel Tuples GetHashCode (von ILSpy übernommen):

    int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
    {
        return Tuple.CombineHashCodes(comparer.GetHashCode(this.m_Item1), comparer.GetHashCode(this.m_Item2), comparer.GetHashCode(this.m_Item3));
    }
7
Notoriousxl

Tupel eignen sich hervorragend, um mehrere asynchrone IO - Operationen gleichzeitig auszuführen und alle Werte zusammen zurückzugeben. Hier sind die Beispiele, wie man es mit und ohne Tupel macht. Tupel können Ihren Code tatsächlich klarer machen!

Ohne (böse Verschachtelung!):

Task.Factory.StartNew(() => data.RetrieveServerNames())
    .ContinueWith(antecedent1 =>
        {
            if (!antecedent1.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent1.Result);
                Task.Factory.StartNew(() => data.RetrieveLogNames())
                    .ContinueWith(antecedent2 =>
                        {
                            if (antecedent2.IsFaulted)
                            {
                                LogNames = KeepExistingFilter(LogNames, antecedent2.Result);
                                Task.Factory.StartNew(() => data.RetrieveEntryTypes())
                                    .ContinueWith(antecedent3 =>
                                        {
                                            if (!antecedent3.IsFaulted)
                                            {
                                                EntryTypes = KeepExistingFilter(EntryTypes, antecedent3.Result);
                                            }
                                        });
                            }
                        });
            }
        });

Mit Tuple

Task.Factory.StartNew(() =>
    {
        List<string> serverNames = data.RetrieveServerNames();
        List<string> logNames = data.RetrieveLogNames();
        List<string> entryTypes = data.RetrieveEntryTypes();
        return Tuple.Create(serverNames, logNames, entryTypes);
    }).ContinueWith(antecedent =>
        {
            if (!antecedent.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent.Result.Item1);
                LogNames = KeepExistingFilter(LogNames, antecedent.Result.Item2);
                EntryTypes = KeepExistingFilter(EntryTypes, antecedent.Result.Item3);
            }
        });

Wenn Sie eine anonyme Funktion mit einem implizierten Typ jedenfalls verwendet haben, machen Sie den Code durch Verwendung des Tupels nicht weniger klar. Einen Tupel von einer Methode abstimmen? Verwenden Sie sparsam, wenn Code-Klarheit ausschlaggebend ist, meiner bescheidenen Meinung nach. Ich weiß, dass funktionale Programmierung in C # schwer zu widerstehen ist, aber wir müssen all diese alten klobigen "objektorientierten" C # -Programmierer berücksichtigen.

6
AndyClaw

Ich neige dazu, Tuple für die meisten Szenarien zu vermeiden, da dies die Lesbarkeit beeinträchtigt. Tuple ist jedoch nützlich, wenn Sie nicht zusammenhängende Daten gruppieren müssen.

Angenommen, Sie haben eine Liste der Autos und der Städte, in denen sie gekauft wurden:

Mercedes, Seattle
Mustang, Denver
Mercedes, Seattle
Porsche, Seattle
Tesla, Seattle
Mercedes, Seattle

Sie möchten die Anzahl für jedes Auto pro Stadt zusammenfassen:

Mercedes, Seattle [3]
Mustang, Denver [1]
Porsche, Seattle [1]
Tesla, Seattle [1]

Dazu erstellen Sie eine Dictionary. Sie haben einige Möglichkeiten:

  1. Erstellen Sie einen Dictionary<string, Dictionary<string, int>>.
  2. Erstellen Sie einen Dictionary<CarAndCity, int>.
  3. Erstellen Sie einen Dictionary<Tuple<string, string>, int>.

Die Lesbarkeit geht mit der ersten Option verloren. Sie müssen viel mehr Code schreiben.

Die zweite Option funktioniert und ist knapp, aber Auto und Stadt sind nicht wirklich verwandt und gehören wahrscheinlich nicht zu einer Klasse.

Die dritte Option ist knapp und sauber. Es ist eine gute Verwendung von Tuple.

5
John Kurlak

Tupel werden häufig in funktionalen Sprachen verwendet, die mehr mit ihnen tun können. Jetzt ist F # eine "offizielle" .net-Sprache, mit der Sie möglicherweise von C # aus mit C arbeiten und sie zwischen zweisprachigem Code übergeben können.

5
Mant101

Ein paar Beispiele aus meinem Kopf:

  • Eine X- und Y-Position (und Z, wenn Sie möchten)
  • eine Breite und Höhe
  • Alles, was über die Zeit gemessen wird

Beispielsweise möchten Sie System.Drawing nicht in eine Webanwendung aufnehmen, um nur Point/PointF und Size/SizeF zu verwenden.

4
James Westgate

Sie sollten mit der Verwendung von Tuple sehr vorsichtig sein und dies wahrscheinlich zweimal überlegen. Aus meiner früheren Erfahrung habe ich herausgefunden, dass die Verwendung von Tuple es in Zukunft sehr schwierig macht, Code zu lesen und zu unterstützen. Vor einiger Zeit musste ich Code korrigieren, in dem Tupel fast überall verwendet wurden. Anstatt über geeignete Objektmodelle nachzudenken, benutzten sie lediglich Tupel. Das war ein Albtraum ... manchmal wollte ich den Kerl töten, der den Code geschrieben hat ...

Ich möchte nicht sagen, dass Sie Tuple nicht verwenden sollten und es ist böse oder etwas. Ich bin hundertprozentig sicher, dass es einige Aufgaben gibt, bei denen Tuple der beste Kandidat ist, der verwendet werden soll brauchen?

2
Mr. Pumpkin

Ich habe gerade die Lösung eines meiner Probleme in Tuple gefunden. Es ist wie das Deklarieren einer Klasse im Gültigkeitsbereich einer Methode, jedoch mit einer langsamen Deklaration der Feldnamen. Sie arbeiten mit Sammlungen von Tupeln und ihren einzelnen Instanzen und erstellen dann eine Sammlung anonymer Typen mit den erforderlichen Feldnamen, die auf Ihrem Tuple basieren. Dies verhindert, dass Sie die neue Klasse für diesen Zweck erstellen.

Die Aufgabe besteht darin, eine JSON-Antwort aus LINQ ohne zusätzliche Klassen zu schreiben:

 //I select some roles from my ORM my with subrequest and save results to Tuple list
 var rolesWithUsers = (from role in roles
                       select new Tuple<string, int, int>(
                         role.RoleName, 
                         role.RoleId, 
                         usersInRoles.Where(ur => ur.RoleId == role.RoleId).Count()
                      ));

 //Then I add some new element required element to this collection
 var tempResult = rolesWithUsers.ToList();
 tempResult.Add(new Tuple<string, int, int>(
                        "Empty", 
                         -1,
                         emptyRoleUsers.Count()
                      ));

 //And create a new anonimous class collection, based on my Tuple list
 tempResult.Select(item => new
            {
                GroupName = item.Item1,
                GroupId = item.Item2,
                Count = item.Item3
            });


 //And return it in JSON
 return new JavaScriptSerializer().Serialize(rolesWithUsers);

Natürlich könnten wir dies mit der Deklaration einer neuen Klasse für meine Gruppen tun, aber die Idee, eine solche anonyme Sammlung zu erstellen, ohne neue Klassen zu deklarieren.

1
Alex

In meinem Fall musste ich ein Tuple verwenden, als ich herausfand, dass wir out-Parameter nicht in einer asynchronen Methode verwenden können. Lesen Sie darüber hier . Ich brauchte auch einen anderen Rückgabetyp. Also habe ich stattdessen einen Tupel als Rückgabetyp verwendet und die Methode als asynchron markiert.

Beispielcode unten.

...
...
// calling code.
var userDetails = await GetUserDetails(userId);
Console.WriteLine("Username : {0}", userDetails.Item1);
Console.WriteLine("User Region Id : {0}", userDetails.Item2);
...
...

private async Tuple<string,int> GetUserDetails(int userId)
{
    return new Tuple<string,int>("Amogh",105);
    // Note that I can also use the existing helper method (Tuple.Create).
}

Lesen Sie mehr über Tuple hier . Hoffe das hilft.

1
Amogh Natu

Die beste Verwendung für Tupel, die ich gefunden habe, ist, wenn Sie mehr als einen Objekttyp von einer Methode zurückgeben müssen. Sie wissen, welche Objekttypen und welche Anzahl sie sein werden, und es ist keine lange Liste.

Andere einfache Alternativen wären die Verwendung eines Out-Parameters

private string MyMethod(out object)

oder ein Wörterbuch machen

Dictionary<objectType1, objectType2>

Die Verwendung eines Tupels erspart jedoch entweder das Erstellen des "out" -Objekts oder das Nachschlagen des Eintrags im Wörterbuch.

1
sidjames

Nun, ich habe drei Möglichkeiten ausprobiert, um dasselbe Problem in C # 7 zu lösen, und ich habe einen Anwendungsfall für Tuples gefunden.

Das Arbeiten mit dynamischen Daten in Webprojekten kann beim Mapping u.

Ich mag die Art und Weise, wie der Tupel gerade automatisch auf item1, item2, itemN abgebildet wird, was für mich robuster erscheint als Array-Indizes zu verwenden, bei denen Sie von einem nicht im Index befindlichen Element erfasst werden könnten, oder den anonymen Typ, bei dem Sie einen Eigenschaftennamen falsch schreiben können.

Es scheint, als wäre ein DTO nur mit einem Tuple kostenlos erstellt worden, und ich kann mit itemN auf alle Eigenschaften zugreifen, was sich eher wie statische Typisierung anfühlt, ohne dass dafür ein separates DTO erstellt werden muss.

using System;

namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var Tuple = GetTuple();
            Console.WriteLine(Tuple.Item1);
            Console.WriteLine(Tuple.Item2);
            Console.WriteLine(Tuple.Item3);
            Console.WriteLine(Tuple);

            Console.WriteLine("---");

            var dyn = GetDynamic();
            Console.WriteLine(dyn.First);
            Console.WriteLine(dyn.Last);
            Console.WriteLine(dyn.Age);
            Console.WriteLine(dyn);

            Console.WriteLine("---");

            var arr = GetArray();
            Console.WriteLine(arr[0]);
            Console.WriteLine(arr[1]);
            Console.WriteLine(arr[2]);
            Console.WriteLine(arr);

            Console.Read();

            (string, string, int) GetTuple()
            {
                return ("John", "Connor", 1);
            }

            dynamic GetDynamic()
            {
                return new { First = "John", Last = "Connor", Age = 1 };
            }

            dynamic[] GetArray()
            {
                return new dynamic[] { "John", "Connor", 1 };
            }
        }
    }
}
0
ozzy432836

Nur für das Prototyping - Tupel sind bedeutungslos. Es ist praktisch, sie zu verwenden, aber es ist nur eine Abkürzung! Für Prototypen - gut. Stellen Sie sicher, dass Sie diesen Code später löschen.

Es ist leicht zu schreiben, schwer zu lesen. Es hat keine sichtbaren Vorteile gegenüber Klassen, inneren Klassen, anonymen Klassen usw. 

0
bunny1985

Ändern Sie die Form von Objekten, wenn Sie sie über das Kabel senden oder an eine andere Anwendungsebene weiterleiten müssen, und mehrere Objekte werden zu einer einzigen verschmolzen:

Beispiel:

var customerDetails = new Tuple<Customer, List<Address>>(mainCustomer, new List<Address> {mainCustomerAddress}).ToCustomerDetails();

Erweiterungsmethode:

public static CustomerDetails ToCustomerDetails(this Tuple<Website.Customer, List<Website.Address>> customerAndAddress)
    {
        var mainAddress = customerAndAddress.Item2 != null ? customerAndAddress.Item2.SingleOrDefault(o => o.Type == "Main") : null;
        var customerDetails = new CustomerDetails
        {
            FirstName = customerAndAddress.Item1.Name,
            LastName = customerAndAddress.Item1.Surname,
            Title = customerAndAddress.Item1.Title,
            Dob = customerAndAddress.Item1.Dob,
            EmailAddress = customerAndAddress.Item1.Email,
            Gender = customerAndAddress.Item1.Gender,
            PrimaryPhoneNo = string.Format("{0}", customerAndAddress.Item1.Phone)
        };

        if (mainAddress != null)
        {
            customerDetails.AddressLine1 =
                !string.IsNullOrWhiteSpace(mainAddress.HouseName)
                    ? mainAddress.HouseName
                    : mainAddress.HouseNumber;
            customerDetails.AddressLine2 =
                !string.IsNullOrWhiteSpace(mainAddress.Street)
                    ? mainAddress.Street
                    : null;
            customerDetails.AddressLine3 =
                !string.IsNullOrWhiteSpace(mainAddress.Town) ? mainAddress.Town : null;
            customerDetails.AddressLine4 =
                !string.IsNullOrWhiteSpace(mainAddress.County)
                    ? mainAddress.County
                    : null;
            customerDetails.PostCode = mainAddress.PostCode;
        }
...
        return customerDetails;
    }
0

Ein out-Parameter ist großartig, wenn nur einige wenige Werte zurückgegeben werden müssen, Wenn Sie jedoch auf 4, 5, 6 oder mehr Werte stoßen, die zurückgegeben werden müssen, kann dies Unhandlich werden. Eine andere Möglichkeit, mehrere Werte zurückzugeben, besteht darin, eine benutzerdefinierte Klasse/Struktur zu erstellen und zurückzugeben oder einen Tuple zu verwenden, um alle Werte zu packen, die von einer Methode zurückgegeben werden müssen.

Die erste Option, bei der eine Klasse/Struktur zur Rückgabe der Werte verwendet wird, ist unkompliziert. Erstellen Sie einfach den Typ (in diesem Beispiel eine Struktur) wie folgt:

public struct Dimensions
{
public int Height;
public int Width;
public int Depth;
}

Die zweite Möglichkeit, ein Tuple zu verwenden, ist eine noch elegantere Lösung als ein benutzerdefiniertes Objekt. Ein Tuple kann erstellt werden, um eine beliebige Anzahl von Werten unterschiedlichen Typs aufzunehmen ..__ Darüber hinaus sind die Daten, die Sie im Tuple speichern, unveränderlich. Wenn Sie die Daten über den Konstruktor oder die statische Create-Methode zu dem Tuple hinzugefügt haben, können diese Daten nicht mehr geändert werden. Sie können bis zu acht separate Werte akzeptieren. Wenn Sie Mehr als acht Werte zurückgeben müssen, müssen Sie die spezielle Tuple-Klasse verwenden: Tuple-Klasse Wenn Sie einen Tuple mit mehr als acht Werten erstellen, können Sie die statische Create Methode - Sie müssen stattdessen den Konstruktor der Klasse verwenden. So würden Sie Ein Tuple mit 10 ganzzahligen Werten erstellen:

var values = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>> (
1, 2, 3, 4, 5, 6, 7, new Tuple<int, int, int> (8, 9, 10));

Natürlich können Sie am Ende jedes eingebetteten Tupels weitere Tupel hinzufügen, Sie können einen beliebigen Tupel erstellen, den Sie benötigen.