web-dev-qa-db-ja.com

タプルのリストを簡単に初期化する方法

私は大好きです タプル 。それらを使用すると、構造体やクラスを作成しなくても、関連情報をすばやくグループ化できます。非常にローカライズされたコードをリファクタリングしながらこれは非常に便利です。

ただし、それらのリストを初期化するのは少し冗長です。

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

もっと良い方法はありませんか?私は Dictionary initializer に沿った解決策が大好きです。

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

同じような構文を使うことはできませんか?

226
Steven Jeuris

c#7.0はあなたがこれを行うことができます:

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

Listではなく配列だけが必要な場合は、次のようにします。

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

あなたが "Item1"と "Item2"を好きではないなら、あなたはすることができます:

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

または

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

tupleList[0].IndextupleList[0].Name

フレームワーク4.6.2以下

Nuget Package Managerから System.ValueTuple をインストールする必要があります。

フレームワーク4.7以上

それはフレームワークに組み込まれています。 System.ValueTupleをインストールしないしない。実際には、それを削除しそしてbinディレクトリから削除してください。

注:実生活では、牛、鶏、飛行機の中から選ぶことはできません。私は本当に引き裂かれるでしょう

164
toddmo

はい! これは可能です

コレクション初期化子の{}構文は、正しい量の引数を持つAddメソッドを持つすべてのIEnumerable型に対して機能します。それがカバーの下でどのように機能するかを気にすることなく、それはあなたがList <T>から拡張することができ、あなたのTを初期化するカスタムAddメソッドを追加できやった!

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

これにより、次のことが可能になります。

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

C#6はこのためだけに新しい機能を追加しています。これはVB.netでは常に可能でしたが、現在はC#で利用可能です。

Add()メソッドを直接あなたのクラスに追加する必要はありません、あなたはそれらを拡張メソッドとして実装することができます。列挙型をAdd()メソッドで拡張すると、コレクション初期化式でそれを使用できます。そのため、もうリストから明示的に派生する必要はなく( 別の回答で説明したように )、単にそれを拡張することができます。

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

これにより、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 },
};

もちろん、あなたはタプルのコレクションを拡張することに制限されていません、それはあなたが特別な構文を欲しているどんな特定のタイプのコレクションのためでもありえます。

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は、言語に組み込まれたタプルのサポートを追加する予定ですが、それらは異なるタイプになります(代わりにSystem.ValueTuple)。そのため、値タプルにオーバーロードを追加して、それらを使用することもできます。残念ながら、この2つの間で暗黙の変換は定義されていません。

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

このようにして、リストの初期化はさらに良くなります。

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

しかし、このような問題をすべて解決するのではなく、ValueTupleを排他的に使用するように切り替えるほうがよい場合があります。

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

毎回コンストラクタを呼び出すことでこれを行うことができます。

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

古い質問ですが、これは私が物事をもう少し読みやすくするために私が通常することです:

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" )
};
28
Asad Saeeduddin

Super Duper Old私は知っているが、私はC#7を使ってメソッドにLinqと継続ラムダを使うことに私の作品を追加するつもりである。モックやテストのためにはまだクラスが必要ですが、クラス内で物事をインラインでやり取りすることは、この新しいオプションのIMHOを使用するのに最適です。あなたはそれらをインスタンス化することができます

  1. 直接インスタンス化
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
  1. 既存のコレクションから離れたところで、匿名の投影法が行われていたのと同じように、よく型付けされたタプルを返すことができます。
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. 後でグループ化メソッドでタプルを再利用するか、メソッドを使用してそれらを他のロジックにインラインで構築します。
//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

私が思うに一つのテクニックはもう少し簡単です、そしてそれはここで前に述べられませんでした:

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

私はそれがもう少しきれいだと思います:

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