Meilleure dénomination dans les classes Tuple que «Item1», «Item2»


204

Existe-t-il un moyen d'utiliser une classe Tuple, mais de fournir les noms des éléments qu'elle contient?

Par exemple:

public Tuple<int, int, int int> GetOrderRelatedIds()

Cela renvoie les identifiants de OrderGroupId, OrderTypeId, OrderSubTypeId et OrderRequirementId.

Ce serait bien de faire savoir aux utilisateurs de ma méthode laquelle est laquelle. (Lorsque vous appelez la méthode, les résultats sont result.Item1, result.Item2, result.Item3, result.Item4. On ne sait pas lequel est lequel.)

(Je sais que je pourrais simplement créer une classe pour contenir tous ces identifiants, mais si ces identifiants ont déjà leurs propres classes dans lesquelles ils vivent et créer une classe pour la valeur de retour de cette méthode semble idiot.)


1
Vous allez devoir rouler le vôtre - Tupleest très générique, c'est donc tout ce que vous obtenez
BrokenGlass

NON, vous ne pouvez pas faire comme ça, voir ce lien pour plus d'informations msdn.microsoft.com/en-us/vcsharp/ee957397
Enigma State

1
Je me risquerais à dire que l'utilisation d'un Tuple comme type de données accessible au public pour votre API peut ne pas être recommandée. J'utilise généralement Tuple pour des choses internes de courte durée et non comme valeur de retour d'une API.
Mike Burdick


4
Il est dans la liste de travail de C # 7 Voir github.com/dotnet/roslyn/issues/347
Philip Ding

Réponses:


277

Dans C # 7.0 (Visual Studio 2017), il existe une nouvelle construction pour le faire:

(string first, string middle, string last) LookupName(long id)

68
La syntaxe est List<(int first, int second)>. J'ai dû télécharger le package System.ValueTuple de NuGet pour le faire fonctionner dans Visual Studio 2017.
Matt Davis

14
Pour créer la valeurreturn (first: first, middle: middle, last: last);
fiat

4
ou seulement: return (first, middle, last);dans .NET 4.7.1 (pas sûr pour 4.7.0)
watbywbarif

1
pour l'utiliser, vous devrez ajouter le paquet de pépites System.ValueTuple
Alex G

11
Il convient de noter que les C # 7 ValueTuple, bien que généralement excellents, sont un type de valeur mutable (struct), tandis Tuplequ'un type de référence immuable (classe). Pour autant que je sache, il n'y a aucun moyen d'obtenir un type de référence Tupleavec des noms d'élément conviviaux.
dx_over_dt

51

Jusqu'à C # 7.0, il n'y avait aucun moyen de faire cela sans définir votre propre type.


14
Je ne peux pas croire que cette réponse soit acceptée avec un score de 40. Vous auriez pu au moins montrer comment une classe avec un constructeur approprié peut remplacer cela.
bytecode77

1
@ bytecode77 Eh bien, très bientôt, cette réponse va être tout à fait fausse: github.com/dotnet/roslyn/issues/347
MarkPflug

J'ai vu ces propositions de festivités linguistiques. Mais jusqu'à présent, une classe est la seule solution appropriée pour les types de données plus complexes. Pourquoi voudriez-vous utiliser de force Tuples quoi qu'il arrive (voir les autres réponses)
bytecode77

3
Après la sortie de C # 7, il sera possible de le faire: msdn.microsoft.com/en-us/magazine/mt595758.aspx
Burak Karakuş

11
Le Q 'a c # 4 comme balise, donc bien que cette réponse soit courte, elle est toujours correcte.
Steve Drake

33

Voici une version trop compliquée de ce que vous demandez:

class MyTuple : Tuple<int, int>
{
    public MyTuple(int one, int two)
        :base(one, two)
    {

    }

    public int OrderGroupId { get{ return this.Item1; } }
    public int OrderTypeId { get{ return this.Item2; } }

}

Pourquoi ne pas simplement faire un cours?


2
struct serait mieux dans ce cas au lieu de classe?
deathrace

5
Le léger avantage que je vois de cela est qu'il implémente automatiquement l'opérateur égal, vérifiant que 2 instances sont égales si les éléments sont tous égaux.
JSoet

8
Un autre inconvénient de cette approche est que Item1 et Item2 sont toujours des propriétés publiques sur MyTuple
RJFalconer

3
@deathrace Tuple eux-mêmes sont des classes, donc si vous voulez hériter directement de Tuple<T, T2>vous ne pouvez pas être une structure.
Chakrava

3
Je peux me tromper, mais j'utilise principalement tuple partout où je veux retourner un objet, mais je ne veux pas définir une classe spécifique ..
Jay

12

Avec .net 4, vous pourriez peut-être regarder le ExpandoObject, cependant, ne l'utilisez pas pour ce cas simple car ce qui aurait été des erreurs de compilation deviendrait des erreurs d'exécution.

class Program
{
    static void Main(string[] args)
    {
        dynamic employee, manager;

        employee = new ExpandoObject();
        employee.Name = "John Smith";
        employee.Age = 33;

        manager = new ExpandoObject();
        manager.Name = "Allison Brown";
        manager.Age = 42;
        manager.TeamSize = 10;

        WritePerson(manager);
        WritePerson(employee);
    }
    private static void WritePerson(dynamic person)
    {
        Console.WriteLine("{0} is {1} years old.",
                          person.Name, person.Age);
        // The following statement causes an exception
        // if you pass the employee object.
        // Console.WriteLine("Manages {0} people", person.TeamSize);
    }
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.

Quelque chose d'autre mérite d'être mentionné est un type anonyme pour une méthode , mais vous devez créer une classe si vous souhaitez la renvoyer.

var MyStuff = new
    {
        PropertyName1 = 10,
        PropertyName2 = "string data",
        PropertyName3 = new ComplexType()
    };

10

Reproduire ma réponse à partir de cela post car elle est mieux adaptée ici.

À partir de C # v7.0, il est désormais possible de nommer les propriétés de tuple qui, auparavant, utilisaient par défaut des noms prédéfinis comme Item1, Item2etc.

Nommer les propriétés des Tuple Literals :

var myDetails = (MyName: "RBT_Yoga", MyAge: 22, MyFavoriteFood: "Dosa");
Console.WriteLine($"Name - {myDetails.MyName}, Age - {myDetails.MyAge}, Passion - {myDetails.MyFavoriteFood}");

La sortie sur console:

Nom - RBT_Yoga, 22 ans, passion - Dosa

Retour de Tuple (ayant des propriétés nommées) à partir d'une méthode :

static void Main(string[] args)
{
    var empInfo = GetEmpInfo();
    Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}

static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
    //This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
    return ("Rasik", "Bihari", "Rasik-PC", 1000);
}

La sortie sur console:

Détails sur l'employé: Rasik, Bihari, Rasik-PC, 1000

Création d'une liste de tuples ayant des propriétés nommées

var tupleList = new List<(int Index, string Name)>
{
    (1, "cow"),
    (5, "chickens"),
    (1, "airplane")
};

foreach (var tuple in tupleList)
    Console.WriteLine($"{tuple.Index} - {tuple.Name}");

Sortie sur console:

1 - vache 5 - poulets 1 - avion

J'espère avoir tout couvert. Dans le cas où il y a quelque chose que j'ai manqué, merci de me faire part de vos commentaires.

Remarque : Mes extraits de code utilisent la fonction d'interpolation de chaînes de C # v7, comme détaillé ici .


3

MichaelMocko Answered est super,

mais je veux ajouter quelques choses que je devais comprendre

(string first, string middle, string last) LookupName(long id)

la ligne ci-dessus vous donnera une erreur de temps de compilation si vous utilisez le framework .net <4.7

Donc, si vous avez un projet qui utilise le framework .net <4.7 et que vous souhaitez toujours utiliser ValueTuple, workAround installerait ce package de nuget



2

Si les types de vos articles sont tous différents, voici un cours que j'ai fait pour les obtenir de manière plus intuitive.

L'utilisation de cette classe:

var t = TypedTuple.Create("hello", 1, new MyClass());
var s = t.Get<string>();
var i = t.Get<int>();
var c = t.Get<MyClass>();

Code source:

public static class TypedTuple
{
    public static TypedTuple<T1> Create<T1>(T1 t1)
    {
        return new TypedTuple<T1>(t1);
    }

    public static TypedTuple<T1, T2> Create<T1, T2>(T1 t1, T2 t2)
    {
        return new TypedTuple<T1, T2>(t1, t2);
    }

    public static TypedTuple<T1, T2, T3> Create<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
    {
        return new TypedTuple<T1, T2, T3>(t1, t2, t3);
    }

    public static TypedTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4)
    {
        return new TypedTuple<T1, T2, T3, T4>(t1, t2, t3, t4);
    }

    public static TypedTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
    {
        return new TypedTuple<T1, T2, T3, T4, T5>(t1, t2, t3, t4, t5);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6>(t1, t2, t3, t4, t5, t6);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6, T7>(t1, t2, t3, t4, t5, t6, t7);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8>(t1, t2, t3, t4, t5, t6, t7, t8);
    }

}

public class TypedTuple<T>
{
    protected Dictionary<Type, object> items = new Dictionary<Type, object>();

    public TypedTuple(T item1)
    {
        Item1 = item1;
    }

    public TSource Get<TSource>()
    {
        object value;
        if (this.items.TryGetValue(typeof(TSource), out value))
        {
            return (TSource)value;
        }
        else
            return default(TSource);
    }

    private T item1;
    public T Item1 { get { return this.item1; } set { this.item1 = value; this.items[typeof(T)] = value; } }
}

public class TypedTuple<T1, T2> : TypedTuple<T1>
{
    public TypedTuple(T1 item1, T2 item2)
        : base(item1)
    {
        Item2 = item2;
    }

    private T2 item2;
    public T2 Item2 { get { return this.item2; } set { this.item2 = value; this.items[typeof(T2)] = value; } }
}

public class TypedTuple<T1, T2, T3> : TypedTuple<T1, T2>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3)
        : base(item1, item2)
    {
        Item3 = item3;
    }

    private T3 item3;
    public T3 Item3 { get { return this.item3; } set { this.item3 = value; this.items[typeof(T3)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4> : TypedTuple<T1, T2, T3>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4)
        : base(item1, item2, item3)
    {
        Item4 = item4;
    }

    private T4 item4;
    public T4 Item4 { get { return this.item4; } set { this.item4 = value; this.items[typeof(T4)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5> : TypedTuple<T1, T2, T3, T4>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
        : base(item1, item2, item3, item4)
    {
        Item5 = item5;
    }

    private T5 item5;
    public T5 Item5 { get { return this.item5; } set { this.item5 = value; this.items[typeof(T5)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6> : TypedTuple<T1, T2, T3, T4, T5>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
        : base(item1, item2, item3, item4, item5)
    {
        Item6 = item6;
    }

    private T6 item6;
    public T6 Item6 { get { return this.item6; } set { this.item6 = value; this.items[typeof(T6)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6, T7> : TypedTuple<T1, T2, T3, T4, T5, T6>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
        : base(item1, item2, item3, item4, item5, item6)
    {
        Item7 = item7;
    }

    private T7 item7;
    public T7 Item7 { get { return this.item7; } set { this.item7 = value; this.items[typeof(T7)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> : TypedTuple<T1, T2, T3, T4, T5, T6, T7>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
        : base(item1, item2, item3, item4, item5, item6, item7)
    {
        Item8 = item8;
    }

    private T8 item8;
    public T8 Item8 { get { return this.item8; } set { this.item8 = value; this.items[typeof(T8)] = value; } }
}

4
Cela semble être beaucoup de travail pour peu ou pas de gains. Il a une limitation non intuitive (pas de types en double), et je trouve l'idée de récupérer une valeur par son seul type incroyablement peu intuitive et ne peut pas penser à un cas d'utilisation pratique pour cela. Cela équivaut à créer un tableau de données pour les employés, puis à décider de récupérer les employés par leur prénom (par opposition à une clé unique) et à exiger par la suite que tous les employés aient des prénoms différents. Ce n'est pas une solution à un problème, c'est utiliser une solution au prix de créer un problème supplémentaire.
Flater

Et que Dieu ait pitié de votre âme.
Jamie M.

1

C'est très ennuyeux et je m'attends à ce que les futures versions de C # répondent à ce besoin. Je trouve que le travail le plus simple consiste à utiliser un type de structure de données différent ou à renommer les «éléments» pour votre raison et pour la raison des autres qui lisent votre code.

Tuple<ApiResource, JSendResponseStatus> result = await SendApiRequest();
ApiResource apiResource = result.Item1;
JSendResponseStatus jSendStatus = result.Item2;

0

Je pense que je créerais une classe mais une autre alternative est les paramètres de sortie.

public void GetOrderRelatedIds(out int OrderGroupId, out int OrderTypeId, out int OrderSubTypeId, out int OrderRequirementId)

Étant donné que votre tuple ne contient que des entiers, vous pouvez le représenter avec un Dictionary<string,int>

var orderIds = new Dictionary<string, int> {
    {"OrderGroupId", 1},
    {"OrderTypeId", 2},
    {"OrderSubTypeId", 3},
    {"OrderRequirementId", 4}.
};

mais je ne le recommande pas non plus.


0

Pourquoi tout le monde rend la vie si difficile. Les tuples sont destinés à un traitement des données plutôt temporaire . Travailler avec Tuples tout le temps rendra le code très difficile à comprendre à un moment donné. Créer des cours pour tout pourrait éventuellement gonfler votre projet.

Il s'agit cependant d'équilibre ...

Votre problème semble être quelque chose pour lequel vous voudriez un cours. Et juste pour être complet, cette classe ci-dessous contient également des constructeurs.


Ceci est le modèle approprié pour

  • Un type de données personnalisé
    • sans autre fonctionnalité. Les accesseurs et les setters peuvent également être étendus avec du code, obtenant / définissant des membres privés avec le modèle de nom "_orderGroupId", tout en exécutant également du code fonctionnel.
  • Y compris les constructeurs. Vous pouvez également choisir d'inclure un seul constructeur si tous propriétés sont obligatoires.
  • Si vous souhaitez utiliser tous les constructeurs, le bullage comme celui-ci est le modèle approprié pour éviter la duplication de code.

public class OrderRelatedIds
{
    public int OrderGroupId { get; set; }
    public int OrderTypeId { get; set; }
    public int OrderSubTypeId { get; set; }
    public int OrderRequirementId { get; set; }

    public OrderRelatedIds()
    {
    }
    public OrderRelatedIds(int orderGroupId)
        : this()
    {
        OrderGroupId = orderGroupId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId)
        : this(orderGroupId)
    {
        OrderTypeId = orderTypeId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId)
        : this(orderGroupId, orderTypeId)
    {
        OrderSubTypeId = orderSubTypeId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId, int orderRequirementId)
        : this(orderGroupId, orderTypeId, orderSubTypeId)
    {
        OrderRequirementId = orderRequirementId;
    }
}

Ou, si vous voulez que ce soit très simple: vous pouvez également utiliser des initialiseurs de type:

OrderRelatedIds orders = new OrderRelatedIds
{
    OrderGroupId = 1,
    OrderTypeId = 2,
    OrderSubTypeId = 3,
    OrderRequirementId = 4
};

public class OrderRelatedIds
{
    public int OrderGroupId;
    public int OrderTypeId;
    public int OrderSubTypeId;
    public int OrderRequirementId;
}

0

J'écrirais les noms des éléments dans le résumé .. donc en survolant la fonction helloworld (), le texte dira bonjour = élément1 et monde = élément2

 helloworld("Hi1,Hi2");

/// <summary>
/// Return hello = Item1 and world Item2
/// </summary>
/// <param name="input">string to split</param>
/// <returns></returns>
private static Tuple<bool, bool> helloworld(string input)
{
    bool hello = false;
    bool world = false;
    foreach (var hw in input.Split(','))
    {
        switch (hw)
        {
            case "Hi1":
                hello= true;
                break;
            case "Hi2":
                world= true;
                break;
        }

    }
    return new Tuple<bool, bool>(hello, world);
}

0

Juste pour ajouter à la réponse @MichaelMocko. Les tuples ont quelques accrochages en ce moment:

Vous ne pouvez pas les utiliser dans les arbres d'expression EF

Exemple:

public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        // Selecting as Tuple
        .Select(person => (person.Name, person.Surname))
        .First();
}

Cela ne parviendra pas à compiler avec l'erreur «Une arborescence d'expression peut ne pas contenir de littéral de tuple». Malheureusement, l'API des arborescences d'expression n'a pas été étendue avec la prise en charge des tuples lorsque ceux-ci ont été ajoutés au langage.

Suivez (et votez) ce problème pour les mises à jour: https://github.com/dotnet/roslyn/issues/12897

Pour contourner le problème, vous pouvez d'abord le convertir en type anonyme, puis convertir la valeur en tuple:

// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => new { person.Name, person.Surname })
        .ToList()
        .Select(person => (person.Name, person.Surname))
        .First();
}

Une autre option consiste à utiliser ValueTuple.Create:

// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => ValueTuple.Create(person.Name, person.Surname))
        .First();
}

Références:

Vous ne pouvez pas les déconstruire à Lambdas

Il y a une proposition pour ajouter le support: https://github.com/dotnet/csharplang/issues/258

Exemple:

public static IQueryable<(string name, string surname)> GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => ValueTuple.Create(person.Name, person.Surname));
}

// This won't work
ctx.GetPersonName(id).Select((name, surname) => { return name + surname; })

// But this will
ctx.GetPersonName(id).Select(t => { return t.name + t.surname; })

Références:

Ils ne sérialiseront pas bien

using System;
using Newtonsoft.Json;

public class Program
{
    public static void Main() {
        var me = (age: 21, favoriteFood: "Custard");
        string json = JsonConvert.SerializeObject(me);

        // Will output {"Item1":21,"Item2":"Custard"}
        Console.WriteLine(json); 
    }
}

Les noms de champ de tuple ne sont disponibles qu'au moment de la compilation et sont complètement effacés lors de l'exécution.

Références:


-1

Vous pouvez écrire une classe qui contient le tuple.

Vous devez remplacer les fonctions Equals et GetHashCode

et les opérateurs == et! =.

class Program
{
    public class MyTuple
    {
        private Tuple<int, int> t;

        public MyTuple(int a, int b)
        {
            t = new Tuple<int, int>(a, b);
        }

        public int A
        {
            get
            {
                return t.Item1;
            }
        }

        public int B
        {
            get
            {
                return t.Item2;
            }
        }

        public override bool Equals(object obj)
        {
            return t.Equals(((MyTuple)obj).t);
        }

        public override int GetHashCode()
        {
            return t.GetHashCode();
        }

        public static bool operator ==(MyTuple m1, MyTuple m2)
        {
            return m1.Equals(m2);
        }

        public static bool operator !=(MyTuple m1, MyTuple m2)
        {
            return !m1.Equals(m2);
        }
    }

    static void Main(string[] args)
    {
        var v1 = new MyTuple(1, 2);
        var v2 = new MyTuple(1, 2);

        Console.WriteLine(v1 == v2);

        Dictionary<MyTuple, int> d = new Dictionary<MyTuple, int>();
        d.Add(v1, 1);

        Console.WriteLine(d.ContainsKey(v2));
    }
}

retournera:

Vrai

Vrai


2
Si vous avez déjà implémenté une classe pour ce type de données, pourquoi déclarez-vous un tuple pour les données sous-jacentes au lieu de simplement des propriétés?
bytecode77

Je veux utiliser l'attribut tuple qu'il compère par valeur dans la fonction Equals
ss

Cela peut être un bonus. Mais d'un autre côté, vous avez essentiellement créé une classe avec des propriétés allant de Item1 à ItemX. Je choisirais un nom correct et plus de code dans Equals () plutôt que d'utiliser un tuple.
bytecode77

-1

Exemple de tuple C # 7

var tuple = TupleExample(key, value);

     private (string key1, long value1) ValidateAPIKeyOwnerId(string key, string value)
            {
                return (key, value);
            }
      if (!string.IsNullOrEmpty(tuple.key1) && tuple.value1 > 0)
          {
                    //your code

                }     
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.