ここではC#のLINQを学習します。
LINQとは
LINQとは、コレクションに対してSQLっぽい処理を記述できる手法です。
正式名称は統合言語クエリ(Language-Integrated Query)となり、略してLINQと呼ばれます。
なお、公式の説明は超難しいです。
統合言語クエリ(LINQ)は、C#言語への直接的なクエリ機能の統合に基づくテクノロジのセットの名前です。これまでは、データに対するクエリは、コンパイル時の型チェックやIntelliSenseのサポートがない単純な文字列として表現されてきました。さらに、SQLデータベース、XMLドキュメント、さまざまなWebサービスなど、各種データソースの異なるクエリ言語を学習する必要があります。LINQでは、クエリは、クラス、メソッド、イベントと同様に、ファーストクラスの言語コンストラクトです。
Retrieved 22:59, April 13, 2025, from https://learn.microsoft.com/ja-jp/dotnet/csharp/linq/

なるほどねぇ。

これどういう意味なの?

とりあえず、意味が分からないことが分かった。

そうですか...
ちなみにSQLっぽく書ける手段があるだけでSQLは一切関係ありません。
あくまでコレクションに対しての処理を記述する1つの仕組みです。
TipsSQLとはデータベースに対する操作を記述するプログラミング言語です。
LINQを利用するための前知識
LINQを利用するための前知識として匿名型やラムダ式の知識が必要です。
後、知らなくても問題ありませんが、各種LINQは拡張メソッドとして定義されてます。
ここではC#の匿名型とdynamic型を学習します。 匿名型(Anonymous type) 匿名型とは、型名が存在しない型を意味します。これは型名が匿名とも言えます。SNS等で本人特定されないことを匿名性とか言いますが、似てなくもないです。 型の名前が存在しないって意味が分からないと思いますが、本当にそのままです。メソッド配下など、記述できる場所は限られますが、次のような名無し型を作れます。 namespace Sample { internal class Pr...
ここではC#のラムダ式を学習します。個人的にC#で1番好きな記述方法です。 いわゆる簡易記述になり、ラムダ式を使わない記述もできます。ただ、実際は必須です。これを覚えないと現実的なコードが書けません。 ラムダ式(Lambda expressions) ラムダ式とは、メソッド(関数)を定義せずに直接記述する方法です。 言ってる意味が分かりません。 正直、いい感じの説明がない。上の説明も合ってるか怪しい。 と言うことで、実際のラムダ式を体験しましょ...
ここではC#の拡張メソッドを学習します。 拡張メソッド(Extension Methods) 拡張メソッドとは、既存のクラスにメソッドを追加できる機能です。 継承を利用すれば既存クラスに機能を追加することができますが、その場合は別クラスになります。対して拡張メソッドの場合、元のクラスに新規メソッドが増えたような扱いになります。 最初に次のようなクラスがあったとします。単にプロパティが2個と表示用のメソッドを所有します。 namespace Sample { p...
名前空間は不要
拡張メソッドを利用するためには名前空間の記述が必要です。
LINQの場合は次の名前空間を指定しますが、現在は内部的に自動宣言されてるので不要です。
using System.Linq;

古いシステムだと任意の宣言が必要です。

いつから不要なの?

知らん。書いて駄目なら宣言して。
LINQを使ってみる
先に伝えた通り、LINQはコレクションに対する処理を記述する手段です。
まず、次のコレクションがあるとしましょう。単なるint型の配列です。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new int[] { 1, 2, 3, 4, 5 };
foreach (var d in collection)
Console.WriteLine(d);
}
}
}
ここから特定の数値だけを抜き出して表示したいと思います。
仮に3以上の数値だけを表示するとします。こんな感じですかね。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new int[] { 1, 2, 3, 4, 5 };
foreach (var d in collection)
{
if (d >= 3)
Console.WriteLine(d);
}
}
}
}
でっ、これをLINQに書き換えてみます。結論としてこうなります。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new int[] { 1, 2, 3, 4, 5 };
// LINQ利用
var query = collection.Where(x => x >= 3);
foreach (var d in query)
Console.WriteLine(d);
}
}
}
コレクションに対してWhereで記述してるのがLINQです。WhereはLINQの中の1機能です。
条件をラムダ式で記述します。ちなみにLINQで書かれる式をクエリと呼びます。
// LINQ利用
var query = collection.Where(x => x >= 3);
これは対象のコレクションから条件を満たす値を抜き出す式です。
まぁ、式という表現が正しいかは分かりませんが、そういうものです。
他にもSelectとかAnyとかGroupByとかとか、色々と実装されてます。
SQLっぽいのは使われてる名前が似てるからですかね。
2つの記述方法
LINQには2つの記述方法が存在します。それをメソッド構文とクエリ構文と言います。
出来ることに違いは無いと思います。ただ、僕の経験だとメソッド構文しか使いません。
メソッド構文
さっきのパターンです。こんな感じにメソッドっぽく記述するやつ。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new int[] { 1, 2, 3, 4, 5 };
// LINQ利用(メソッド構文)
var query = collection.Where(x => x >= 3);
foreach (var d in query)
Console.WriteLine(d);
}
}
}

実質的にメソッド構文の1択。

そうなの?

いや、知らねぇけど。

...
クエリ構文
こっちはSQLっぽく記述するパターンです。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new int[] { 1, 2, 3, 4, 5 };
// LINQ利用(クエリ構文)
var query = from x in collection where x >= 3 select x;
foreach (var d in query)
Console.WriteLine(d);
}
}
}

人生で初めて書いた。間違ってたらすまんな(適当)。

雑すぎる...
補足このサイトではクエリ構文は一切扱いません。
LINQを使う理由
LINQを使うのは処理をシンプルに記述できるからです。
ここでは2つのパターンの使用例を紹介します。
LINQは連続して記述できる
1つ目のパターンです。
LINQは複数の処理を連続(結合)して記述できます。
これはSkipとWhereとMaxを繋げてみました。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new int[] { 8, 1, 15, 6, 19 };
var query = collection.Skip(1).Where(x => x < 10).Max();
Console.WriteLine(query);
}
}
}
Skipは指定した個数分をスキップ、Whereは条件指定、Maxは最大値を求めます。
つまり、コレクションに対して1個分をスキップし、10より小さい値を集め、その中から最大値を探しました。
これを1行で書けるのがLINQです。最強ですね。
なお、人によっては改行します。この辺は処理の長さとか好み。
var query = collection
.Skip(1)
.Where(x => x < 10)
.Max();
var query = (
collection
.Skip(1)
.Where(x => x < 10)
.Max()
);

こうやってコーディング論争が起きるのであった。

嫌な業界だね。
条件に処理を記述できる
2つ目のパターンです。
LINQは条件をラムダ式で記述しますが、そこに自由な式を記述できます。
まぁ、単なるメソッドなので当たり前ですね。とりあえず、こんなコードがあったとします。
namespace Sample
{
internal class Data
{
public string Name { get; set; }
public int Score { get; set; }
public void Display()
{
Console.WriteLine($"Name={this.Name}, Score={this.Score}");
}
}
internal class Program
{
static void Main(string[] args)
{
var collection = new Data[]
{
new Data() { Name = "A", Score = 90, },
new Data() { Name = "B", Score = 50, },
new Data() { Name = "C", Score = 30, },
};
foreach (var d in collection)
d.Display();
}
}
}
ここからScoreが最も大きい人物を探したいと思います。
最大値なので先ほど使ったMaxが活用できそうです。
namespace Sample
{
internal class Data
{
public string Name { get; set; }
public int Score { get; set; }
public void Display()
{
Console.WriteLine($"Name={this.Name}, Score={this.Score}");
}
}
internal class Program
{
static void Main(string[] args)
{
var collection = new Data[]
{
new Data() { Name = "A", Score = 90, },
new Data() { Name = "B", Score = 50, },
new Data() { Name = "C", Score = 30, },
};
var query = collection.Max();
query.Display();
}
}
}
単純にこうなります。と言いたいとこですが、これは動きません。
実行すると例外が発生すると思います。理由はMaxが対象とするのはData型だからです。
つまり、対象をData型の中に存在するScoreプロパティにする必要があります。
これをラムダ式は簡単に記述できます。こんな感じです。
namespace Sample
{
internal class Data
{
public string Name { get; set; }
public int Score { get; set; }
public void Display()
{
Console.WriteLine($"Name={this.Name}, Score={this.Score}");
}
}
internal class Program
{
static void Main(string[] args)
{
var collection = new Data[]
{
new Data() { Name = "A", Score = 90, },
new Data() { Name = "B", Score = 50, },
new Data() { Name = "C", Score = 30, },
};
var query = collection.Max(x => x.Score);
Console.WriteLine(query);
}
}
}
注意として、Maxで求めることができるのは最大値であり、最大値を持つオブジェクトではありません。
つまり、オブジェクトが所有するDisplayメソッドは利用できません。
ここで大切なのはLINQが求める型と返す型を理解することです。
LINQが難しいと感じる人は型への理解が甘いです。LINQを使う以前の問題です。
ここではC#の型について学びます。 型(type) の概念 変数や定数、評価式の結果など、C#では存在する値の全てに型があります。さらにC#は厳密に型指定された言語となり、異なる型の扱いに非常に厳しいです。 数値型で用意した変数に文字列を代入する処理を認める言語もありますが、C#ではこのような処理は認められません。故に型を理解することはC#では必須の知識になります。自分がどんな型を扱ってるか常に把握しましょう。 C#において型を間違えようものならボコボコにされます。 ...
後、各LINQの仕様は公式ドキュメントをどうぞ。
https://learn.microsoft.com/en-us/dotnet/api/system.linq
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable
遅延実行と即時実行
LINQには利用時の注意があります。それが遅延実行と即時実行です。
まず、LINQには記述した時点では評価(演算)されないメソッドが存在します。
それを遅延実行または即時実行で表現します。両方ともそのままの意味です。
ちなみに遅延実行がDeferred execution、即時実行がImmediate executionらしい。
分類は公式ドキュメントに書いてあります。
https://learn.microsoft.com/en-us/dotnet/csharp/linq/get-started/introduction-to-linq-queries#classification-table

値を求めるようなやつは即時実行で並び替えみたいなやつが遅延実行って感じです。
即時実行は書いてあるとおりなので説明不要と思います。
LINQを書いた時点で処理されます。注意が必要なのは遅延実行ですね。
この遅延実行を大雑把に説明すると、LINQが使われる時点まで処理されません。
次の例を見てください。これはリストの中身(3番目)を途中で消してます。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new List<int>() { 1, 2, 3, 4, 5 };
var query = collection.Where(x => x == 3);
collection.RemoveAt(2);
foreach (var d in query)
Console.WriteLine(d);
}
}
}
LINQを記述したのはリストの中身を削除する前です。にも関わらず、最後の表示では対象が消えてます。
つまり、実際の処理が行われたのはforeachを呼び出した瞬間となります。これが遅延実行です。
即時実行の強制
コーディングしてると遅延実行を気にするよりも、任意の時点で実行したい場面のほうが多いです。
これを解決するのが即時実行の強制です。呼び方があってるかは分かりませんが、そのままの意味です。
記述は遅延実行のクエリに続けて即時実行のクエリを書きます。
普通はTo〇〇となってるメソッドを使います。いわゆる変換用のメソッドですね。
これはToArrayなら配列に変換、ToListならリストに変換してくれます。
例えるならクエリ式を配列やリスト等の実オブジェクトに変換したと言えます。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var collection = new List<int>() { 1, 2, 3, 4, 5 };
// 即時実行の強制
var query = collection.Where(x => x == 3).ToList();
collection.RemoveAt(2);
foreach (var d in query)
Console.WriteLine(d);
}
}
}
POINTコレクションとして使い回したい場合も即時実行が便利です。
あとがき
LINQは超便利です。と言うか、何故LINQで書かないってソースが普通にあるある。
if文だと何行も必要な処理がLINQなら1行で書けます。後はグループ化も便利ですね。
◆ C#に関する学習コンテンツ
この記事は参考になりましたか?
コメント