これは抑えておきたい。
成果物
コード
Code0.cs
using System; using System.Collections.Generic; using System.Linq; namespace Tutorial_Linq { class Code1 { public void Run() { var startingDeck = from s in Suits() from r in Ranks() select new { Suit = s, Rank = r }; // var startingDeck = Suits().SelectMany(suit => Ranks().Select(rank => new { Suit = suit, Rank = rank })); // Display each card that we've generated and placed in startingDeck in the console foreach (var card in startingDeck) { Console.WriteLine(card); } } public IEnumerable<string> Suits() { yield return "clubs"; yield return "diamonds"; yield return "hearts"; yield return "spades"; } public IEnumerable<string> Ranks() { yield return "two"; yield return "three"; yield return "four"; yield return "five"; yield return "six"; yield return "seven"; yield return "eight"; yield return "nine"; yield return "ten"; yield return "jack"; yield return "queen"; yield return "king"; yield return "ace"; } } }
$ dotnet run { Suit = clubs, Rank = two } { Suit = clubs, Rank = three } { Suit = clubs, Rank = four } { Suit = clubs, Rank = five } { Suit = clubs, Rank = six } { Suit = clubs, Rank = seven } { Suit = clubs, Rank = eight } { Suit = clubs, Rank = nine } { Suit = clubs, Rank = ten } { Suit = clubs, Rank = jack } { Suit = clubs, Rank = queen } { Suit = clubs, Rank = king } { Suit = clubs, Rank = ace } { Suit = diamonds, Rank = two } { Suit = diamonds, Rank = three } { Suit = diamonds, Rank = four } { Suit = diamonds, Rank = five } { Suit = diamonds, Rank = six } { Suit = diamonds, Rank = seven } { Suit = diamonds, Rank = eight } { Suit = diamonds, Rank = nine } { Suit = diamonds, Rank = ten } { Suit = diamonds, Rank = jack } { Suit = diamonds, Rank = queen } { Suit = diamonds, Rank = king } { Suit = diamonds, Rank = ace } { Suit = hearts, Rank = two } { Suit = hearts, Rank = three } { Suit = hearts, Rank = four } { Suit = hearts, Rank = five } { Suit = hearts, Rank = six } { Suit = hearts, Rank = seven } { Suit = hearts, Rank = eight } { Suit = hearts, Rank = nine } { Suit = hearts, Rank = ten } { Suit = hearts, Rank = jack } { Suit = hearts, Rank = queen } { Suit = hearts, Rank = king } { Suit = hearts, Rank = ace } { Suit = spades, Rank = two } { Suit = spades, Rank = three } { Suit = spades, Rank = four } { Suit = spades, Rank = five } { Suit = spades, Rank = six } { Suit = spades, Rank = seven } { Suit = spades, Rank = eight } { Suit = spades, Rank = nine } { Suit = spades, Rank = ten } { Suit = spades, Rank = jack } { Suit = spades, Rank = queen } { Suit = spades, Rank = king } { Suit = spades, Rank = ace }
要点
IEnumerable
型のLINQ
IEnumerable
型をクロス結合する。SQL文でいうとcross join
。全パターン網羅。
var startingDeck = from s in Suits() from r in Ranks() select new { Suit = s, Rank = r };
同様のことを以下のメソットで記述できる。
var startingDeck = Suits().SelectMany(suit => Ranks().Select(rank => new { Suit = suit, Rank = rank }));
IEnumerable
メソッド
SelectMany()
Select()
Take()
Skip()
バグ
匿名クラスの型はdynamic
である
LINQのコードでIEnumerable<T>
型を受け取るとき、しばしばvar
型を使う。だが、var
型はローカル変数にしか使えない。var
型はメソッドの戻り値に使えない。そこでdynamic
型を使う。
private System.Collections.Generic.IEnumerable<dynamic> Create() { var startingDeck = from s in Suits() from r in Ranks() select new { Suit = s, Rank = r }; return startingDeck; }
エラーログ
IEnumerable<T>
には変換できない。以下のように怒られる。
private System.Collections.Generic.IEnumerable<string> Create() {
Code2.cs(22,20): error CS0266: 型 'System.Collections.Generic.IEnumerable<<anonymous type: string Suit, string Rank>>' を 'System.Collections.Generic.IEnumerable<string>' に暗黙的に変換できません。明示的な変換が存在します (cast が不足していないかどうかを確認してください) [/tmp/work/Tutorial_Linq/Tutorial_Linq.csproj]
IEnumerable<T, T>
も使えない。以下のように怒られる。
private System.Collections.Generic.IEnumerable<string, string> Create() {
Code2.cs(17,44): error CS0305: ジェネリック 種類 'IEnumerable<T>' を使用するには、1 型引数が必要です。 [/tmp/work/Tutorial_Linq/Tutorial_Linq.csproj]
どうやらIEnumerable
は<T>
しか使えないらしい。では、IEnumerable<T>
のT
に匿名クラスnew { Suit = s, Rank = r }
を含めたいときはどうすればいいのか? 「C# 無名クラスを返す メソッド シグネチャ」でググると以下がヒットした。
匿名クラスに対応する型はdynamic
らしい。というわけで、IEnumerable<dynamic>
とした。エラーが消えた。
ちなみに、from ... select new { Suit = s, Rank = r }
の戻り値の型を調べてみた。
Console.WriteLine(startingDeck.GetType());
System.Linq.Enumerable+<SelectManyIterator>d__180`3[System.String,System.String,<>f__AnonymousType0`2[System.String,System.String]]
AnonymousType
とかいうのが匿名クラスらしい。Anonymous
の意味を調べるとそのまま「匿名」だった。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux