C#のFuncとは?Actionとの違い・使い方・ラムダ式の実例を初心者向けに解説
はじめに
C#でLINQやラムダ式を学び始めると、Funcという型をよく見かけます。
たとえば、次のようなコードです。
C#Func<int, int> doubleNumber = x => x * 2;
int result = doubleNumber(5);
Console.WriteLine(result); // 10
初めて見ると、「Func<int, int>とは何?」「普通のメソッドと何が違うの?」「Actionとはどう使い分けるの?」と感じるかもしれません。
Funcは、簡単にいうと戻り値のある処理を変数のように扱うための仕組みです。メソッドやラムダ式を変数に代入したり、別のメソッドへ引数として渡したりできます。
この記事では、C#のFuncについて、初心者にもわかりやすいように、基本の書き方、Actionとの違い、ラムダ式との関係、実践的な使い方まで順番に解説します。
1. C#のFuncとは?戻り値のあるメソッドを変数のように扱う仕組み
Funcは、C#で「処理そのもの」を扱うために使われる型です。
通常、変数には数値や文字列などの値を入れます。
C#int number = 10;
string name = "Taro";
一方で、Funcを使うと、次のように「計算する処理」を変数に入れることができます。
C#Func<int, int> square = x => x * x;
Console.WriteLine(square(4)); // 16
この例では、x => x * xという処理をsquareという変数に代入しています。square(4)と呼び出すと、4を受け取って16を返します。
つまり、Funcは戻り値を返す処理を入れられる変数のようなものです。
1-1. Funcは.NETに用意されている汎用デリゲート
Funcは、.NETに最初から用意されている汎用デリゲートです。
デリゲートとは、簡単にいうと「メソッドを参照するための型」です。通常の変数が値を参照するのに対して、デリゲートはメソッドや処理を参照します。
Funcは、そのデリゲートをより簡単に使えるようにした標準の型です。
たとえば、次のように書くと、intを受け取ってintを返す処理を表せます。
C#Func<int, int> func;
これは、「int型の引数を1つ受け取り、int型の戻り値を返す処理を代入できる」という意味です。
実際には、次のようにラムダ式を代入します。
C#Func<int, int> doubleNumber = x => x * 2;
Funcを使えば、自分でデリゲート型を定義しなくても、よくある「値を受け取って値を返す処理」をすぐに表現できます。
1-2. 「メソッドを引数として渡す」「処理を変数に入れる」とはどういうことか
通常のメソッド呼び出しでは、メソッドに値を渡します。
C#int result = Add(3, 5);
これは、Addメソッドに3と5という値を渡しています。
一方で、Funcを使うと、メソッドに「処理そのもの」を渡せます。
C#static int Execute(int number, Func<int, int> operation)
{
return operation(number);
}
int result = Execute(10, x => x * 2);
Console.WriteLine(result); // 20
この例では、Executeメソッドに10という値だけでなく、x => x * 2という処理も渡しています。
Executeメソッドの中では、渡された処理をoperation(number)として実行しています。
つまり、Funcを使うと、メソッドの外から「どんな処理を実行するか」を差し替えられるようになります。
1-3. Funcを使うと何が便利になるのか
Funcを使うと、処理を柔軟に切り替えられます。
たとえば、数値に対して何らかの計算を行うメソッドを考えてみます。
C#static int Calculate(int number, Func<int, int> operation)
{
return operation(number);
}
このメソッドは、渡されたoperationによって動作が変わります。
C#int a = Calculate(10, x => x * 2);
int b = Calculate(10, x => x + 100);
int c = Calculate(10, x => x * x);
Console.WriteLine(a); // 20
Console.WriteLine(b); // 110
Console.WriteLine(c); // 100
Calculateメソッド自体は1つだけですが、渡すFuncを変えることで、さまざまな処理を実行できます。
このように、Funcを使うと次のようなメリットがあります。
処理を変数として扱えるため、コードの再利用性が高くなります。
処理をメソッドの引数として渡せるため、柔軟な設計ができます。
ラムダ式と組み合わせることで、短く読みやすいコードを書けます。
LINQのSelectやWhereなどを理解しやすくなります。
1-4. Funcを理解する前に知っておきたいデリゲートの基礎
Funcを理解するには、デリゲートの考え方を少し知っておくと役立ちます。
デリゲートは、メソッドを代入できる型です。
たとえば、独自のデリゲートを定義すると次のようになります。
C#delegate int CalculateDelegate(int x);
static int Double(int x)
{
return x * 2;
}
CalculateDelegate calc = Double;
int result = calc(5);
Console.WriteLine(result); // 10
このコードでは、CalculateDelegateというデリゲート型を定義し、Doubleメソッドを代入しています。
ただし、毎回このように独自デリゲートを定義するのは少し手間です。
そこで、.NETにはあらかじめFuncが用意されています。上のコードは、Funcを使うと次のように短く書けます。
C#static int Double(int x)
{
return x * 2;
}
Func<int, int> calc = Double;
int result = calc(5);
Console.WriteLine(result); // 10
Funcは、よく使うデリゲートを簡単に書くための便利な型だと考えると理解しやすいです。
2. Funcの基本的な書き方と型引数の意味
Funcは、山かっこ<>の中に型を指定して使います。
C#Func<引数の型, 戻り値の型>
たとえば、intを受け取ってstringを返す処理なら次のようになります。
C#Func<int, string> func;
ポイントは、最後に書いた型が戻り値の型になることです。
2-1. Func<TResult>:引数なしで戻り値だけ返す
引数を受け取らず、戻り値だけを返す処理は、Func<TResult>で表します。
C#Func<string> getMessage = () => "Hello";
string message = getMessage();
Console.WriteLine(message); // Hello
Func<string>は、「引数なしでstringを返す処理」という意味です。
ラムダ式の()は、引数がないことを表しています。
C#() => "Hello"
現在時刻を返すような処理にも使えます。
C#Func<DateTime> getNow = () => DateTime.Now;
DateTime now = getNow();
Console.WriteLine(now);
2-2. Func<T, TResult>:1つの引数を受け取って値を返す
1つの引数を受け取り、値を返す処理は、Func<T, TResult>で表します。
C#Func<int, int> doubleNumber = x => x * 2;
int result = doubleNumber(10);
Console.WriteLine(result); // 20
この場合、最初のintが引数の型、最後のintが戻り値の型です。
C#Func<int, int>
// ↑ ↑
// 引数 戻り値
文字列を受け取って文字数を返す例も見てみましょう。
C#Func<string, int> getLength = text => text.Length;
int length = getLength("C# Func");
Console.WriteLine(length); // 7
Func<string, int>は、「stringを受け取ってintを返す処理」を表しています。
2-3. Func<T1, T2, TResult>:複数の引数を受け取る
複数の引数を受け取る場合は、型を順番に並べます。
C#Func<int, int, int> add = (x, y) => x + y;
int result = add(3, 5);
Console.WriteLine(result); // 8
Func<int, int, int>は、次の意味です。
C#Func<int, int, int>
// ↑ ↑ ↑
// 引数1 引数2 戻り値
文字列を2つ受け取って結合する処理も書けます。
C#Func<string, string, string> join = (first, second) => first + second;
string result = join("Hello, ", "C#");
Console.WriteLine(result); // Hello, C#
引数が増えても、最後の型が戻り値になるというルールは変わりません。
2-4. 最後の型引数が戻り値の型になるルール
Funcで特に重要なのは、最後の型引数が戻り値の型になることです。
たとえば、次のFuncを見てください。
C#Func<int, string, bool> check;
これは、intとstringを受け取り、boolを返す処理です。
C#Func<int, string, bool>
// ↑ ↑ ↑
// 引数1 引数2 戻り値
実際のコード例は次のとおりです。
C#Func<int, string, bool> isLongEnough = (minLength, text) => text.Length >= minLength;
bool result = isLongEnough(5, "Hello");
Console.WriteLine(result); // True
int型のminLengthと、string型のtextを受け取り、条件判定の結果としてboolを返しています。
Funcの型引数を読むときは、常に「最後が戻り値」と覚えておきましょう。
2-5. Funcは最大何個まで引数を指定できるのか
Funcは、戻り値とは別に、最大16個までの引数を指定できます。
つまり、次のような形まで用意されています。
C#Func<T1, T2, T3, ..., T16, TResult>
ただし、実際の開発で引数が多すぎるFuncを書くことはあまりおすすめできません。
たとえば、次のようなコードは読みにくくなりがちです。
C#Func<int, int, int, int, int> calc = (a, b, c, d) => a + b + c + d;
この例では、4つのintを受け取り、intを返しています。
引数が多くなる場合は、クラスや構造体にまとめる、専用のメソッドを作る、独自のデリゲートを定義するなどを検討するとよいでしょう。
3. Funcの使い方を初心者向けコードで解説
ここからは、Funcの具体的な使い方をコードで見ていきます。
Funcには、主に次のようなものを代入できます。
ラムダ式
通常のメソッド
匿名メソッド
それぞれの書き方を順番に確認しましょう。
3-1. ラムダ式をFuncに代入する基本例
もっともよく使われるのは、Funcにラムダ式を代入する書き方です。
C#Func<int, int> doubleNumber = x => x * 2;
int result = doubleNumber(5);
Console.WriteLine(result); // 10
x => x * 2は、「xを受け取って、xの2倍を返す」という意味です。
文字列を加工する例も見てみましょう。
C#Func<string, string> addGreeting = name => "Hello, " + name;
string message = addGreeting("Taro");
Console.WriteLine(message); // Hello, Taro
ラムダ式を使うと、短い処理をその場で簡潔に書けます。
3-2. 通常のメソッドをFuncに代入する例
Funcには、通常のメソッドも代入できます。
C#static int Double(int x)
{
return x * 2;
}
Func<int, int> func = Double;
int result = func(10);
Console.WriteLine(result); // 20
このとき、Doubleメソッドの形はFunc<int, int>と一致している必要があります。
Func<int, int>は、「intを受け取ってintを返す処理」です。
Doubleメソッドも、intを受け取ってintを返しています。
C#static int Double(int x)
{
return x * 2;
}
そのため、Func<int, int>に代入できます。
注意点として、代入するときは通常、メソッド名だけを書きます。
C#Func<int, int> func = Double;
次のように書くと、メソッドを代入しているのではなく、メソッドをその場で実行していることになります。
C#Func<int, int> func = Double(10); // エラー
Funcに入れたいのは「実行結果」ではなく「処理そのもの」です。
3-3. 匿名メソッドをFuncに代入する例
C#では、ラムダ式のほかに匿名メソッドを使ってFuncに処理を代入することもできます。
C#Func<int, int> doubleNumber = delegate (int x)
{
return x * 2;
};
int result = doubleNumber(5);
Console.WriteLine(result); // 10
匿名メソッドは、delegateキーワードを使ってその場でメソッドのような処理を書く方法です。
現在では、より短く書けるラムダ式が使われることが多いです。
ラムダ式で書くと、同じ処理は次のようになります。
C#Func<int, int> doubleNumber = x => x * 2;
初心者のうちは、基本的にはラムダ式を中心に覚えれば問題ありません。
3-4. Funcを呼び出して戻り値を受け取る方法
Funcに代入した処理は、通常のメソッドと同じように呼び出せます。
C#Func<int, int> square = x => x * x;
int result = square(4);
Console.WriteLine(result); // 16
square(4)と書くことで、Funcに入っている処理が実行されます。
複数の引数がある場合も同じです。
C#Func<int, int, int> add = (x, y) => x + y;
int result = add(3, 7);
Console.WriteLine(result); // 10
引数なしの場合は、空の丸かっこで呼び出します。
C#Func<string> getMessage = () => "こんにちは";
string message = getMessage();
Console.WriteLine(message); // こんにちは
Funcは戻り値があるため、呼び出した結果を変数に代入できます。
C#string message = getMessage();
もちろん、戻り値をそのまま別のメソッドに渡すこともできます。
C#Console.WriteLine(getMessage());
3-5. 型推論を使ってコードを短く書く方法
ラムダ式をFuncに代入する場合、左辺で型が決まっていれば、ラムダ式の引数型は省略できます。
C#Func<int, int> doubleNumber = x => x * 2;
これは、次のように明示的に型を書くこともできます。
C#Func<int, int> doubleNumber = (int x) => x * 2;
左辺のFunc<int, int>から、xがintであることをコンパイラが推論できるため、(int x)ではなくxと書けます。
複数の引数でも同じです。
C#Func<int, int, int> add = (x, y) => x + y;
この場合も、xとyがintであることは、左辺のFunc<int, int, int>からわかります。
ただし、初心者が注意したいのは、古いC#では次のようにvarだけでラムダ式を受けることはできない点です。
C#var doubleNumber = x => x * 2; // 基本的には避ける
ラムダ式は、代入先の型があることで意味が決まります。そのため、学習段階では次のようにFuncの型を明示して書くのがおすすめです。
C#Func<int, int> doubleNumber = x => x * 2;
一方で、戻り値を受け取る変数にはvarを使えます。
C#Func<int, int> doubleNumber = x => x * 2;
var result = doubleNumber(10);
Console.WriteLine(result); // 20
この場合、resultはint型として推論されます。
4. FuncとActionの違い
Funcとよく比較されるものにActionがあります。
どちらも.NETに用意されている汎用デリゲートですが、最大の違いは戻り値があるかどうかです。
4-1. Funcは戻り値あり、Actionは戻り値なし
Funcは、戻り値のある処理を表します。
C#Func<int, int> doubleNumber = x => x * 2;
int result = doubleNumber(5);
Console.WriteLine(result); // 10
この例では、doubleNumberはintを返します。
一方、Actionは戻り値のない処理を表します。
C#Action<string> showMessage = message => Console.WriteLine(message);
showMessage("Hello");
Console.WriteLineは画面に文字を表示しますが、値を返すわけではありません。このような処理はActionに向いています。
4-2. FuncとActionの書き方の違い
Funcは、最後の型引数が戻り値の型です。
C#Func<int, int> doubleNumber = x => x * 2;
これは、「intを受け取ってintを返す処理」です。
一方、Actionには戻り値の型を書きません。
C#Action<int> showNumber = x => Console.WriteLine(x);
これは、「intを受け取って何か処理をするが、戻り値は返さない処理」です。
引数なしのActionも書けます。
C#Action sayHello = () => Console.WriteLine("Hello");
sayHello();
Funcで引数なし・戻り値ありの処理を書く場合は、次のようになります。
C#Func<string> getHello = () => "Hello";
string message = getHello();
Actionは「実行するだけ」、Funcは「実行して結果を返す」と考えるとわかりやすいです。
4-3. Console.WriteLineのような処理はAction向き
Console.WriteLineのように、画面に表示するだけで戻り値を使わない処理はAction向きです。
C#Action<string> print = text => Console.WriteLine(text);
print("C# Action");
次のように、戻り値を返していない処理をFuncに代入しようとするとエラーになります。
C#Func<string, string> print = text => Console.WriteLine(text); // エラー
Console.WriteLine(text)は文字列を返さないため、Func<string, string>には合いません。
この場合はAction<string>を使います。
C#Action<string> print = text => Console.WriteLine(text);
戻り値が必要ない処理には、FuncではなくActionを使いましょう。
4-4. 計算結果や判定結果を返す処理はFunc向き
計算結果や判定結果を返す処理は、Funcに向いています。
C#Func<int, int> square = x => x * x;
int result = square(6);
Console.WriteLine(result); // 36
条件判定を行い、boolを返す処理もFuncで表せます。
C#Func<int, bool> isEven = x => x % 2 == 0;
Console.WriteLine(isEven(4)); // True
Console.WriteLine(isEven(5)); // False
文字列を受け取って加工した結果を返す処理もFuncです。
C#Func<string, string> toUpper = text => text.ToUpper();
string result = toUpper("hello");
Console.WriteLine(result); // HELLO
何かしらの値を返す処理であれば、基本的にはFuncを選びます。
4-5. FuncとActionの使い分け早見表
FuncとActionは、戻り値の有無で使い分けます。
| 種類 | 戻り値 | 例 | 向いている処理 |
|---|---|---|---|
| Func | あり | Func<int, int> | 計算、変換、判定 |
| Action | なし | Action<string> | 表示、保存、ログ出力 |
たとえば、数値を2倍にして返すならFuncです。
C#Func<int, int> doubleNumber = x => x * 2;
画面に表示するだけならActionです。
C#Action<string> print = text => Console.WriteLine(text);
迷ったときは、「その処理は結果を返す必要があるか?」と考えてみましょう。
結果を返すならFunc、返さないならActionです。
5. Funcとラムダ式の関係
Funcは、ラムダ式と一緒に使われることが非常に多いです。
特にLINQでは、WhereやSelectなどのメソッドにラムダ式を渡す場面で、内部的にFuncが使われています。
5-1. ラムダ式とは何か
ラムダ式とは、メソッドのような処理を短く書くための構文です。
たとえば、次のように書きます。
C#x => x * 2
これは、「xを受け取って、xの2倍を返す」という意味です。
通常のメソッドで書くと、次のようになります。
C#static int Double(int x)
{
return x * 2;
}
ラムダ式を使うと、短い処理をその場で書けます。
C#Func<int, int> doubleNumber = x => x * 2;
ラムダ式は、特にFuncやActionと組み合わせて使うことで力を発揮します。
5-2. Funcにラムダ式を代入できる理由
Funcにラムダ式を代入できるのは、ラムダ式の形がFuncの型と一致しているからです。
たとえば、次のコードを見てください。
C#Func<int, int> doubleNumber = x => x * 2;
Func<int, int>は、「intを受け取ってintを返す処理」を表します。
ラムダ式x => x * 2も、intを受け取ってintを返す処理として解釈できます。
そのため、代入できます。
次の例も同じです。
C#Func<string, int> getLength = text => text.Length;
Func<string, int>は、「stringを受け取ってintを返す処理」です。
text => text.Lengthも、文字列を受け取り、その長さをintで返します。
型が一致しているため、Funcに代入できます。
5-3. 式形式のラムダ式とステートメント形式のラムダ式
ラムダ式には、大きく分けて2つの書き方があります。
1つ目は、式形式のラムダ式です。
C#Func<int, int> doubleNumber = x => x * 2;
x * 2の結果がそのまま戻り値になります。
2つ目は、ステートメント形式のラムダ式です。
C#Func<int, int> doubleNumber = x =>
{
int result = x * 2;
return result;
};
波かっこ{}を使って、複数行の処理を書けます。
ステートメント形式では、Funcに戻り値があるため、returnを書く必要があります。
たとえば、次のように条件分岐を含めることもできます。
C#Func<int, string> judge = score =>
{
if (score >= 80)
{
return "合格";
}
return "不合格";
};
Console.WriteLine(judge(90)); // 合格
Console.WriteLine(judge(60)); // 不合格
短い処理なら式形式、複数行の処理ならステートメント形式を使うとよいでしょう。
5-4. 引数が1つの場合・複数の場合の書き方
ラムダ式では、引数が1つの場合、丸かっこを省略できます。
C#Func<int, int> doubleNumber = x => x * 2;
丸かっこを付けても問題ありません。
C#Func<int, int> doubleNumber = (x) => x * 2;
複数の引数がある場合は、丸かっこが必要です。
C#Func<int, int, int> add = (x, y) => x + y;
引数がない場合は、空の丸かっこを書きます。
C#Func<string> getMessage = () => "Hello";
型を明示して書くこともできます。
C#Func<int, int, int> add = (int x, int y) => x + y;
ただし、多くの場合は左辺のFuncから型を推論できるため、次のように省略して書くのが一般的です。
C#Func<int, int, int> add = (x, y) => x + y;
5-5. 戻り値の型が合わないとコンパイルエラーになる理由
Funcでは、指定した戻り値の型と、実際に返す値の型が一致している必要があります。
たとえば、次のコードはエラーになります。
C#Func<int, int> func = x => "結果は" + x; // エラー
Func<int, int>は、intを返す必要があります。
しかし、ラムダ式x => "結果は" + xは文字列を返しています。
戻り値の型がintではなくstringなので、型が合わずコンパイルエラーになります。
正しく書くなら、戻り値の型をstringにします。
C#Func<int, string> func = x => "結果は" + x;
string result = func(10);
Console.WriteLine(result); // 結果は10
また、returnを書き忘れた場合もエラーになります。
C#Func<int, int> func = x =>
{
int result = x * 2;
// returnがないのでエラー
};
Funcは戻り値のある処理なので、ステートメント形式のラムダ式では必ず値を返す必要があります。
C#Func<int, int> func = x =>
{
int result = x * 2;
return result;
};
6. Funcの実践的な利用シーン
Funcは基本文法だけを見ると少し抽象的に感じるかもしれません。
しかし、実際のC#開発ではさまざまな場面で使われています。
特に、LINQ、処理の差し替え、条件による処理の切り替え、コールバック、共通処理の再利用などで役立ちます。
6-1. LINQのSelectやWhereで使われるFunc
C#でFuncがよく使われる代表例がLINQです。
たとえば、Whereを使って偶数だけを取り出すコードを見てみましょう。
C#int[] numbers = { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.Where(x => x % 2 == 0);
foreach (int number in evenNumbers)
{
Console.WriteLine(number);
}
Whereに渡しているx => x % 2 == 0は、条件判定を行うラムダ式です。
これは、イメージとしては次のようなFunc<int, bool>です。
C#Func<int, bool> isEven = x => x % 2 == 0;
Whereは、各要素に対してこの処理を実行し、trueになった要素だけを残します。
SelectもFuncと関係があります。
C#int[] numbers = { 1, 2, 3 };
var doubled = numbers.Select(x => x * 2);
foreach (int number in doubled)
{
Console.WriteLine(number);
}
Selectに渡しているx => x * 2は、各要素を別の値に変換する処理です。
イメージとしては次のようなFunc<int, int>です。
C#Func<int, int> doubleNumber = x => x * 2;
LINQを理解するうえでも、Funcの考え方はとても重要です。
6-2. メソッドの引数にFuncを渡して処理を差し替える
Funcをメソッドの引数にすると、外から処理を差し替えられます。
C#static int Execute(int value, Func<int, int> operation)
{
return operation(value);
}
このメソッドは、operationとして渡された処理を実行します。
C#int result1 = Execute(10, x => x * 2);
int result2 = Execute(10, x => x + 5);
int result3 = Execute(10, x => x * x);
Console.WriteLine(result1); // 20
Console.WriteLine(result2); // 15
Console.WriteLine(result3); // 100
Executeメソッド自体は同じですが、渡すFuncを変えることで結果が変わります。
このように、処理の一部を外から指定できるようにすると、コードの柔軟性が高くなります。
6-3. 条件に応じて実行する処理を切り替える
Funcは変数に代入できるため、条件に応じて実行する処理を切り替えることもできます。
C#bool useDiscount = true;
Func<int, int> calculatePrice;
if (useDiscount)
{
calculatePrice = price => price - 100;
}
else
{
calculatePrice = price => price;
}
int result = calculatePrice(1000);
Console.WriteLine(result); // 900
この例では、useDiscountがtrueの場合は100円引きの処理を使い、falseの場合はそのままの価格を返します。
条件によって処理を切り替えたい場合、Funcを使うとすっきり書けることがあります。
より短く書くなら、三項演算子も使えます。
C#bool useDiscount = true;
Func<int, int> calculatePrice = useDiscount
? price => price - 100
: price => price;
int result = calculatePrice(1000);
Console.WriteLine(result); // 900
6-4. コールバック処理としてFuncを使う
Funcは、コールバック処理としても使えます。
コールバックとは、ある処理の途中や完了後に、外から渡された処理を呼び出す仕組みです。
たとえば、数値を処理したあと、その結果をFuncで変換する例を見てみましょう。
C#static string ProcessNumber(int number, Func<int, string> callback)
{
int calculated = number * 2;
return callback(calculated);
}
このメソッドは、数値を2倍にしたあと、callbackに渡して結果を返します。
C#string result = ProcessNumber(10, x => $"計算結果は{x}です");
Console.WriteLine(result); // 計算結果は20です
ProcessNumberメソッドは、最終的な文字列の作り方を知りません。
文字列の作り方は、外から渡されたFunc<int, string>に任せています。
これにより、メソッドの汎用性が高くなります。
6-5. 共通処理を再利用しやすくする
Funcを使うと、共通処理の一部だけを差し替えられます。
たとえば、処理の前後にログを出したい場合を考えます。
C#static int RunWithLog(string name, Func<int> operation)
{
Console.WriteLine($"{name}を開始します");
int result = operation();
Console.WriteLine($"{name}が終了しました");
return result;
}
このメソッドでは、開始ログと終了ログは共通で、実際の処理だけをFunc<int>として外から渡しています。
C#int result = RunWithLog("計算処理", () =>
{
int a = 10;
int b = 20;
return a + b;
});
Console.WriteLine(result); // 30
このようにすると、ログ出力のような共通処理を1か所にまとめつつ、実行する処理だけを柔軟に差し替えられます。
7. Func・Action・Predicate・delegateの違い
C#には、FuncやAction以外にも、Predicateやdelegateがあります。
どれも「処理を扱う」ための仕組みですが、用途や書き方が少しずつ違います。
7-1. Predicateはboolを返すFuncのようなもの
Predicate<T>は、T型の値を受け取ってboolを返すデリゲートです。
C#Predicate<int> isEven = x => x % 2 == 0;
Console.WriteLine(isEven(4)); // True
Console.WriteLine(isEven(5)); // False
これは、次のFuncとよく似ています。
C#Func<int, bool> isEven = x => x % 2 == 0;
どちらも「intを受け取ってboolを返す処理」を表します。
違いは、Predicate<T>は条件判定用であることが名前からわかりやすい点です。
C#Predicate<string> isEmpty = text => text.Length == 0;
ただし、実際の開発ではFunc<T, bool>もよく使われます。特にLINQのWhereでは、Func<T, bool>の形で条件を渡すことが多いです。
7-2. delegateを自分で定義する場合との違い
delegateを使うと、自分でデリゲート型を定義できます。
C#delegate int Calculate(int x, int y);
static int Add(int x, int y)
{
return x + y;
}
Calculate calc = Add;
Console.WriteLine(calc(3, 5)); // 8
同じことは、Funcを使って次のように書けます。
C#static int Add(int x, int y)
{
return x + y;
}
Func<int, int, int> calc = Add;
Console.WriteLine(calc(3, 5)); // 8
Funcを使うと、独自のデリゲートを定義しなくても済みます。
ただし、独自デリゲートには名前を付けられるというメリットがあります。
C#delegate int PriceCalculator(int price, int taxRate);
このように書くと、「価格計算用の処理である」という意味がコードから伝わりやすくなります。
一方、Func<int, int, int>だけを見ると、何の処理なのかは変数名や文脈を見ないとわかりません。
7-3. Funcを使うべきケース
Funcを使うべきなのは、戻り値のある処理を簡潔に扱いたい場合です。
たとえば、次のようなケースです。
値を変換する処理
計算結果を返す処理
条件判定の結果を返す処理
LINQで使う処理
メソッドの引数として処理を渡したい場合
例を見てみましょう。
C#Func<int, int> doubleNumber = x => x * 2;
Func<string, int> getLength = text => text.Length;
Func<int, bool> isEven = x => x % 2 == 0;
短く書けて、処理の意味が明確であれば、Funcはとても便利です。
7-4. Actionを使うべきケース
Actionを使うべきなのは、戻り値が不要な処理を扱いたい場合です。
たとえば、次のようなケースです。
画面に表示する
ログを出力する
ファイルに保存する
状態を変更する
通知を送る
例は次のとおりです。
C#Action<string> print = message => Console.WriteLine(message);
print("ログを出力します");
戻り値が必要ないのにFuncを使おうとすると、かえってコードがわかりにくくなります。
戻り値がない処理にはActionを使いましょう。
7-5. 独自delegateを使うべきケース
独自のdelegateを使うべきなのは、処理に明確な名前を付けたい場合です。
たとえば、次のようなFuncがあったとします。
C#Func<int, int, int> calculator;
これだけでは、足し算なのか、料金計算なのか、スコア計算なのかがわかりません。
独自デリゲートを使うと、意味を名前で表せます。
C#delegate int PriceCalculator(int price, int taxRate);
このように定義しておけば、価格計算用の処理であることがわかりやすくなります。
C#PriceCalculator calculator = (price, taxRate) =>
{
return price + price * taxRate / 100;
};
int result = calculator(1000, 10);
Console.WriteLine(result); // 1100
特に、公開APIや大きなプロジェクトでは、意味のある名前を付けられる独自デリゲートが役立つことがあります。
一方で、短い処理や一時的な処理であれば、FuncやActionで十分です。
8. Funcで初心者がつまずきやすいポイント
Funcは便利ですが、初心者がつまずきやすいポイントもあります。
特に多いのは、型引数の順番、戻り値の有無、ラムダ式の型、メソッド呼び出しとの違い、nullの扱いです。
8-1. 型引数の順番を間違える
Funcでよくあるミスが、型引数の順番を間違えることです。
Funcでは、最後の型引数が戻り値の型です。
C#Func<int, string> func = x => x.ToString();
これは、「intを受け取ってstringを返す処理」です。
しかし、次のように逆にしてしまうと意味が変わります。
C#Func<string, int> func = x => x.ToString(); // エラー
Func<string, int>は、「stringを受け取ってintを返す処理」です。
しかし、x.ToString()はstringを返すため、戻り値の型が合いません。
Funcを読むときは、必ず最後の型が戻り値だと確認しましょう。
8-2. 戻り値がない処理をFuncに代入しようとする
Funcは戻り値のある処理を表します。
そのため、戻り値がない処理を代入するとエラーになります。
C#Func<string, string> func = text => Console.WriteLine(text); // エラー
Console.WriteLineは値を返さないため、Func<string, string>には代入できません。
この場合はActionを使います。
C#Action<string> action = text => Console.WriteLine(text);
action("Hello");
「結果を返す処理か」「実行するだけの処理か」を意識すると、FuncとActionを間違えにくくなります。
8-3. ラムダ式の引数とFuncの型が一致していない
ラムダ式の引数の数や型は、Funcの型と一致している必要があります。
たとえば、次のコードはエラーです。
C#Func<int, int> func = (x, y) => x + y; // エラー
Func<int, int>は、引数が1つで戻り値がintの処理です。
しかし、ラムダ式はxとyの2つの引数を受け取っています。
2つのintを受け取ってintを返すなら、次のように書きます。
C#Func<int, int, int> func = (x, y) => x + y;
int result = func(3, 5);
Console.WriteLine(result); // 8
引数の数と戻り値の型が合っているかを確認しましょう。
8-4. Funcとメソッド呼び出しの違いがわからない
Funcにメソッドを代入するとき、初心者がよく間違えるのが、メソッド名の後ろに丸かっこを付けてしまうことです。
C#static int GetNumber()
{
return 100;
}
Func<int> func = GetNumber; // 正しい
Func<int> func2 = GetNumber(); // エラー
GetNumberはメソッドそのものを指します。
一方、GetNumber()はメソッドを実行した結果です。
GetNumber()の戻り値はintなので、Func<int>には代入できません。
次のように考えるとわかりやすいです。
C#Func<int> func = GetNumber;
これは、「あとで実行できるように、GetNumberメソッドをfuncに入れておく」という意味です。
呼び出すときに丸かっこを付けます。
C#int result = func();
8-5. nullのFuncを呼び出して例外が発生する
Funcは参照型なので、nullになることがあります。
C#Func<int, int> func = null;
この状態で呼び出すと、NullReferenceExceptionが発生します。
C#int result = func(10); // 例外
Funcがnullの可能性がある場合は、呼び出す前に確認しましょう。
C#Func<int, int>? func = null;
if (func != null)
{
int result = func(10);
Console.WriteLine(result);
}
null条件演算子を使うこともできます。
C#Func<int, int>? func = null;
int? result = func?.Invoke(10);
Console.WriteLine(result);
func?.Invoke(10)は、funcがnullでなければ呼び出し、nullなら何もしません。
ただし、戻り値もnullになり得るため、結果の扱いには注意が必要です。
9. Funcを使ったサンプルコード集
ここでは、Funcの使い方を具体的なサンプルコードで確認します。
簡単な例から、メソッドの引数に渡す例まで見ていきましょう。
9-1. 数値を2倍にするFunc
もっとも基本的な例です。
C#Func<int, int> doubleNumber = x => x * 2;
int result = doubleNumber(8);
Console.WriteLine(result); // 16
Func<int, int>は、intを受け取ってintを返す処理です。
x => x * 2は、受け取った数値を2倍にして返しています。
ステートメント形式で書くと、次のようになります。
C#Func<int, int> doubleNumber = x =>
{
return x * 2;
};
int result = doubleNumber(8);
Console.WriteLine(result); // 16
短い処理であれば、式形式のラムダ式のほうが読みやすいです。
9-2. 文字列を加工して返すFunc
文字列を受け取って、加工した文字列を返す例です。
C#Func<string, string> addPrefix = text => "入力値: " + text;
string result = addPrefix("C# Func");
Console.WriteLine(result); // 入力値: C# Func
大文字に変換する処理も書けます。
C#Func<string, string> toUpper = text => text.ToUpper();
string result = toUpper("hello");
Console.WriteLine(result); // HELLO
文字列を受け取って数値を返す場合は、戻り値の型をintにします。
C#Func<string, int> getLength = text => text.Length;
int length = getLength("CSharp");
Console.WriteLine(length); // 6
9-3. 2つの数値を受け取って計算するFunc
2つの数値を受け取り、計算結果を返す例です。
C#Func<int, int, int> add = (x, y) => x + y;
int result = add(10, 20);
Console.WriteLine(result); // 30
引き算も同じ形で書けます。
C#Func<int, int, int> subtract = (x, y) => x - y;
int result = subtract(20, 5);
Console.WriteLine(result); // 15
掛け算の場合も同じです。
C#Func<int, int, int> multiply = (x, y) => x * y;
int result = multiply(6, 7);
Console.WriteLine(result); // 42
Func<int, int, int>では、最初の2つのintが引数、最後のintが戻り値です。
9-4. 条件に応じて異なるFuncを実行する例
条件によって使うFuncを切り替える例です。
C#string mode = "taxIncluded";
Func<int, int> calculatePrice;
if (mode == "taxIncluded")
{
calculatePrice = price => (int)(price * 1.1);
}
else
{
calculatePrice = price => price;
}
int result = calculatePrice(1000);
Console.WriteLine(result); // 1100
この例では、modeがtaxIncludedの場合は税込価格を返し、それ以外の場合は元の価格を返します。
三項演算子を使うと、次のようにも書けます。
C#string mode = "discount";
Func<int, int> calculatePrice = mode == "discount"
? price => price - 200
: price => price;
int result = calculatePrice(1000);
Console.WriteLine(result); // 800
処理を変数として扱えるため、条件に応じた切り替えがしやすくなります。
9-5. Funcを引数に取るメソッドの例
最後に、Funcをメソッドの引数として受け取る例です。
C#static int Apply(int value, Func<int, int> operation)
{
return operation(value);
}
このメソッドは、valueに対してoperationを実行し、その結果を返します。
C#int result1 = Apply(10, x => x * 2);
int result2 = Apply(10, x => x + 100);
int result3 = Apply(10, x => x * x);
Console.WriteLine(result1); // 20
Console.WriteLine(result2); // 110
Console.WriteLine(result3); // 100
Applyメソッドは、具体的に何をするかを知りません。
何をするかは、引数として渡されたFunc<int, int>によって決まります。
文字列を処理する例も見てみましょう。
C#static string FormatText(string text, Func<string, string> formatter)
{
return formatter(text);
}
string result1 = FormatText("hello", x => x.ToUpper());
string result2 = FormatText("hello", x => "[" + x + "]");
Console.WriteLine(result1); // HELLO
Console.WriteLine(result2); // [hello]
このように、Funcを引数に取るメソッドを作ると、処理を外から自由に差し替えられます。
10. C#のFuncに関するよくある質問
ここでは、C#のFuncについて初心者が疑問に感じやすいポイントをQ&A形式で整理します。
10-1. Funcはいつ使えばいい?
Funcは、戻り値のある処理を変数に入れたいときや、メソッドの引数として渡したいときに使います。
たとえば、次のような場面です。
計算処理を差し替えたい
条件判定を外から渡したい
文字列の加工方法を変更したい
LINQでデータを変換・抽出したい
コールバックとして結果を返したい
簡単な例は次のとおりです。
C#static int Calculate(int value, Func<int, int> operation)
{
return operation(value);
}
int result = Calculate(10, x => x * 3);
Console.WriteLine(result); // 30
「処理を値のように扱いたい」と思ったときに、Funcが役立ちます。
10-2. Funcとラムダ式は同じもの?
Funcとラムダ式は同じものではありません。
Funcは、処理を入れるための型です。
ラムダ式は、処理そのものを書くための構文です。
たとえば、次のコードでは、Func<int, int>が型で、x => x * 2がラムダ式です。
C#Func<int, int> doubleNumber = x => x * 2;
イメージとしては、Funcという箱に、ラムダ式という処理を入れていると考えるとわかりやすいです。
通常のメソッドもFuncに代入できます。
C#static int Double(int x)
{
return x * 2;
}
Func<int, int> doubleNumber = Double;
つまり、Funcは型、ラムダ式は処理の書き方です。
10-3. FuncとActionはどちらを覚えればいい?
FuncとActionはどちらもよく使うため、両方覚えておくのがおすすめです。
ただし、違いはシンプルです。
戻り値があるならFuncです。
C#Func<int, int> doubleNumber = x => x * 2;
戻り値がないならActionです。
C#Action<string> print = text => Console.WriteLine(text);
最初は、この違いだけ押さえれば十分です。
その後、LINQやラムダ式に慣れてくると、自然に使い分けられるようになります。
10-4. Funcは初心者でも使うべき?
初心者でも、Funcは少しずつ使えるようになっておくとよいです。
特に、C#でLINQを使うなら、Funcの考え方は避けて通れません。
たとえば、次のようなLINQのコードはよく使われます。
C#var numbers = new[] { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(x => x % 2 == 0);
このx => x % 2 == 0は、Func<int, bool>のような処理として使われています。
最初から難しい使い方を覚える必要はありません。
まずは、次のような基本形から慣れるのがおすすめです。
C#Func<int, int> doubleNumber = x => x * 2;
Console.WriteLine(doubleNumber(5)); // 10
Funcは、ラムダ式やLINQを理解するための重要な基礎です。
10-5. Funcを使わない書き方との違いは?
Funcを使わなくても、通常のメソッドで同じ処理を書くことはできます。
C#static int Double(int x)
{
return x * 2;
}
int result = Double(5);
Console.WriteLine(result); // 10
一方、Funcを使うと、処理を変数に入れたり、引数として渡したりできます。
C#Func<int, int> doubleNumber = x => x * 2;
int result = doubleNumber(5);
Console.WriteLine(result); // 10
さらに、メソッドの引数として渡すこともできます。
C#static int Execute(int value, Func<int, int> operation)
{
return operation(value);
}
int result = Execute(5, x => x * 2);
Console.WriteLine(result); // 10
通常のメソッドは、処理を固定して書くときに向いています。
Funcは、処理を差し替えたいとき、引数として渡したいとき、ラムダ式で簡潔に書きたいときに向いています。
どちらが優れているというより、用途に応じて使い分けることが大切です。
まとめ
C#のFuncは、戻り値のある処理を変数のように扱うための汎用デリゲートです。
基本形は次のように書きます。
C#Func<引数の型, 戻り値の型>
たとえば、intを受け取ってintを返す処理は次のように書けます。
C#Func<int, int> doubleNumber = x => x * 2;
複数の引数がある場合も、最後の型引数が戻り値になるというルールは変わりません。
C#Func<int, int, int> add = (x, y) => x + y;
FuncとActionの違いは、戻り値の有無です。
戻り値がある処理はFuncです。
C#Func<int, int> square = x => x * x;
戻り値がない処理はActionです。
C#Action<string> print = text => Console.WriteLine(text);
また、Funcはラムダ式と組み合わせて使われることが多く、LINQのWhereやSelectを理解するうえでも重要です。
Funcを使えるようになると、処理を変数に入れる、メソッドの引数として渡す、条件に応じて処理を切り替えるといった柔軟なコードが書けるようになります。
最初は難しく感じるかもしれませんが、まずは次の3つを覚えておきましょう。
Funcは戻り値のある処理を表す。
最後の型引数が戻り値の型になる。
戻り値がない処理にはActionを使う。
この3点を押さえれば、C#のFuncはぐっと理解しやすくなります。

