it-swarm.com.de

Wie kann man eine Liste von Tupeln leicht initialisieren?

Ich liebe Tupel . Damit können Sie relevante Informationen schnell zusammenfassen, ohne dafür eine Struktur oder Klasse schreiben zu müssen. Dies ist sehr nützlich, wenn Sie sehr lokalisierten Code umwandeln.

Das Initialisieren einer Liste von ihnen erscheint jedoch ein wenig redundant.

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

Gibt es nicht einen besseren Weg? Ich würde gerne eine Lösung im Sinne des Dictionary-Initialisierers finden.

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

Können wir nicht eine ähnliche Syntax verwenden?

204
Steven Jeuris

mit c # 7.0 können Sie Folgendes tun:

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

Wenn Sie keine List benötigen, sondern nur ein Array, können Sie Folgendes tun:

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

Wenn Sie "Item1" und "Item2" nicht mögen, können Sie Folgendes tun:

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

oder

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

was Sie tun können: tupleList[0].Index und tupleList[0].Name

Framework 4.6.2 und darunter

Sie müssen System.ValueTuple vom Nuget Package Manager installieren.

Framework 4.7 und höher

Es ist in das Framework eingebaut. Führen Sie nicht installieren System.ValueTuple aus. Entfernen Sie es und löschen Sie es aus dem bin-Verzeichnis.

Anmerkung: Im wirklichen Leben könnte ich mich nicht zwischen Kuh, Hühnern oder Flugzeug entscheiden. Ich wäre wirklich zerrissen.

130
toddmo

Ja! Das ist möglich .

Die {} -Syntax des Collection-Initialisierers funktioniert für alle IEnumerable Typ, der eine Add -Methode mit der korrekten Anzahl von Argumenten hat . Ohne sich darum zu kümmern, wie das unter den Abdeckungen funktioniert, können Sie Erweitern Sie einfach von List <T>, fügen Sie eine benutzerdefinierte Add-Methode hinzu, um Ihre .__-Datei zu initialisieren. T, und du bist fertig!

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

Auf diese Weise können Sie Folgendes tun:

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

C # 6 fügt eine neue Funktion hinzu: Erweiterung Methoden hinzufügen. Dies war für VB.net immer möglich, ist jedoch jetzt in C # verfügbar.

Jetzt müssen Sie den Klassen keine Add()-Methoden direkt hinzufügen, sondern können sie als Erweiterungsmethoden implementieren. Wenn Sie einen beliebigen aufzählbaren Typ mit einer Add()-Methode erweitern, können Sie ihn in Auflistungsinitialisiererausdrücken verwenden. Sie müssen also nicht mehr explizit von Listen ableiten ( wie in einer anderen Antwort erwähnt ), Sie können sie einfach erweitern.

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

Auf diese Weise können Sie dies für jede Klasse tun, die IList<> implementiert:

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

Natürlich sind Sie nicht darauf beschränkt, Sammlungen von Tupeln zu erweitern, es kann sich dabei um Sammlungen eines bestimmten Typs handeln, für den Sie die spezielle Syntax wünschen.

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 wird Unterstützung für in die Sprache integrierte Tupel hinzufügen, obwohl sie einen anderen Typ haben (stattdessen System.ValueTuple). Daher wäre es gut, Überladungen für Wertetupel hinzuzufügen, sodass Sie die Option haben, sie auch zu verwenden. Leider sind keine impliziten Konvertierungen zwischen den beiden definiert.

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

Auf diese Weise wird die Listeninitialisierung noch schöner aussehen.

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

Aber anstatt all diese Schwierigkeiten durchzugehen, ist es vielleicht besser, ausschließlich auf ValueTuple umzusteigen.

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

Sie können dies tun, indem Sie den Konstruktor jedes Mal aufrufen, wenn etwas besser ist

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

Alte Frage, aber das tue ich normalerweise, um die Dinge lesbarer zu machen:

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 Ich weiß, aber ich würde meinen Beitrag zur Verwendung von Linq und Fortführungs-Lambdas zu Methoden mit C # 7 hinzufügen. Ich versuche, benannte Tupel als Ersatz für DTOs und anonyme Projektionen zu verwenden, wenn sie in einer Klasse verwendet werden. Ja, um Spott zu machen und zu testen, benötigen Sie noch Klassen, aber Inline-Aktivitäten und das Weitergeben in einer Klasse ist schön, wenn Sie diese neuere Option IMHO haben. Sie können sie aus instanziieren

  1. Direkte Instantiierung
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
  1. Wenn Sie sich aus einer vorhandenen Sammlung befinden, können Sie jetzt gut typisierte Tupel zurückgeben, ähnlich wie zuvor anonyme Projektionen.
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. Verwenden Sie die Tupel später erneut mit Gruppierungsmethoden oder verwenden Sie eine Methode, um sie in einer anderen Logik zu erstellen:
//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

Eine Technik, die ich denke, ist etwas einfacher und wurde hier noch nicht erwähnt:

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

Ich denke, das ist ein bisschen sauberer als:

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