メソッドは名前の付いたコードのブロックで、入力(引数)を受け取り、処理を行い、結果(戻り値)を返すことができます。C#では、すべてのメソッドが型の中にあり、独立した関数はありません。
出力:
Headphones 48.00
Cable 11.40
メソッドの構成
public static decimal PriceWithTax(decimal net, decimal taxRate)
{
return net * (1 + taxRate);
}
- アクセス修飾子(
public、privateなど):誰が呼び出せるかを決めます。省略すると、メソッドはその型のprivateになります。 static:メソッドがオブジェクトではなく型に属します。詳しくは後述します。- 戻り値の型:返す値の型、または何も返さない場合は
voidです。 - 名前:慣例でPascalCaseにし、通常は動詞にします(
Calculate、SendEmail、TryParse)。 - 引数リスト:0個以上の
type nameの組です。各引数は、呼び出し側の実引数で初期化されるローカル変数です。 - 本体:波かっこの間の文です。
名前と引数の型(および ref、out、in の修飾子)がメソッドのシグネチャを構成します。戻り値の型はシグネチャに含まれず、これはオーバーロードで重要になります。
戻り値とvoid
return はメソッドをすぐに終わらせ、値を返します。戻り値の型を持つメソッドは、すべての経路でその型の値を返さなければなりません。
static string Grade(int score)
{
if (score >= 50)
return "pass";
} // error CS0161: 'Program.Grade(int)': not all code paths return a value
コンパイラはすべての分岐を確認します。足りない return "fail"; を追加するか、その地点に到達するのがバグなら throw を書きます。
void メソッドは何も返しません。それでも、値のない return; で早めに抜けることはできます。
出力:
ana@mail.com: newsletter sent
ben@mail.com: not subscribed, skipped
False
True
式形式のメソッド
メソッドの本体が1つの式なら、波かっこと return の代わりに => を使えます。
static decimal Discount(decimal price) => price * 0.1m;
static bool IsAdult(int age) => age >= 18;
static void Log(string message) => Console.WriteLine($"[log] {message}");
これは同じメソッドを短く書いたものです。本体が1つの式(呼び出しや代入)である限り、void メソッドにも使えます。
引数はコピー
既定では、引数は値渡しされます。引数は実引数のコピーを受け取ります。メソッドの中で引数に代入しても、呼び出し側の変数には影響しません。
出力:
inside: 42
after: 21
scores: 70, 85, 100
List が変わったのは、参照型ではコピーされる値が参照だからです。メソッドと呼び出し側は同じリストのオブジェクトを指しています。メソッドの中で scores = new List<int>() と再代入しても、呼び出し側には影響しません。メソッドから呼び出し側の変数に代入させるには ref か out を使います。引数に既定値を与えて名前で指定して呼ぶには、省略可能な引数と名前付き引数を使います。
タプルで複数の値を返す
C# 7.0以降、メソッドは名前付きの要素を持つタプルを返すことができ、呼び出し側はそれを別々の変数に分解できます。
出力:
min 15, max 27, avg 21.0
range 12
_ は不要な要素を破棄します。多くの場所で使う結果なら、名前付きのプロパティを持つ小さなクラスやレコードのほうが、タプルより形をよく表します。
オーバーロード
引数リストの数、型、型の順序が異なれば、複数のメソッドが同じ名前を共有できます。コンパイラは呼び出しごとに最も一致するものを選びます。
出力:
12.57
13.50
25
Area(5) は完全に一致するので int 版を選び、Area(2.0) は double 版を選びます。戻り値の型だけが異なるメソッドは定義できません。int と double を返す2つの Area(int) メソッドはエラーCS0111 Type 'Program' already defines a member called 'Area' with the same parameter types になります。Area(5) のような呼び出しで区別できないからです。
どのオーバーロードも受け付けない呼び出しも、コンパイル時に失敗します。Area(1, 2, 3) はCS1501 No overload for method 'Area' takes 3 arguments、Area("big") はCS1503 Argument 1: cannot convert from 'string' to 'double' になります。
params:可変個の引数
params 配列の引数は、0個を含む任意の個数の引数を受け付けます。配列はコンパイラが作ってくれます。
出力:
empty cart: 0 item(s), 0
one item: 1 item(s), 9.99
three items: 3 item(s), 19.75
from array: 2 item(s), 50
ルール:params は最後の引数でなければならず(そうでなければCS0231)、1つのメソッドに多くても1つで、既存の配列を渡すとその配列がそのまま渡されます。Console.WriteLine("{0} and {1}", a, b) や string.Format は params object[] のメソッドです。C# 13で params は List<T> や ReadOnlySpan<T> などの他のコレクション型に拡張されました。それ以前は配列しか使えませんでした。
静的メソッドとインスタンスメソッド
static メソッドは型に属し、型名に対して呼び出します:Math.Max(3, 7)、Program.Total(...)。インスタンスメソッドはオブジェクトに属し、そのオブジェクトのフィールドを使えます。
出力:
12
オブジェクトなしで静的メソッドからインスタンスメソッドを呼び出すのは、C#の初心者に最も多いエラーです。
class Program
{
void Greet() => Console.WriteLine("hi");
static void Main()
{
Greet();
// error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
}
}
Greet に static を付けるか、オブジェクトを作ってそれに対して呼び出します:new Program().Greet();。静的メンバーと静的クラスについては、staticのページで詳しく扱います。
Mainメソッド
Main はプログラムが始まる場所です。static でなければならず、次のシグネチャが有効です。
static void Main() { }
static void Main(string[] args) { } // command-line arguments
static int Main(string[] args) { return 0; } // exit code for the shell
static async Task Main() { } // C# 7.1 and later
async Task Main の形なら、エントリポイントで直接 await できます(asyncとawaitを参照)。C# 9以降は、代わりにトップレベルステートメントをファイルに書けます。新しい dotnet new console プロジェクトのように、クラスも Main もないコードです。コンパイラがその周りにクラスと Main メソッドを生成するので、同じものを短く書いたことになります。args も引き続き使えます。
再帰
メソッドは自分自身を呼び出せます。呼び出しごとに独自の引数とローカル変数を持ち、基底ケースで連鎖を止めなければなりません。
出力:
120
2432902008176640000
35
基底ケースがない場合や入力が深すぎる場合は、呼び出しのたびにスタックフレームが追加され、最終的にプログラムは StackOverflowException で終了します。この例外はキャッチできないので、深さがわからない入力に対しては、再帰をループに書き換えます。
ローカル関数
C# 7.0でローカル関数が導入されました。別のメソッドの中で宣言され、その中でだけ見えるメソッドです。外側のメソッドの変数を使えます。
// C# 7.0 and later
static int CountValid(string[] codes)
{
int count = 0;
foreach (string code in codes)
{
if (IsValid(code)) count++;
}
return count;
bool IsValid(string c) => c.Length == 6 && char.IsLetter(c[0]);
}
ヘルパーを、クラスの他の部分に公開せずに、唯一の呼び出し元のすぐ隣に置けます。C# 8以降は static を付けて、外側の変数のキャプチャを禁止できます。ローカル関数が使えない場合は、このページの他の例のように、同じクラスのprivateな静的メソッドで同じことができます。
よくある質問
C#のメソッドと関数の違いは何ですか?
C#では、すべての関数はクラス、構造体、レコードの中で宣言され、メソッドと呼ばれます。独立した関数はありません。トップレベルステートメント(C# 9)やローカル関数(C# 7)でさえ、生成された型や外側の型のメソッドにコンパイルされます。「関数」は、くだけた言い方や、ラムダとデリゲートに対して使われます。
C#のparamsは何をするものですか?
params を使うと、メソッドは1つの型の可変個の引数を受け取れます。static int Sum(params int[] numbers) は Sum()、Sum(4)、Sum(4, 5, 6)、Sum(existingArray) のように呼べます。コンパイラが引数を配列にまとめます。params の引数は最後でなければならず、1つのメソッドに1つしか置けません。
C#のMainメソッドとは何ですか?
Main はプログラムのエントリポイントで、ランタイムが最初に呼び出すメソッドです。static でなければならず、void か int(終了コード)を返せ、コマンドライン引数のために string[] args を受け取れます。C# 7.1以降は async Task Main にもでき、C# 9以降はトップレベルステートメントを使えばコンパイラが生成してくれます。
C#のメソッドから複数の値を返すには?
タプルを返します:static (int Min, int Max) Range(int[] values) と return (min, max);。呼び出し側は result.Min を読むか、var (lo, hi) = Range(values); で分解できます。コードの中で常に一緒に扱う値なら、小さなクラスやレコードのほうが明確です。out パラメーターは古くからある代替手段です。
「An object reference is required for the non-static field, method, or property」になるのはなぜですか?
Main のような static メソッドがインスタンスメソッドを直接呼び出したからです。静的メソッドは型に属し、呼び出しの対象となるオブジェクトを持ちません。呼び出されるメソッドに static を付けるか、先にオブジェクトを作ります:var app = new Program(); app.Greet();。
「not all code paths return a value」とはどういう意味ですか?
エラーCS0161です。戻り値の型を持つメソッドに、return なしで閉じ波かっこに到達する経路が少なくとも1つあります。たいていは if が値を返しているのに、その後に else も最後の return もない場合です。メソッドの最後に return(または throw)を追加します。