web-dev-qa-db-fra.com

Exemple pratique où Tuple peut être utilisé dans .Net 4.0?

J'ai vu le tuple introduit dans .Net 4 mais je ne peux pas imaginer où il peut être utilisé. Nous pouvons toujours créer une classe ou une structure personnalisée.

94
Amitabh

C'est le point - il est plus pratique pas de faire une classe personnalisée ou une structure tout le temps. C'est une amélioration comme Action ou Func... vous pouvez créer ces types vous-même, mais il est pratique qu'ils existent dans le cadre.

82
tanascius

Avec des n-uplets, vous pouvez facilement implémenter un dictionnaire à deux dimensions (ou n-dimensionnel) Par exemple, vous pouvez utiliser un tel dictionnaire pour implémenter un mappage d'échange de devises:

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

Il existe un excellent article dans le magazine MSDN qui parle des considérations de conception et de douleur au ventre qui ont conduit à l’ajout de Tuple à la BCL. Choisir entre un type de valeur et un type de référence est particulièrement intéressant.

Comme le précise l'article, la force motrice de Tuple était son utilisation par de nombreux groupes de Microsoft, l'équipe F # à l'avant-garde. Bien que cela ne soit pas mentionné, j’estime que le nouveau mot clé "dynamique" en C # (et VB.NET) y est pour quelque chose, les n-uplets sont très courants dans les langages dynamiques.

Autrement, ce n'est pas particulièrement supérieur à la création de votre propre poco, au moins vous pouvez donner aux membres un meilleur nom.


UPDATE: en raison d'une révision importante dans la version 7 de C #, la syntaxe est de plus en plus populaire. Annonce préliminaire dans cet article de blog .

25
Hans Passant

J'ai utilisé un tuple pour résoudre Problème 11 de Project Euler

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

Maintenant, je peux résoudre tout le problème avec:

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

Je pourrais ai utilisé un type personnalisé pour cela, mais il aurait semblé exactement comme Tuple.

23
Craig Stuntz

Voici un petit exemple - supposons que vous ayez une méthode qui nécessite de rechercher le pseudo et l'adresse e-mail d'un utilisateur, à partir d'un identifiant d'utilisateur. Vous pouvez toujours créer une classe personnalisée contenant ces données, ou utiliser un paramètre ref/out pour ces données, ou simplement renvoyer un tuple et avoir une signature de méthode Nice sans avoir à créer un nouveau POCO. 

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

La syntaxe des tuples de C # est ridiculement volumineuse, donc les tuples sont difficiles à déclarer. Et il n'y a pas de correspondance de motif, ils sont donc également douloureux à utiliser.

Mais parfois, vous voulez juste un regroupement d'objets ad-hoc sans créer de classe pour cela. Par exemple, disons que je voulais agréger une liste, mais que je voulais deux valeurs au lieu d'une:

// 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));

Au lieu de combiner une collection de valeurs dans un seul résultat, développons un seul résultat en une collection de valeurs. Le moyen le plus simple d'écrire cette fonction est:

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 convertit un état en tuple. Nous renvoyons la première valeur du tuple et définissons notre nouvel état sur la deuxième valeur. Cela nous permet de conserver l’état tout au long du calcul.

Vous l'utilisez comme tel:

// 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 est assez simple, mais fibs est un peu plus intelligent. Sa state est en fait un tuple qui contient respectivement fib (n-2) et fib (n-1).

16
Juliet

Je n’aime pas les abus, car ils produisent du code qui ne s’explique pas, mais ils sont géniaux pour implémenter des clés composées à la volée, car ils implémentent IStructuralEquatable et IStructuralComparable fins). 

Et ils combinent les codes de hachage de tous leurs éléments, en interne; Par exemple, voici le GetHashCode de Tuple (tiré de ILSpy):

    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

Les tuples sont parfaits pour effectuer plusieurs opérations asynchrones IO à la fois et renvoyer toutes les valeurs ensemble. Voici les exemples de le faire avec et sans Tuple. Les tuples peuvent réellement rendre votre code plus clair!

Sans (nidification désagréable!):

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

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

Si vous utilisiez une fonction anonyme avec un type implicite de toute façon alors vous ne rendez pas le code moins clair en utilisant le tuple. Réaccorder un tuple d'une méthode? Utilisez avec parcimonie lorsque la clarté du code est la clé, à mon humble avis. Je sais qu'il est difficile de résister à la programmation fonctionnelle en C #, mais nous devons prendre en compte tous ces vieux programmeurs C # maladroits "orientés objet".

6
AndyClaw

J'ai tendance à éviter Tuple pour la plupart des scénarios car cela nuit à la lisibilité. Cependant, Tuple est utile lorsque vous devez regrouper des données non liées.

Par exemple, supposons que vous ayez une liste des voitures et des villes dans lesquelles elles ont été achetées:

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

Vous voulez agréger les chiffres pour chaque voiture par ville:

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

Pour ce faire, vous créez une Dictionary. Vous avez quelques options:

  1. Créez un Dictionary<string, Dictionary<string, int>>.
  2. Créez un Dictionary<CarAndCity, int>.
  3. Créez un Dictionary<Tuple<string, string>, int>.

La lisibilité est perdue avec la première option. Il vous faudra écrire beaucoup plus de code.

La deuxième option fonctionne et est succincte, mais voiture et ville ne sont pas vraiment liés et n'appartiennent probablement pas à une classe.

La troisième option est succincte et propre. C'est un bon usage de Tuple.

5
John Kurlak

Les tuples sont très utilisés dans les langages fonctionnels qui peuvent faire plus de choses avec eux, maintenant F # est un langage .net 'officiel' que vous voudrez peut-être interagir à partir de C # et les passer entre du code écrit en deux langues.

5
Mant101

Quelques exemples en tête:

  • Un emplacement X et Y (et Z si vous aimez)
  • a largeur et hauteur
  • Tout ce qui est mesuré au fil du temps

Par exemple, vous ne voudriez pas inclure System.Drawing dans une application Web uniquement pour utiliser Point/PointF et Size/SizeF.

4
James Westgate

Vous devez faire très attention à utiliser Tuple et probablement y réfléchir à deux fois avant de le faire. D'après mon expérience précédente, j'ai découvert que l'utilisation de Tuple rend le code très difficile à lire et à prendre en charge à l'avenir. Il y a quelque temps, j'ai dû corriger un code où les n-uplets étaient utilisés presque partout. Au lieu de penser à des modèles d'objet appropriés, ils ont simplement utilisé des n-uplets. C'était un cauchemar ... parfois je voulais tuer le gars qui a écrit le code ...

Vous ne voulez pas dire que vous ne devriez pas utiliser Tuple et que c'est mal ou quelque chose comme ça et je suis à cent pour cent sûr qu'il existe certaines tâches pour lesquelles Tuple est le meilleur candidat à utiliser, mais vous devriez probablement y repenser, vraiment besoin de ça?

2
Mr. Pumpkin

Je viens de trouver la solution d’un de mes problèmes dans Tuple. C'est comme déclarer une classe dans la portée d'une méthode, mais avec une déclaration lente de ses noms de champs. Vous travaillez avec des collections de tuples, ses instances uniques, puis vous créez une collection de type anonyme avec les noms de champs requis, en vous basant sur votre tuple. Cela vous évite de créer la nouvelle classe à cette fin.

La tâche consiste à écrire une réponse JSON à partir de LINQ sans classes supplémentaires:

 //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);

Nous pourrions bien faire cela en déclarant une nouvelle classe pour mes groupes, mais l’idée de créer de telles collections anonymes sans déclarer de nouvelles classes.

1
Alex

Eh bien dans mon cas, j'ai dû utiliser un tuple quand j'ai découvert que nous ne pouvions pas utiliser le paramètre out dans une méthode asynchrone. Lire à ce sujet ici . J'avais aussi besoin d'un type de retour différent. J'ai donc utilisé un tuple à la place comme type de retour et marqué la méthode comme asynchrone.

Exemple de code ci-dessous.

...
...
// 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).
}

En savoir plus sur Tuple ici . J'espère que cela t'aides.

1
Amogh Natu

La meilleure utilisation pour Tuples que j'ai trouvée est lorsqu'il est nécessaire de renvoyer plus d'un type d'objet à partir d'une méthode, vous savez quels types d'objet et nombre ils seront, et la liste n'est pas longue.

D'autres solutions simples consisteraient à utiliser un paramètre "out"

private string MyMethod(out object)

ou faire un dictionnaire

Dictionary<objectType1, objectType2>

L'utilisation d'un tuple permet toutefois d'éviter de créer l'objet "out" ou de devoir essentiellement rechercher l'entrée dans le dictionnaire;

1
sidjames

Eh bien, j'ai essayé 3 façons de résoudre le même problème en C # 7 et j'ai trouvé un cas d'utilisation de Tuples.

Travailler avec des données dynamiques dans des projets Web peut parfois être difficile lors de la cartographie, etc.

J'aime la façon dont le tuple vient d'être automatiquement mappé sur item1, item2, itemN qui me semble plus robuste que d'utiliser des index de tableau où vous pourriez être attrapé sur un élément non indexé ou en utilisant le type anonyme où vous pouvez mal orthographier un nom de propriété.

C'est comme si un DTO avait été créé gratuitement en utilisant simplement un Tuple et je pouvais accéder à toutes les propriétés en utilisant itemN, qui ressemblait davantage à du typage statique sans avoir à créer un DTO séparé à cet effet.

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

Seulement pour le prototypage - Les tuples n'ont pas de sens. C'est pratique de les utiliser mais c'est un raccourci seulement! Pour les prototypes - bien. Assurez-vous simplement de supprimer ce code ultérieurement.

C'est facile à écrire, difficile à lire. Il n'a aucun avantage visible sur les classes, les classes internes, les classes anonymes, etc. 

0
bunny1985

Modification de la forme des objets lorsque vous devez les envoyer par câble ou les transmettre à un autre calque d'application lorsque plusieurs objets sont fusionnés en un seul:

Exemple:

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

Méthode d'extension:

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
Matas Vaitkevicius

Un paramètre out est idéal lorsque seules quelques valeurs doivent être renvoyées, .__, mais lorsque vous commencez à rencontrer 4, 5, 6 ou plusieurs valeurs devant être renvoyées, il peut devenir difficile à manier. Une autre option pour renvoyer plusieurs valeurs consiste à créer et à renvoyer Une classe/structure définie par l'utilisateur ou à utiliser un tuple pour regrouper toutes les valeurs devant être retournées par une méthode.

La première option, utilisant une classe/structure pour renvoyer les valeurs, est simple. Il suffit de créer le type (dans cet exemple, une structure) comme suit:

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

La deuxième option, qui utilise un tuple, est une solution encore plus élégante que l’utilisation d’un objet Défini par l’utilisateur. Un tuple peut être créé pour contenir un nombre quelconque de valeurs de types variables ..__ De plus, les données que vous stockez dans le tuple sont immuables; Une fois que vous avez ajouté les données à Tuple par le biais du constructeur ou de la méthode statique Create, ces données ne peuvent plus être modifiées . Tuples peut accepter jusqu'à huit valeurs distinctes et les inclure. Si vous devez renvoyer Plus de huit valeurs, vous devez utiliser la classe spéciale Tuple: Tuple Class Lorsque vous créez un tuple avec plus de huit valeurs, vous ne pouvez pas utiliser la méthode statique Create. méthode: vous devez plutôt utiliser le constructeur de la classe. Voici comment vous créeriez un tuple de 10 valeurs entières:

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

Bien sûr, vous pouvez continuer à ajouter plus de n-uplets à la fin de chaque tuple incorporé, , En créant un tuple de la taille dont vous avez besoin.

0