C#のアロー演算子「=>」とは?ラムダ式・LINQ・式形式メンバーの使い方を初心者向けに解説

はじめに

C#のコードを読んでいると、次のような「=>」という記号をよく見かけます。

C#
var result = numbers.Where(x => x > 10);
C#
public int Double(int x) => x * 2;

この「=>」は、C#ではラムダ式や式形式メンバーで使われる重要な記号です。初心者のうちは「何をどこに渡しているのか」「= と何が違うのか」「LINQでなぜよく出てくるのか」が分かりにくいかもしれません。

この記事では、C#のアロー演算子「=>」について、ラムダ式、LINQ、式形式メンバーでの使い方を初心者向けに解説します。読み方やよくあるエラー、実践的なサンプルコードも紹介するので、C#の「=>」が出てきても迷わず読めるようになります。

1. C#のアロー演算子「=>」とは?

C#の「=>」は、主に「ラムダ式」と「式形式メンバー」で使われる記号です。

よく「アロー演算子」と呼ばれますが、C#の文脈では「ラムダ演算子」と呼ばれることもあります。コード上では、左側に入力や対象を書き、右側に処理や返す値を書く形で使われます。

たとえば、次のコードを見てください。

C#
x => x * 2

これは「xを受け取り、x * 2 を返す処理」と読めます。

つまり「=>」は、左側の値を使って右側の処理を行うための記号だと考えると理解しやすくなります。

1-1. 「=>」はラムダ式と式形式メンバーで使う記号

C#で「=>」がよく使われる代表的な場面は、次の2つです。

1つ目はラムダ式です。

C#
x => x + 1

これは、引数 x を受け取り、x + 1 を返す名前のない関数のようなものです。

2つ目は式形式メンバーです。

C#
public int Square(int x) => x * x;

これは、通常のメソッドを短く書いた形です。次のコードとほぼ同じ意味になります。

C#
public int Square(int x)
{
return x * x;
}

このように「=>」は、処理を簡潔に書くためによく使われます。

1-2. 「左側を右側の処理・値に渡す」と考えると理解しやすい

「=>」を理解するときは、まず次のように考えると分かりやすいです。

C#
左側 => 右側

左側には、処理に渡される値や引数を書きます。

右側には、その値を使った処理や返す値を書きます。

たとえば、次のラムダ式があります。

C#
x => x * 2

これは「xを受け取って、x * 2 を返す」と読めます。

もう少し具体的に見ると、x5 が入った場合、右側の x * 210 になります。

C#
Func<int, int> doubleNumber = x => x * 2;

Console.WriteLine(doubleNumber(5)); // 10

「=>」そのものが何かを計算しているというより、左側と右側をつなぎ、「この入力に対してこの処理をする」と表していると考えるとよいでしょう。

1-3. 「アロー演算子」「ラムダ演算子」と呼ばれる理由

「=>」は見た目が矢印のように見えるため、一般的に「アロー演算子」と呼ばれることがあります。

また、C#ではラムダ式で使われるため、「ラムダ演算子」と呼ばれることもあります。

C#
x => x > 0

この式は「xを受け取り、x > 0 という条件を返すラムダ式」です。

ただし、厳密には「=>」はラムダ式だけでなく、式形式メンバーやswitch式などでも使われます。そのため、初心者のうちは「アロー演算子」という呼び方でも問題ありませんが、公式ドキュメントや技術記事では「ラムダ演算子」「式本体定義」などの言葉も出てくることを覚えておきましょう。

1-4. 「=>」が使われる主な場面:ラムダ式・LINQ・式形式メンバー

C#の「=>」は、特に次の場面でよく登場します。

ラムダ式では、名前のない短い処理を書くときに使います。

C#
x => x * 2

LINQでは、配列やリストなどのコレクションから条件に合うデータを取り出したり、データを変換したりするときに使います。

C#
var adults = people.Where(p => p.Age >= 20);

式形式メンバーでは、メソッドやプロパティを短く書くときに使います。

C#
public string FullName => FirstName + " " + LastName;

この3つを押さえておけば、C#で見かける「=>」の多くを理解できるようになります。

2. まず押さえたい「=>」の基本構文

「=>」の基本はとてもシンプルです。

C#
引数 => 処理

または、

C#
引数 => 戻り値

と考えると分かりやすいです。

たとえば次のコードは、数値を受け取り、その数値を2倍にして返します。

C#
x => x * 2

この形を理解しておくと、LINQや式形式メンバーも読みやすくなります。

2-1. 基本形:引数 => 式

ラムダ式の基本形は次の通りです。

C#
引数 => 

例を見てみましょう。

C#
Func<int, int> square = x => x * x;

このコードでは、x => x * x がラムダ式です。

意味は「int型の値を1つ受け取り、その値を2乗して返す」です。

C#
Console.WriteLine(square(4)); // 16

x4 が渡されると、右側の x * x が実行され、結果として 16 が返ります。

2-2. 引数が1つの場合の書き方

引数が1つの場合は、型を省略して次のように書けます。

C#
x => x + 10

実際のコードでは次のようになります。

C#
Func<int, int> addTen = x => x + 10;

Console.WriteLine(addTen(5)); // 15

この場合、Func<int, int> から、C#コンパイラは「xはint型だ」と推論できます。そのため、次のように型を書かなくても動作します。

C#
x => x + 10

型を明示したい場合は、次のようにも書けます。

C#
Func<int, int> addTen = (int x) => x + 10;

引数が1つで型を省略する場合は、丸かっこを省略できるのがポイントです。

2-3. 引数が複数ある場合の書き方

引数が複数ある場合は、丸かっこが必要です。

C#
(x, y) => x + y

たとえば、2つの整数を受け取って合計を返すラムダ式は次のように書けます。

C#
Func<int, int, int> add = (x, y) => x + y;

Console.WriteLine(add(3, 4)); // 7

x3y4 が入り、右側の x + y が実行されます。

型を明示する場合は、次のように書きます。

C#
Func<int, int, int> add = (int x, int y) => x + y;

複数の引数がある場合は、型を省略しても丸かっこは省略できません。

2-4. 引数がない場合の書き方

引数がないラムダ式では、空の丸かっこを書きます。

C#
() => 処理

たとえば、現在時刻を返すラムダ式は次のように書けます。

C#
Func<DateTime> getNow = () => DateTime.Now;

Console.WriteLine(getNow());

戻り値がない処理の場合は、Action を使います。

C#
Action sayHello = () => Console.WriteLine("こんにちは");

sayHello();

引数がない場合でも、=> の左側には必ず () が必要です。

2-5. 式だけを書く場合とブロックを書く場合の違い

ラムダ式の右側には、1つの式を書くことも、複数行のブロックを書くこともできます。

1つの式だけを書く場合は、次のようにシンプルに書けます。

C#
Func<int, int> doubleNumber = x => x * 2;

この場合、右側の x * 2 の結果がそのまま戻り値になります。return は書きません。

一方、複数の処理を書きたい場合は { } を使ってブロック形式にします。

C#
Func<int, int> doubleNumber = x =>
{
int result = x * 2;
return result;
};

ブロック形式では、戻り値がある場合に return が必要です。

次のように return を書き忘れるとエラーになります。

C#
Func<int, int> doubleNumber = x =>
{
int result = x * 2;
// return result; が必要
};

短い処理なら式形式、複数行の処理ならブロック形式を使うとよいでしょう。

3. ラムダ式で使う「=>」の意味と使い方

C#の「=>」を理解するうえで、最も重要なのがラムダ式です。

ラムダ式は、名前のない関数を短く書くための構文です。特にLINQやイベント処理、コールバック処理などでよく使われます。

3-1. ラムダ式とは「名前のない関数」を短く書く方法

通常、C#で処理をまとめる場合はメソッドを定義します。

C#
int Double(int x)
{
return x * 2;
}

このメソッドは、次のようなラムダ式で短く表せます。

C#
x => x * 2

ラムダ式は「名前のない関数」のようなものです。

通常のメソッドには Double という名前がありますが、ラムダ式には名前がありません。その代わり、変数に代入したり、メソッドの引数として渡したりできます。

C#
Func<int, int> doubleNumber = x => x * 2;

このように、処理そのものを値のように扱えるのがラムダ式の特徴です。

3-2. 通常のメソッドとラムダ式の違い

通常のメソッドは、クラスの中に名前付きで定義します。

C#
public int Double(int x)
{
return x * 2;
}

呼び出すときは、メソッド名を使います。

C#
int result = Double(5);

一方、ラムダ式は名前を持たない処理です。

C#
Func<int, int> doubleNumber = x => x * 2;

int result = doubleNumber(5);

どちらも「5を受け取って10を返す」という意味では同じです。

ただし、ラムダ式は短い処理をその場で書きたいときに向いています。

特に、次のようなLINQのコードではラムダ式がよく使われます。

C#
var evenNumbers = numbers.Where(x => x % 2 == 0);

この場合、x => x % 2 == 0 は「xが偶数かどうかを判定する処理」です。

3-3. Funcでラムダ式を変数に代入する例

戻り値があるラムダ式を変数に代入するときは、よく Func を使います。

C#
Func<int, int> doubleNumber = x => x * 2;

Func<int, int> は、「int型の引数を1つ受け取り、int型の値を返す処理」を表します。

C#
Console.WriteLine(doubleNumber(10)); // 20

引数が2つある場合は、次のように書きます。

C#
Func<int, int, int> add = (x, y) => x + y;

Console.WriteLine(add(3, 5)); // 8

Func<int, int, int> の最後の int は戻り値の型です。

つまり、次のように読みます。

C#
Func<第1引数の型, 第2引数の型, 戻り値の型>

そのため、Func<int, int, int> は「intを2つ受け取り、intを返す処理」です。

3-4. Actionで戻り値のない処理を書く例

戻り値がないラムダ式には Action を使います。

C#
Action sayHello = () => Console.WriteLine("こんにちは");

sayHello();

このコードは、引数も戻り値もない処理です。

引数がある場合は、次のように書きます。

C#
Action<string> greet = name => Console.WriteLine($"こんにちは、{name}さん");

greet("田中");

Action<string> は「string型の引数を1つ受け取り、戻り値は返さない処理」を表します。

複数の引数を受け取ることもできます。

C#
Action<string, int> introduce = (name, age) =>
{
Console.WriteLine($"{name}さんは{age}歳です");
};

introduce("佐藤", 25);

戻り値がある場合は Func、戻り値がない場合は Action と覚えると分かりやすいです。

3-5. Predicateで条件判定を書く例

条件判定を表すラムダ式では、Predicate<T> が使われることもあります。

Predicate<T> は、「T型の値を受け取り、bool型を返す処理」です。

C#
Predicate<int> isEven = x => x % 2 == 0;

Console.WriteLine(isEven(4)); // True
Console.WriteLine(isEven(5)); // False

このラムダ式は、「xが偶数ならtrue、そうでなければfalseを返す」という意味です。

文字列に対する条件判定も書けます。

C#
Predicate<string> isLongName = name => name.Length >= 5;

Console.WriteLine(isLongName("Taro")); // False
Console.WriteLine(isLongName("Tanaka")); // True

LINQの Where では Predicate<T> そのものではなく、主に Func<T, bool> が使われますが、考え方はほとんど同じです。

C#
Func<int, bool> isEven = x => x % 2 == 0;

条件判定のラムダ式では、右側が true または false になる式になっていることが重要です。

3-6. 型推論により引数の型を省略できる理由

ラムダ式では、引数の型を省略できることが多いです。

C#
Func<int, int> doubleNumber = x => x * 2;

ここでは x の型を書いていませんが、C#は Func<int, int> を見て、xint 型だと判断できます。

次のように型を明示しても同じ意味です。

C#
Func<int, int> doubleNumber = (int x) => x * 2;

LINQでも型推論が働きます。

C#
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };

var longNames = names.Where(name => name.Length >= 5);

namesList<string> なので、Where の中の namestring 型だと推論されます。

そのため、name.Length のように文字列のプロパティを使えます。

型推論があるおかげで、ラムダ式は短く読みやすく書けます。

4. LINQでよく見る「=>」の使い方

C#の「=>」を実際のコードでよく見る場面がLINQです。

LINQでは、リストや配列などのデータに対して、絞り込み、変換、並び替え、検索などを簡潔に書けます。

たとえば、次のようなコードです。

C#
var adults = people.Where(p => p.Age >= 20);

この中の p => p.Age >= 20 がラムダ式です。

4-1. LINQとはコレクションを簡潔に操作する機能

LINQは、配列やリストなどのコレクションを簡潔に操作するための機能です。

たとえば、次のような数値リストがあるとします。

C#
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

この中から偶数だけを取り出す場合、通常の foreach では次のように書けます。

C#
List<int> evenNumbers = new List<int>();

foreach (int number in numbers)
{
if (number % 2 == 0)
{
evenNumbers.Add(number);
}
}

LINQを使うと、次のように短く書けます。

C#
var evenNumbers = numbers.Where(number => number % 2 == 0);

number => number % 2 == 0 は、「numberが偶数かどうかを判定する処理」です。

LINQでは、このような条件や変換処理をラムダ式で書くことが多いです。

4-2. Whereで条件に合う要素を絞り込む

Where は、条件に合う要素だけを取り出すためのLINQメソッドです。

C#
var evenNumbers = numbers.Where(x => x % 2 == 0);

このコードは、「numbersの中から、x % 2 == 0 を満たす要素だけを取り出す」という意味です。

具体例を見てみましょう。

C#
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

var evenNumbers = numbers.Where(x => x % 2 == 0);

foreach (int number in evenNumbers)
{
Console.WriteLine(number);
}

実行結果は次の通りです。

C#
2
4
6

x には、リストの要素が1つずつ入ります。

最初は 1、次に 2、次に 3 というように順番に判定されます。

x % 2 == 0true になった要素だけが結果に残ります。

4-3. Selectで要素を変換する

Select は、コレクションの各要素を別の形に変換するためのLINQメソッドです。

C#
var doubled = numbers.Select(x => x * 2);

このコードは、「numbersの各要素を2倍に変換する」という意味です。

C#
List<int> numbers = new List<int> { 1, 2, 3 };

var doubled = numbers.Select(x => x * 2);

foreach (int number in doubled)
{
Console.WriteLine(number);
}

実行結果は次の通りです。

C#
2
4
6

オブジェクトの一覧から特定のプロパティだけを取り出すときにも Select はよく使われます。

C#
var names = people.Select(p => p.Name);

これは「peopleの各要素からNameだけを取り出す」という意味です。

4-4. OrderByで並び替える

OrderBy は、指定した値を基準に昇順で並び替えるメソッドです。

C#
var sortedPeople = people.OrderBy(p => p.Age);

このコードは、「peopleをAgeの小さい順に並び替える」という意味です。

例を見てみましょう。

C#
var people = new List<Person>
{
new Person { Name = "田中", Age = 30 },
new Person { Name = "佐藤", Age = 20 },
new Person { Name = "鈴木", Age = 25 }
};

var sortedPeople = people.OrderBy(p => p.Age);

foreach (var person in sortedPeople)
{
Console.WriteLine($"{person.Name}: {person.Age}");
}

実行結果は次のようになります。

C#
佐藤: 20
鈴木: 25
田中: 30

降順にしたい場合は、OrderByDescending を使います。

C#
var sortedPeople = people.OrderByDescending(p => p.Age);

4-5. FirstOrDefaultで条件に合う最初の要素を取得する

FirstOrDefault は、条件に合う最初の要素を取得するメソッドです。

C#
var adult = people.FirstOrDefault(p => p.Age >= 20);

このコードは、「Ageが20以上の最初の人を取得する」という意味です。

条件に合う要素が見つからない場合は、型に応じた既定値が返ります。参照型の場合は null が返ります。

C#
var person = people.FirstOrDefault(p => p.Name == "山田");

if (person != null)
{
Console.WriteLine(person.Name);
}
else
{
Console.WriteLine("見つかりませんでした");
}

FirstOrDefault を使うときは、結果が null になる可能性を考えておくことが大切です。

4-6. Count・Any・Allで条件判定する

LINQでは、条件に合う要素の数を数えたり、条件を満たす要素があるかを調べたりできます。

Count は、条件に合う要素数を数えます。

C#
int adultCount = people.Count(p => p.Age >= 20);

これは「20歳以上の人の数を数える」という意味です。

Any は、条件に合う要素が1つでもあるかを判定します。

C#
bool hasAdult = people.Any(p => p.Age >= 20);

これは「20歳以上の人が1人でもいるか」を調べます。

All は、すべての要素が条件を満たすかを判定します。

C#
bool allAdults = people.All(p => p.Age >= 20);

これは「すべての人が20歳以上か」を調べます。

それぞれの違いをまとめると、次のようになります。

C#
Count: 条件に合う数を返す
Any: 条件に合う要素が1つでもあればtrue
All: すべての要素が条件に合えばtrue

4-7. LINQの「x => x.Property」が読めるようになる考え方

LINQでは、次のような書き方をよく見ます。

C#
p => p.Name

初心者のうちは、この p が何なのか分かりにくいかもしれません。

p は、コレクションの中の要素を1つずつ表す仮の名前です。

たとえば、次のコードを見てください。

C#
var names = people.Select(p => p.Name);

この場合、people の中にある Person オブジェクトが1つずつ p に入ります。

そして、右側の p.Name によって、その人の名前だけを取り出しています。

つまり、次のように読めます。

C#
p => p.Name

「pを受け取って、pのNameを返す」

同じように、次のコードはこう読めます。

C#
p => p.Age >= 20

「pを受け取って、pのAgeが20以上かどうかを返す」

この読み方に慣れると、LINQのコードが一気に読みやすくなります。

5. 式形式メンバーで使う「=>」の意味と使い方

「=>」はラムダ式だけでなく、式形式メンバーでも使われます。

式形式メンバーとは、メソッドやプロパティなどを短く書くための構文です。

たとえば、次のようなメソッドがあります。

C#
public int Double(int x)
{
return x * 2;
}

これを式形式メンバーで書くと、次のようになります。

C#
public int Double(int x) => x * 2;

短い処理であれば、式形式メンバーを使うことでコードをすっきり書けます。

5-1. 式形式メンバーとはメソッドやプロパティを短く書く構文

式形式メンバーは、1つの式で表せるメンバーを短く書くための構文です。

通常のメソッドでは、{ } の中に処理を書きます。

C#
public string GetMessage()
{
return "Hello";
}

式形式メンバーでは、=> を使って次のように書けます。

C#
public string GetMessage() => "Hello";

この場合、右側の "Hello" がそのまま戻り値になります。

プロパティでも使えます。

C#
public string FullName => FirstName + " " + LastName;

これは、FullName プロパティを参照したときに、FirstName + " " + LastName の結果を返すという意味です。

5-2. メソッドを「=>」で1行にする例

短いメソッドは、式形式メンバーを使うと1行で書けます。

C#
public int Add(int a, int b) => a + b;

これは次の通常のメソッドと同じ意味です。

C#
public int Add(int a, int b)
{
return a + b;
}

文字列を返すメソッドも書けます。

C#
public string Greet(string name) => $"こんにちは、{name}さん";

戻り値がないメソッドにも使えます。

C#
public void PrintMessage(string message) => Console.WriteLine(message);

ただし、式形式メンバーは基本的に1つの式で書ける処理に向いています。複雑な処理を無理に1行にまとめると、かえって読みにくくなります。

5-3. 読み取り専用プロパティを「=>」で書く例

式形式メンバーは、読み取り専用プロパティを書くときにも便利です。

C#
public string FullName => FirstName + " " + LastName;

これは、次のようなプロパティと似た意味です。

C#
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}

より短く書くなら、次のようになります。

C#
public string FullName => $"{FirstName} {LastName}";

サンプルクラス全体で見ると、次のようになります。

C#
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }

public string FullName => $"{FirstName} {LastName}";
}

この場合、FullName は値を保存しているのではなく、呼び出されるたびに FirstNameLastName から計算されます。

5-4. コンストラクター・インデクサー・演算子で使う例

式形式メンバーは、メソッドやプロパティ以外にも使えます。

たとえば、コンストラクターでも使えます。

C#
public class Person
{
public string Name { get; }

public Person(string name) => Name = name;
}

このコードは、コンストラクターの中で Name = name; を実行しています。

インデクサーでも使えます。

C#
public class SampleList
{
private string[] items = { "A", "B", "C" };

public string this[int index] => items[index];
}

演算子のオーバーロードでも使えます。

C#
public class Point
{
public int X { get; }
public int Y { get; }

public Point(int x, int y)
{
X = x;
Y = y;
}

public static Point operator +(Point a, Point b)
=> new Point(a.X + b.X, a.Y + b.Y);
}

このように、短く表せるメンバーであれば、さまざまな場所で「=>」を使えます。

5-5. 通常のメソッド構文との書き換え比較

通常のメソッド構文と式形式メンバーを比較してみましょう。

通常の書き方です。

C#
public int Square(int x)
{
return x * x;
}

式形式メンバーで書くと、次のようになります。

C#
public int Square(int x) => x * x;

プロパティも比較してみます。

通常の書き方です。

C#
public bool IsAdult
{
get
{
return Age >= 20;
}
}

式形式メンバーで書くと、次のようになります。

C#
public bool IsAdult => Age >= 20;

どちらも同じ意味ですが、処理が短い場合は式形式メンバーの方がすっきりします。

ただし、処理内容が複雑な場合は、通常のメソッド構文の方が読みやすいこともあります。

5-6. 式形式メンバーが向いている処理・向いていない処理

式形式メンバーが向いているのは、次のような処理です。

C#
public int Double(int x) => x * 2;
C#
public bool IsAdult => Age >= 20;
C#
public string FullName => $"{FirstName} {LastName}";

このように、1行で意味が分かる処理には向いています。

一方で、次のような処理にはあまり向いていません。

C#
public string CreateReport()
{
var data = LoadData();
var filtered = FilterData(data);
var report = BuildReport(filtered);

return report;
}

複数の手順がある処理を無理に式形式メンバーにすると、読みづらくなります。

式形式メンバーは「短く書けるから使う」のではなく、「短く書いても分かりやすいときに使う」と考えましょう。

6. 「=>」と混同しやすい記号の違い

C#には、「=>」と見た目が似ている記号がいくつかあります。

特に初心者が混同しやすいのは、次の記号です。

C#
=
=>
->
<=
>=

それぞれ意味がまったく違うため、違いを整理しておきましょう。

6-1. 「=>」と「=」の違い

= は代入に使う記号です。

C#
int x = 10;

これは「xに10を代入する」という意味です。

一方、=> はラムダ式や式形式メンバーで使います。

C#
Func<int, int> doubleNumber = x => x * 2;

このコードには、==> の両方が出てきます。

C#
Func<int, int> doubleNumber = x => x * 2;

左側の = は、「変数 doubleNumber にラムダ式を代入する」という意味です。

右側の => は、「xを受け取って x * 2 を返す」という意味です。

つまり、= は代入、=> は処理の対応関係を表す記号です。

6-2. 「=>」と「->」の違い

C#では、通常のラムダ式に -> は使いません。

C#のラムダ式は次のように書きます。

C#
x => x * 2

JavaやC++など、別の言語では -> が使われる場面があります。

たとえばJavaのラムダ式では、次のように -> を使います。

Java
x -> x * 2

しかし、C#ではこの書き方はできません。

C#
// C#ではエラー
x -> x * 2

C#でラムダ式を書くときは、必ず => を使うと覚えておきましょう。

6-3. 「=>」と「<=」「>=」の違い

<=>= は比較演算子です。

<= は「以下」を表します。

C#
x <= 10

これは「xが10以下」という意味です。

>= は「以上」を表します。

C#
x >= 20

これは「xが20以上」という意味です。

一方、=> はラムダ式や式形式メンバーで使います。

C#
x => x >= 20

このコードでは、=>>= が両方出てきます。

意味は「xを受け取り、xが20以上かどうかを返す」です。

=> はラムダ式の記号、>= は比較演算子です。見た目は似ていますが、役割はまったく違います。

6-4. C#の「=>」とJavaScriptのアロー関数の違い

JavaScriptにもアロー関数があります。

JavaScript
const double = x => x * 2;

見た目はC#のラムダ式とよく似ています。

C#でも次のように書けます。

C#
Func<int, int> doubleNumber = x => x * 2;

ただし、C#とJavaScriptでは型の扱いが大きく違います。

C#では、ラムダ式を代入する変数の型が必要です。

C#
Func<int, int> doubleNumber = x => x * 2;

または、LINQのようにメソッドの引数から型が推論される必要があります。

C#
var result = numbers.Where(x => x > 0);

一方、JavaScriptは動的型付けの言語なので、C#のような Func<int, int> は書きません。

見た目は似ていますが、C#では型推論やデリゲート型とセットで理解することが大切です。

6-5. C#の「=>」とJavaのラムダ式「->」の違い

Javaのラムダ式では、-> を使います。

Java
x -> x * 2

C#のラムダ式では、=> を使います。

C#
x => x * 2

どちらも「引数を受け取り、処理を返す」という考え方は似ています。

ただし、記号が違います。

C#を書いているときにJavaの感覚で -> を書くとエラーになります。

C#
// C#ではエラー
Func<int, int> doubleNumber = x -> x * 2;

C#では次のように書きます。

C#
Func<int, int> doubleNumber = x => x * 2;

他の言語を学んだことがある人ほど混同しやすいので注意しましょう。

7. 初心者がつまずきやすい「=>」の読み方

「=>」を理解するには、実際のコードをどう読むかが重要です。

最初は難しく感じるかもしれませんが、基本は次の形です。

C#
入力 => 結果

または、

C#
対象 => 条件

と考えると読みやすくなります。

7-1. 「x => x * 2」はどう読むのか

次のラムダ式を見てください。

C#
x => x * 2

これは、次のように読みます。

「xを受け取って、x * 2 を返す」

たとえば、x3 が入れば、結果は 6 です。

x10 が入れば、結果は 20 です。

コードにすると、次のようになります。

C#
Func<int, int> doubleNumber = x => x * 2;

Console.WriteLine(doubleNumber(3)); // 6
Console.WriteLine(doubleNumber(10)); // 20

左側の x は入力、右側の x * 2 は結果です。

7-2. 「p => p.Name」はどう読むのか

次のラムダ式を見てください。

C#
p => p.Name

これは、次のように読みます。

「pを受け取って、pのNameを返す」

たとえば、pPerson 型のオブジェクトだとします。

C#
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

次のように Select で使えます。

C#
var names = people.Select(p => p.Name);

これは、「peopleの中にある各PersonからNameだけを取り出す」という意味です。

p は特別な名前ではありません。person と書いても同じです。

C#
var names = people.Select(person => person.Name);

初心者のうちは、短い p よりも person のような分かりやすい名前を使うと読みやすくなります。

7-3. 「items.Where(x => x.Age >= 20)」はどう読むのか

次のコードを見てください。

C#
var adults = items.Where(x => x.Age >= 20);

これは、次のように読みます。

「itemsの中から、xのAgeが20以上のものだけを取り出す」

もう少し細かく分けると、次のようになります。

C#
items.Where(条件)

Where は条件に合う要素だけを絞り込むメソッドです。

その条件が次のラムダ式です。

C#
x => x.Age >= 20

これは、「xを受け取り、x.Age >= 20 の結果を返す」という意味です。

x.Age >= 20true または false になります。

true になった要素だけが、adults に含まれます。

7-4. 「引数」「戻り値」「条件式」の関係を図解で理解する

ラムダ式は、次のように考えると分かりやすいです。

C#
x => x * 2

図のように分解できます。

C#
入力        処理・結果
x => x * 2

x5 の場合は、次のようになります。

C#
5 => 5 * 2 => 10

条件式の場合も同じです。

C#
x => x >= 20

x25 の場合です。

C#
25 => 25 >= 20 => true

x18 の場合です。

C#
18 => 18 >= 20 => false

LINQの Where では、この true または false の結果を使って、要素を残すかどうかを決めています。

7-5. ネストしたラムダ式を読むときのコツ

実際のコードでは、ラムダ式がネストしていることもあります。

C#
var result = groups.Select(g => g.Items.Where(item => item.Price > 1000));

このようなコードは、内側から読むと理解しやすくなります。

まず、内側のラムダ式を見ます。

C#
item => item.Price > 1000

これは、「itemのPriceが1000より大きいか」を判定しています。

次に、外側のラムダ式を見ます。

C#
g => g.Items.Where(item => item.Price > 1000)

これは、「gのItemsの中から、Priceが1000より大きいitemだけを取り出す」という意味です。

ネストしたラムダ式を読むコツは、次の3つです。

まず、=> の左側が何を表しているかを確認します。

次に、右側で何を返しているかを確認します。

最後に、外側からではなく、内側の小さなラムダ式から意味を整理します。

変数名が短すぎて分かりにくい場合は、次のように書き換えると読みやすくなります。

C#
var result = groups.Select(group =>
group.Items.Where(item => item.Price > 1000));

g よりも groupx よりも item のように、意味のある名前を使うと理解しやすくなります。

8. 「=>」でよくあるエラーと対処法

C#の「=>」は便利ですが、初心者がつまずきやすいポイントもあります。

特に多いのは、型推論ができない、戻り値の型が合わない、ブロック形式で return を忘れる、LINQで null を扱ってエラーになる、というケースです。

ここでは、よくあるエラーと対処法を紹介します。

8-1. ラムダ式の引数の型が推論できない

ラムダ式は、代入先やメソッドの引数から型を推論します。

そのため、型の情報がない場所ではエラーになることがあります。

たとえば、次のようなコードはエラーになります。

C#
var func = x => x * 2;

var だけでは、x が何型なのか、戻り値が何型なのかを判断できません。

この場合は、明示的に Func を使います。

C#
Func<int, int> func = x => x * 2;

または、引数の型を明示しても、代入先のデリゲート型は必要です。

C#
Func<int, int> func = (int x) => x * 2;

ラムダ式を書くときは、「このラムダ式がどの型として扱われるのか」を意識しましょう。

8-2. 戻り値の型が一致しない

Func を使う場合、戻り値の型が一致していないとエラーになります。

次のコードを見てください。

C#
Func<int, int> func = x => "結果は" + x;

Func<int, int> は「intを受け取り、intを返す処理」です。

しかし、右側の "結果は" + xstring 型です。

そのため、戻り値の型が一致せずエラーになります。

修正するには、戻り値の型を合わせます。

C#
Func<int, string> func = x => "結果は" + x;

または、右側を int にします。

C#
Func<int, int> func = x => x * 2;

ラムダ式では、右側の式の型が戻り値の型になります。Func の最後の型と一致しているか確認しましょう。

8-3. ブロック形式のラムダ式でreturnを書き忘れる

式形式のラムダ式では、return は不要です。

C#
Func<int, int> doubleNumber = x => x * 2;

しかし、ブロック形式にした場合は、戻り値があるなら return が必要です。

次のコードはエラーになります。

C#
Func<int, int> doubleNumber = x =>
{
int result = x * 2;
};

Func<int, int>int を返す必要がありますが、ブロックの中で何も返していません。

正しくは次のように書きます。

C#
Func<int, int> doubleNumber = x =>
{
int result = x * 2;
return result;
};

式形式なら return 不要、ブロック形式なら return が必要、と覚えておきましょう。

8-4. LINQでnull参照が発生する

LINQでラムダ式を使っていると、null 参照でエラーになることがあります。

たとえば、次のコードです。

C#
var result = people.Where(p => p.Name.Length >= 5);

もし p.Namenull の要素があると、p.Name.Length の部分でエラーになります。

対処法として、nullチェックを追加します。

C#
var result = people.Where(p => p.Name != null && p.Name.Length >= 5);

C#のnull条件演算子を使う方法もあります。

C#
var result = people.Where(p => p.Name?.Length >= 5);

ただし、条件式の型やプロジェクトの設定によっては、明示的に書いた方が分かりやすい場合もあります。

C#
var result = people.Where(p => !string.IsNullOrEmpty(p.Name) && p.Name.Length >= 5);

LINQのラムダ式では、対象のプロパティが null になる可能性があるかを意識しましょう。

8-5. 式形式メンバーで複数行処理を書こうとしてエラーになる

式形式メンバーは、基本的に1つの式を書くための構文です。

次のように複数行の処理を直接書くことはできません。

C#
public int Calculate(int x) =>
{
int result = x * 2;
return result;
};

これはエラーになります。

複数行の処理を書きたい場合は、通常のメソッド構文を使います。

C#
public int Calculate(int x)
{
int result = x * 2;
return result;
}

式形式メンバーは次のように、1つの式で表せる処理に使います。

C#
public int Calculate(int x) => x * 2;

「=>」を使えば何でも短く書けるわけではありません。複数の処理が必要な場合は、通常のメソッドにしましょう。

8-6. デリゲート・Func・Actionの違いが分からない場合の確認ポイント

ラムダ式を学んでいると、デリゲート、FuncAction という言葉が出てきます。

まず、デリゲートは「メソッドや処理を入れられる型」です。

Func は、戻り値がある処理を表します。

C#
Func<int, int> doubleNumber = x => x * 2;

Action は、戻り値がない処理を表します。

C#
Action<string> print = message => Console.WriteLine(message);

Predicate<T> は、条件判定に使われる処理を表します。

C#
Predicate<int> isEven = x => x % 2 == 0;

迷ったときは、次のように確認しましょう。

戻り値があるなら Func

戻り値がないなら Action

条件判定で bool を返すなら Predicate<T> または Func<T, bool>

LINQでは主に Func<T, bool>Func<T, TResult> が使われます。

9. 「=>」を使うメリットと注意点

C#の「=>」を使うと、コードを短く簡潔に書けます。

特にLINQと組み合わせると、条件や変換処理を非常に読みやすく書けます。

ただし、何でも短くすればよいわけではありません。処理が複雑な場合は、通常のメソッドに分けた方が読みやすくなります。

9-1. コードを短く読みやすくできる

「=>」を使う大きなメリットは、短い処理を簡潔に書けることです。

通常のメソッドでは、次のように書きます。

C#
public int Double(int x)
{
return x * 2;
}

式形式メンバーを使うと、次のように書けます。

C#
public int Double(int x) => x * 2;

ラムダ式でも、短い条件や変換処理をすっきり書けます。

C#
var evenNumbers = numbers.Where(x => x % 2 == 0);

このように、処理の意味がすぐ分かる場合は、「=>」を使うことで読みやすさが上がります。

9-2. LINQと組み合わせると条件・変換処理を簡潔に書ける

LINQでは、「=>」を使うことで、条件や変換処理をその場で書けます。

たとえば、20歳以上の人だけを取り出すコードです。

C#
var adults = people.Where(p => p.Age >= 20);

名前だけを取り出すコードです。

C#
var names = people.Select(p => p.Name);

年齢順に並び替えるコードです。

C#
var sorted = people.OrderBy(p => p.Age);

これらを通常の foreach で書くと、コード量が増えます。

LINQとラムダ式を使うことで、「何をしたいか」がコード上で分かりやすくなります。

9-3. 短すぎるラムダ式は処理内容が分かりにくくなる

「=>」は便利ですが、短くしすぎると逆に読みにくくなることがあります。

たとえば、次のコードです。

C#
var result = items.Where(x => x.A > 10 && x.B != null && x.B.C.StartsWith("A"));

処理の内容が複雑になると、x が何を表しているのか、条件が何を意味しているのか分かりにくくなります。

このような場合は、変数名を分かりやすくするだけでも読みやすくなります。

C#
var result = items.Where(item =>
item.A > 10 &&
item.B != null &&
item.B.C.StartsWith("A"));

さらに複雑な場合は、条件をメソッドに分けるのもよい方法です。

C#
var result = items.Where(IsTargetItem);
C#
private bool IsTargetItem(Item item)
{
return item.A > 10 &&
item.B != null &&
item.B.C.StartsWith("A");
}

短さよりも分かりやすさを優先しましょう。

9-4. 複雑な処理は通常のメソッドに分ける

ラムダ式や式形式メンバーは、短い処理に向いています。

しかし、複雑な処理を無理にラムダ式で書くと、読みづらくなります。

たとえば、次のようなコードです。

C#
var result = orders.Where(order =>
{
var total = order.Items.Sum(item => item.Price);
var hasStock = order.Items.All(item => item.Stock > 0);
return total >= 10000 && hasStock;
});

この程度なら読める場合もありますが、処理がさらに増えるならメソッドに分けた方がよいです。

C#
var result = orders.Where(IsValidOrder);
C#
private bool IsValidOrder(Order order)
{
var total = order.Items.Sum(item => item.Price);
var hasStock = order.Items.All(item => item.Stock > 0);

return total >= 10000 && hasStock;
}

メソッド名を付けることで、処理の意図が分かりやすくなります。

9-5. チーム開発で読みやすさを優先する判断基準

チーム開発では、自分だけでなく他のメンバーが読みやすいコードを書くことが重要です。

「=>」を使うかどうか迷ったら、次の基準で判断するとよいでしょう。

まず、1行で意味が分かる処理なら「=>」を使っても問題ありません。

C#
public bool IsAdult => Age >= 20;

次に、条件が長すぎる場合は、改行したりメソッドに分けたりします。

C#
var adults = people.Where(person => person.Age >= 20);

また、xp のような短い変数名で意味が分かりにくい場合は、personorder のような名前にします。

C#
var adults = people.Where(person => person.Age >= 20);

チーム内でコーディング規約がある場合は、それに従いましょう。

大切なのは、「短く書くこと」ではなく「他の人がすぐ理解できること」です。

10. 実践:アロー演算子「=>」を使ったサンプルコード

ここからは、C#のアロー演算子「=>」を使った実践的なサンプルコードを紹介します。

数値リストの絞り込み、オブジェクト一覧から名前を取り出す処理、年齢順の並び替え、条件判定、式形式メンバーへの書き換えを順番に見ていきましょう。

10-1. 数値リストを条件で絞り込むサンプル

数値リストから、10以上の値だけを取り出す例です。

C#
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
static void Main()
{
List<int> numbers = new List<int> { 3, 10, 15, 7, 20 };

var result = numbers.Where(x => x >= 10);

foreach (int number in result)
{
Console.WriteLine(number);
}
}
}

実行結果は次の通りです。

C#
10
15
20

この中のラムダ式は次の部分です。

C#
x => x >= 10

「xを受け取り、xが10以上かどうかを返す」と読みます。

Where は、この条件が true になった要素だけを取り出します。

10-2. オブジェクト一覧から名前だけを取り出すサンプル

次は、Person オブジェクトの一覧から名前だけを取り出す例です。

C#
using System;
using System.Collections.Generic;
using System.Linq;

class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

class Program
{
static void Main()
{
var people = new List<Person>
{
new Person { Name = "田中", Age = 30 },
new Person { Name = "佐藤", Age = 20 },
new Person { Name = "鈴木", Age = 25 }
};

var names = people.Select(person => person.Name);

foreach (string name in names)
{
Console.WriteLine(name);
}
}
}

実行結果は次の通りです。

C#
田中
佐藤
鈴木

このラムダ式は次の部分です。

C#
person => person.Name

「personを受け取り、personのNameを返す」という意味です。

Select は、各要素を別の形に変換するときに使います。

10-3. 年齢順に並び替えるサンプル

次は、Person の一覧を年齢順に並び替える例です。

C#
using System;
using System.Collections.Generic;
using System.Linq;

class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

class Program
{
static void Main()
{
var people = new List<Person>
{
new Person { Name = "田中", Age = 30 },
new Person { Name = "佐藤", Age = 20 },
new Person { Name = "鈴木", Age = 25 }
};

var sortedPeople = people.OrderBy(person => person.Age);

foreach (Person person in sortedPeople)
{
Console.WriteLine($"{person.Name}: {person.Age}");
}
}
}

実行結果は次の通りです。

C#
佐藤: 20
鈴木: 25
田中: 30

このラムダ式は次の部分です。

C#
person => person.Age

「personを受け取り、personのAgeを返す」という意味です。

OrderBy は、返された値を基準に並び替えます。

10-4. 条件に合うデータが存在するか確認するサンプル

次は、条件に合うデータが存在するかを Any で確認する例です。

C#
using System;
using System.Collections.Generic;
using System.Linq;

class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

class Program
{
static void Main()
{
var people = new List<Person>
{
new Person { Name = "田中", Age = 30 },
new Person { Name = "佐藤", Age = 20 },
new Person { Name = "鈴木", Age = 25 }
};

bool hasTeenager = people.Any(person => person.Age < 20);

Console.WriteLine(hasTeenager);
}
}

実行結果は次の通りです。

C#
False

このラムダ式は次の部分です。

C#
person => person.Age < 20

「personを受け取り、personのAgeが20未満かどうかを返す」という意味です。

Any は、条件に合う要素が1つでもあれば true を返します。1つもなければ false を返します。

10-5. メソッドを式形式メンバーに書き換えるサンプル

最後に、通常のメソッドを式形式メンバーに書き換える例を見てみましょう。

通常のメソッドです。

C#
public int Double(int x)
{
return x * 2;
}

式形式メンバーにすると、次のようになります。

C#
public int Double(int x) => x * 2;

プロパティも書き換えられます。

通常のプロパティです。

C#
public bool IsAdult
{
get
{
return Age >= 20;
}
}

式形式メンバーにすると、次のようになります。

C#
public bool IsAdult => Age >= 20;

クラス全体で見ると、次のようになります。

C#
public class Person
{
public string Name { get; set; }
public int Age { get; set; }

public bool IsAdult => Age >= 20;

public string GetProfile() => $"{Name}さんは{Age}歳です";
}

IsAdult は、Age >= 20 の結果を返す読み取り専用プロパティです。

GetProfile は、文字列を返すメソッドです。

どちらも短く分かりやすい処理なので、式形式メンバーに向いています。

11. C#のアロー演算子「=>」に関するFAQ

ここでは、C#のアロー演算子「=>」について、初心者が疑問に思いやすい点をFAQ形式で解説します。

11-1. 「=>」は演算子ですか?

C#では「=>」はラムダ式で使われるラムダ演算子として扱われます。

また、式形式メンバーでは、式本体定義を表すために使われます。

一般的には「アロー演算子」と呼ばれることもありますが、C#ではラムダ式や式形式メンバーを理解するための記号として覚えるとよいでしょう。

たとえば、次のコードではラムダ式として使われています。

C#
x => x * 2

次のコードでは式形式メンバーとして使われています。

C#
public int Double(int x) => x * 2;

11-2. 「=>」はラムダ式専用ですか?

いいえ、ラムダ式専用ではありません。

「=>」はラムダ式でよく使われますが、式形式メンバーでも使われます。

ラムダ式の例です。

C#
Func<int, int> doubleNumber = x => x * 2;

式形式メンバーの例です。

C#
public int Double(int x) => x * 2;

また、C#のswitch式などでも => が使われます。

C#
string result = score switch
{
>= 80 => "合格",
_ => "不合格"
};

ただし、初心者がまず覚えるべきなのは、ラムダ式、LINQ、式形式メンバーでの使い方です。

11-3. 「=>」の左側には何を書きますか?

ラムダ式の場合、「=>」の左側には引数を書きます。

C#
x => x * 2

この場合、左側の x が引数です。

引数が複数ある場合は、丸かっこで囲みます。

C#
(x, y) => x + y

引数がない場合は、空の丸かっこを書きます。

C#
() => DateTime.Now

式形式メンバーの場合は、「=>」の左側にはメソッドやプロパティの定義を書きます。

C#
public int Double(int x) => x * 2;

この場合、左側の public int Double(int x) がメソッド定義です。

11-4. 「=>」の右側には何を書きますか?

ラムダ式の場合、「=>」の右側には処理や戻り値を書きます。

C#
x => x * 2

この場合、右側の x * 2 が戻り値になります。

条件式を書くこともあります。

C#
x => x >= 20

この場合、右側は true または false を返します。

複数行の処理を書きたい場合は、ブロック形式にします。

C#
x =>
{
int result = x * 2;
return result;
}

式形式メンバーの場合、右側には1つの式を書きます。

C#
public int Double(int x) => x * 2;

複雑な複数行処理を書きたい場合は、通常のメソッド構文を使いましょう。

11-5. ラムダ式と匿名メソッドの違いは何ですか?

ラムダ式と匿名メソッドは、どちらも名前のない処理を書くための方法です。

匿名メソッドは、次のように delegate キーワードを使います。

C#
Func<int, int> doubleNumber = delegate (int x)
{
return x * 2;
};

ラムダ式では、次のように短く書けます。

C#
Func<int, int> doubleNumber = x => x * 2;

どちらも同じような処理を表せますが、現在のC#ではラムダ式の方が簡潔でよく使われます。

特にLINQでは、ラムダ式を使うのが一般的です。

C#
var evenNumbers = numbers.Where(x => x % 2 == 0);

初心者は、まずラムダ式を中心に覚えるとよいでしょう。

11-6. 初心者はどこから覚えればいいですか?

初心者は、まず次の3つを覚えるのがおすすめです。

まず、基本形です。

C#
引数 => 戻り値

たとえば、次のラムダ式です。

C#
x => x * 2

これは「xを受け取り、x * 2 を返す」と読みます。

次に、LINQの Where で使う形です。

C#
numbers.Where(x => x % 2 == 0)

これは「numbersの中から偶数だけを取り出す」と読みます。

最後に、式形式メンバーです。

C#
public int Double(int x) => x * 2;

これは、短いメソッドを1行で書く方法です。

最初からすべてを完璧に覚える必要はありません。まずは「左側が入力、右側が処理や戻り値」と考えるところから始めましょう。

まとめ

C#のアロー演算子「=>」は、ラムダ式、LINQ、式形式メンバーでよく使われる重要な記号です。

基本的には、次のように考えると理解しやすくなります。

C#
左側 => 右側

ラムダ式では、左側に引数、右側に処理や戻り値を書きます。

C#
x => x * 2

これは「xを受け取り、x * 2 を返す」と読めます。

LINQでは、条件や変換処理を簡潔に書くために「=>」がよく使われます。

C#
var adults = people.Where(person => person.Age >= 20);

式形式メンバーでは、短いメソッドやプロパティを1行で書けます。

C#
public bool IsAdult => Age >= 20;

「=>」はコードを短く読みやすくできる便利な記号ですが、複雑な処理を無理に1行にまとめると、かえって分かりにくくなることがあります。

短い処理には「=>」を使い、複雑な処理は通常のメソッドに分ける。この使い分けができるようになると、C#のコードをより読みやすく、保守しやすく書けるようになります。