web-dev-qa-db-fra.com

Comment initialiser facilement une liste de Tuples?

J'adore tuples . Ils vous permettent de regrouper rapidement des informations pertinentes sans avoir à écrire une structure ou une classe à cet effet. Ceci est très utile lors du refactoring de code très localisé.

Initialiser une liste d’entre eux semble cependant un peu redondant.

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

N'y a-t-il pas un meilleur moyen? J'aimerais une solution dans le sens du Dictionnaire initializer .

Dictionary<int, string> students = new Dictionary<int, string>()
{
    { 111, "bleh" },
    { 112, "bloeh" },
    { 113, "blah" }
};

Ne pouvons-nous pas utiliser une syntaxe similaire?

204
Steven Jeuris

c # 7.0 vous permet de faire ceci:

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

Si vous n'avez pas besoin d'une List, mais simplement d'un tableau, vous pouvez faire:

  var tupleList = new(int, string)[]
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

Et si vous n'aimez pas "Item1" et "Item2", vous pouvez faire:

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

ou

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

qui vous permet de faire: tupleList[0].Index et tupleList[0].Name

Framework 4.6.2 et inférieur

Vous devez installer System.ValueTuple à partir du gestionnaire de paquets Nuget.

Framework 4.7 et supérieur

Il est construit dans le cadre. Faites pas installez System.ValueTuple. En fait, supprimez-le et supprimez-le du répertoire bin.

note: dans la vraie vie, je ne pourrais pas choisir entre vache, poulet ou avion. Je serais vraiment déchiré.

130
toddmo

Oui! C'est possible .

La syntaxe {} de l'initialiseur de collection fonctionne sur tout IEnumerable type qui a une méthode Add avec la quantité d'arguments correcte . Sans se soucier de la façon dont cela fonctionne sous les couvertures, cela signifie que vous pouvez simplement étendre de List <T>, ajoutez une méthode Add personnalisée pour initialiser votre T, et vous avez terminé!

public class TupleList<T1, T2> : List<Tuple<T1, T2>>
{
    public void Add( T1 item, T2 item2 )
    {
        Add( new Tuple<T1, T2>( item, item2 ) );
    }
}

Cela vous permet de faire ce qui suit:

var groceryList = new TupleList<int, string>
{
    { 1, "kiwi" },
    { 5, "apples" },
    { 3, "potatoes" },
    { 1, "tomato" }
};
207
Steven Jeuris

C # 6 ajoute une nouvelle fonctionnalité rien que pour cela: extension Ajouter des méthodes. Cela a toujours été possible pour VB.net mais est maintenant disponible en C #.

Maintenant, vous n'avez pas à ajouter directement les méthodes Add() à vos classes, vous pouvez les implémenter en tant que méthodes d'extension. Lors de l'extension d'un type énumérable avec une méthode Add(), vous pourrez l'utiliser dans les expressions d'initialiseur de collection. Ainsi, vous n’avez plus à dériver explicitement des listes ( comme mentionné dans une autre réponse ), vous pouvez simplement l’étendre.

public static class TupleListExtensions
{
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
            T1 item1, T2 item2)
    {
        list.Add(Tuple.Create(item1, item2));
    }

    public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
            T1 item1, T2 item2, T3 item3)
    {
        list.Add(Tuple.Create(item1, item2, item3));
    }

    // and so on...
}

Cela vous permettra de le faire sur n'importe quelle classe implémentant IList<>:

var numbers = new List<Tuple<int, string>>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
    { 4, "four" },
    { 5, "five" },
};
var points = new ObservableCollection<Tuple<double, double, double>>
{
    { 0, 0, 0 },
    { 1, 2, 3 },
    { -4, -2, 42 },
};

Bien sûr, vous n'êtes pas obligé d'étendre des collections de n-uplets, cela peut être pour des collections d'un type spécifique pour lequel vous voulez la syntaxe spéciale.

public static class BigIntegerListExtensions
{
    public static void Add(this IList<BigInteger> list,
        params byte[] value)
    {
        list.Add(new BigInteger(value));
    }

    public static void Add(this IList<BigInteger> list,
        string value)
    {
        list.Add(BigInteger.Parse(value));
    }
}

var bigNumbers = new List<BigInteger>
{
    new BigInteger(1), // constructor BigInteger(int)
    2222222222L,       // implicit operator BigInteger(long)
    3333333333UL,      // implicit operator BigInteger(ulong)
    { 4, 4, 4, 4, 4, 4, 4, 4 },               // extension Add(byte[])
    "55555555555555555555555555555555555555", // extension Add(string)
};

C # 7 ajoutera une prise en charge pour les n-uplets intégrés au langage, bien qu’ils soient d’un type différent (System.ValueTuple à la place). Il serait donc bon d’ajouter des surcharges pour les tuples de valeur afin que vous ayez également la possibilité de les utiliser. Malheureusement, aucune conversion implicite n'est définie entre les deux.

public static class ValueTupleListExtensions
{
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
        ValueTuple<T1, T2> item) => list.Add(item.ToTuple());
}

De cette façon, l'initialisation de la liste sera encore plus jolie.

var points = new List<Tuple<int, int, int>>
{
    (0, 0, 0),
    (1, 2, 3),
    (-1, 12, -73),
};

Mais au lieu de passer par tous ces problèmes, il serait peut-être préférable d’utiliser exclusivement ValueTuple.

var points = new List<(int, int, int)>
{
    (0, 0, 0),
    (1, 2, 3),
    (-1, 12, -73),
};
77
Jeff Mercado

Vous pouvez le faire en appelant le constructeur à chaque fois avec un peu mieux

var tupleList = new List<Tuple<int, string>>
{
    new Tuple<int, string>(1, "cow" ),
    new Tuple<int, string>( 5, "chickens" ),
    new Tuple<int, string>( 1, "airplane" )
};
41
Kevin Holditch

Ancienne question, mais voici ce que je fais généralement pour rendre les choses un peu plus lisibles:

Func<int, string, Tuple<int, string>> tc = Tuple.Create;

var tupleList = new List<Tuple<int, string>>
{
    tc( 1, "cow" ),
    tc( 5, "chickens" ),
    tc( 1, "airplane" )
};
26
Asad Saeeduddin

Super Duper Old Je sais mais j'ajouterais mon article sur l'utilisation de Linq et la suite de lambdas sur les méthodes utilisant C # 7. J'essaie d'utiliser des n-uplets nommés en remplacement des DTO et des projections anonymes lorsqu'ils sont réutilisés dans une classe. Oui, pour vous moquer et vous tester, vous avez toujours besoin de cours, mais faire des choses en ligne et se déplacer dans une classe est agréable d'avoir cette nouvelle option à mon humble avis. Vous pouvez les instancier de

  1. Instanciation directe
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
  1. Hors d'une collection existante, vous pouvez maintenant renvoyer des nuplets bien typés, similaires à ceux utilisés auparavant pour les projections anonymes.
public class Hold
{
    public int Id { get; set; }
    public string Name { get; set; }
}

//In some method or main console app:
var holds = new List<Hold> { new Hold { Id = 1, Name = "Me" }, new Hold { Id = 2, Name = "You" } };
var anonymousProjections = holds.Select(x => new { SomeNewId = x.Id, SomeNewName = x.Name });
var namedTuples = holds.Select(x => (TupleId: x.Id, TupleName: x.Name));
  1. Réutilisez les n-uplets plus tard avec des méthodes de regroupement ou utilisez une méthode pour les construire en ligne dans une autre logique:
//Assuming holder class above making 'holds' object
public (int Id, string Name) ReturnNamedTuple(int id, string name) => (id, name);
public static List<(int Id, string Name)> ReturnNamedTuplesFromHolder(List<Hold> holds) => holds.Select(x => (x.Id, x.Name)).ToList();
public static void DoSomethingWithNamedTuplesInput(List<(int id, string name)> inputs) => inputs.ForEach(x => Console.WriteLine($"Doing work with {x.id} for {x.name}"));

var namedTuples2 = holds.Select(x => ReturnNamedTuple(x.Id, x.Name));
var namedTuples3 = ReturnNamedTuplesFromHolder(holds);
DoSomethingWithNamedTuplesInput(namedTuples.ToList());
1
djangojazz

Je pense qu'une technique est un peu plus facile et n'a pas été mentionnée auparavant

var asdf = new [] { 
    (Age: 1, Name: "cow"), 
    (Age: 2, Name: "bird")
}.ToList();

Je pense que c'est un peu plus propre que:

var asdf = new List<Tuple<int, string>> { 
    (Age: 1, Name: "cow"), 
    (Age: 2, Name: "bird")
};
0
Alex Dresko