メソッド (C# プログラミング ガイド)
メソッドは、一連のステートメントが含まれているコード ブロックです。 必要なメソッド引数を指定してプログラムからメソッドを呼び出すと、メソッド内のステートメントが実行されます。 C# では、実行されるすべての命令がメソッドのコンテキストで実行されます。
Main
メソッドは、すべての C# アプリケーションのエントリ ポイントです。プログラムが開始されると、このメソッドが共通言語ランタイム (CLR) によって呼び出されます。 最上位レベルのステートメントを使用するアプリケーションでは、Main
メソッドはコンパイラによって生成され、すべての最上位レベルのステートメントが含まれます。
注意
この記事では、名前付きメソッドについて説明します。 匿名関数について詳しくは、「ラムダ式」をご覧ください。
メソッド シグネチャ
メソッドは、クラス、構造体、またはインターフェイス内で、アクセス レベル (public
や private
など)、オプションの修飾子 (abstract
や sealed
など)、戻り値、メソッドの名前、およびメソッド パラメーターを指定して宣言されます。 これらのまとまりがメソッドのシグネチャとなります。
重要
メソッドのオーバーロードを可能にするために、メソッドの戻り値の型はメソッドのシグネチャには含まれません。 ただし、デリゲートとそれが指すメソッドの互換性を決定する場合には、メソッドのシグネチャの一部となります。
メソッド パラメーターはかっこで囲み、各パラメーターをコンマで区切ります。 かっこ内を空にすると、メソッドでパラメーターが不要なことを意味します。 このクラスには次の 4 つのメソッドが含まれています。
abstract class Motorcycle
{
// Anyone can call this.
public void StartEngine() {/* Method statements here */ }
// Only derived classes can call this.
protected void AddGas(int gallons) { /* Method statements here */ }
// Derived classes can override the base class implementation.
public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }
// Derived classes must implement this.
public abstract double GetTopSpeed();
}
メソッド アクセス
オブジェクトでメソッドを呼び出すのは、フィールドにアクセスするのと似ています。 オブジェクト名の後に、ピリオド、メソッド名、かっこを追加します。 引数はかっこの中に記述し、コンマで区切ります。 Motorcycle
クラスのメソッドの呼び出し例を次に示します。
class TestMotorcycle : Motorcycle
{
public override double GetTopSpeed()
{
return 108.4;
}
static void Main()
{
TestMotorcycle moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
moto.Drive(5, 20);
double speed = moto.GetTopSpeed();
Console.WriteLine("My top speed is {0}", speed);
}
}
メソッドのパラメーターと引数
メソッド定義には、必要なパラメーターの名前と型を指定します。 呼び出し元のコードからメソッドを呼び出すときに、各パラメーターに引数と呼ばれる具体的な値を指定します。 引数にはパラメーター型との互換性が必要ですが、呼び出し元のコードで引数名を使用する場合、引数名がメソッドで定義されるパラメーター名と同じである必要はありません。 次に例を示します。
public void Caller()
{
int numA = 4;
// Call with an int variable.
int productA = Square(numA);
int numB = 32;
// Call with another int variable.
int productB = Square(numB);
// Call with an integer literal.
int productC = Square(12);
// Call with an expression that evaluates to int.
productC = Square(productA * 3);
}
int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
参照渡しと値渡し
既定では、値の型のインスタンスがメソッドに渡されるときは、インスタンス自体ではなく、そのコピーが渡されます。 したがって、引数に加えた変更は、呼び出し元のメソッドにある元のインスタンスには影響しません。 値の型インスタンスを参照で渡すには、ref
キーワードを使用します。 詳細については、「値型パラメーターの引き渡し」を参照してください。
参照型のオブジェクトがメソッドに渡されると、オブジェクトへの参照が渡されます。 つまり、メソッドは、オブジェクト自体ではなく、オブジェクトの場所を示す引数を受け取ります。 この参照を使用してオブジェクトのメンバーを変更した場合は、オブジェクトを値で渡しても、呼び出し元のメソッドの引数に変更が反映されます。
class
キーワードを使用して参照型を作成する例を次に示します。
public class SampleRefType
{
public int value;
}
この型に基づくオブジェクトをメソッドに渡す場合は、オブジェクトへの参照が渡されます。 次の例では、SampleRefType
型のオブジェクトをメソッド ModifyObject
に渡します。
public static void TestRefType()
{
SampleRefType rt = new SampleRefType();
rt.value = 44;
ModifyObject(rt);
Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj)
{
obj.value = 33;
}
この例は、基本的に前の例と同様に、引数を値でメソッドに渡しています。 しかし、参照型を使用しているため、結果は異なります。 ModifyObject
のパラメーター value
の obj
フィールドで行われた変更によって、 value
メソッドの引数 rt
の TestRefType
フィールドも変更されます。 TestRefType
メソッドは出力として 33 を表示します。
参照型を参照渡しまたは値渡しで渡す方法の詳細については、「参照型パラメーターの引き渡し」と「参照型」を参照してください。
戻り値
メソッドは、呼び出し元に値を返すことができます。 戻り値の型 (メソッド名の前に記述されている型) が void
でない場合、メソッドは、return
ステートメントを使用して値を返すことができます。 return
キーワードに続いて戻り値の型に一致する値が記述されたステートメントは、その値をメソッドの呼び出し元に返します。
値を呼び出し元に返す方法には、値によって返す方法と、参照によって返す方法があります。 値が参照によって呼び出し元に返されるのは、ref
キーワードがメソッド シグネチャで使用されていて、そのキーワードが各 return
キーワードの後に続いている場合です。 たとえば、次のメソッド シグネチャと return ステートメントは、メソッドが変数名 estDistance
を参照によって呼び出し元に返すことを示しています。
public ref double GetEstimatedDistance()
{
return ref estDistance;
}
また、 return
キーワードは、メソッドの実行を中止します。 戻り値の型が void
の場合、値を持たない return
ステートメントは、メソッドの実行を中止するときに役立ちます。 return
キーワードを使用しない場合、メソッドは、コード ブロックの最後に到達したときに実行を中止します。 戻り値の型が void 以外のメソッドで値を返すには、 return
キーワードを使用する必要があります。 たとえば、次の 2 つのメソッドは、 return
キーワードを使用して整数を返します。
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2)
{
return number1 + number2;
}
public int SquareANumber(int number)
{
return number * number;
}
}
メソッドから返された値を使用する場合、呼び出し元のメソッド内で同じ型の値を使用している場所では、メソッド呼び出し自体を値として使用できます。 戻り値は、変数に代入することもできます。 たとえば、次の 2 つのコードでは、同様の結果が得られます。
int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);
この場合、ローカル変数 result
を使用して値を格納する手順はオプションです。 このローカル変数によってコードの読みやすさが向上することがあります。また、引数の元の値をメソッドのスコープ全体で保持する場合に必要になることがあります。
メソッドから参照によって返された値を使用する場合、値を変更するには、ref ローカル変数を宣言する必要があります。 たとえば、Planet.GetEstimatedDistance
メソッドが Double の値を参照によって返す場合は、次のようなコードを使用して、その値を ref ローカル変数として定義できます。
ref double distance = ref Planet.GetEstimatedDistance();
呼び出し元の関数から、配列の内容を変更するメソッド M
に配列が渡された場合、M
から多次元配列を返す必要はありません。 値の適切なスタイルまたは機能フローのために M
から結果の配列を返すことはできますが、必須ではありません。変更された配列を返す必要がないのは、C# ではすべての参照型が値で渡され、配列参照の値がその配列へのポインターになるためです。 メソッド M
では、次の例に示すように、配列の内容に対する変更は、配列への参照を含むコードによって監視できます。
static void Main(string[] args)
{
int[,] matrix = new int[2, 2];
FillMatrix(matrix);
// matrix is now full of -1
}
public static void FillMatrix(int[,] matrix)
{
for (int i = 0; i < matrix.GetLength(0); i++)
{
for (int j = 0; j < matrix.GetLength(1); j++)
{
matrix[i, j] = -1;
}
}
}
非同期メソッド
非同期機能を使用することによって、明示的なコールバックを使用せずに、または複数のメソッドやラムダ式にわたって手動でコードを分割することなく、非同期メソッドを呼び出すことができます。
メソッドに async 修飾子を付けると、そのメソッドで await 演算子を使用できます。 コントロールが非同期メソッドの await 式に到達すると、コントロールは呼び出し元に戻り、待機中のタスクが完了するまでメソッドの進行状況は中断されます。 タスクが完了すると、メソッドで実行を再開できます。
注意
非同期メソッドは、まだ完了していない待機中の最初のオブジェクトに達するか、または非同期メソッドの最後に達すると、呼び出し元に戻ります。
非同期メソッドの戻り値の型は一般に、Task<TResult>、Task、IAsyncEnumerable<T>、または void
になります。 戻り値の型 void
は主として、戻り値の型 void
が必要なイベント ハンドラーの定義に使用されます。 void
を返す非同期メソッドは待機できません。void を返すメソッドの呼び出し元は、このメソッドがスローする例外をキャッチできません。 非同期メソッドはタスクと同様の戻り値の型を持つことができます。
次の例で、 DelayAsync
は戻り値の型が Task<TResult>である非同期メソッドです。 DelayAsync
には、整数を返す return
ステートメントがあります。 そのため、メソッド宣言 DelayAsync
では、戻り値の型を Task<int>
とする必要があります。 戻り値の型が Task<int>
であるため、ステートメント await
に示すように、 DoSomethingAsync
内の int result = await delayTask
式を評価すると整数が生成されます。
Main
メソッドは、戻り値の型が Task の非同期メソッドの例です。 これは DoSomethingAsync
メソッドに進みます。1 行で表現されるため、キーワードの async
と await
を省略できます。 DoSomethingAsync
が非同期メソッドであるため、 DoSomethingAsync
を呼び出すタスクは、ステートメント await DoSomethingAsync();
に示すように待機する必要があります。
class Program
{
static Task Main() => DoSomethingAsync();
static async Task DoSomethingAsync()
{
Task<int> delayTask = DelayAsync();
int result = await delayTask;
// The previous two statements may be combined into
// the following statement.
//int result = await DelayAsync();
Console.WriteLine($"Result: {result}");
}
static async Task<int> DelayAsync()
{
await Task.Delay(100);
return 5;
}
}
// Example output:
// Result: 5
非同期メソッドで ref パラメーターまたは out パラメーターを宣言することはできませんが、これらのパラメーターを持つメソッドを呼び出すことはできます。
非同期メソッドの詳細については、「async および await を使用した非同期プログラミング」と非同期の戻り値の型に関するページを参照してください。
式本体の定義
メソッドの定義としては、式の結果を即座に返すか、またはメソッドの本文として 1 つのステートメントを含むものが一般的です。 =>
を使用してこのようなメソッドを定義するための構文ショートカットがあります。
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);
メソッドが void
を返すか、非同期メソッドである場合は、メソッドの本文を (ラムダの場合と同様に) ステートメント式にする必要があります。 プロパティとインデクサーは読み取り専用にする必要があるため、 get
アクセサー キーワードは使用しないでください。
Iterators
反復子は、リストや配列など、コレクションに対するカスタム イテレーションを実行します。 反復子は、 yield return ステートメントを使用して、各要素を 1 回に1 つ返します。 yield return
ステートメントに達すると、コードの現在の場所が記憶されます。 反復子が次回呼び出されたとき、この場所から実行が再開されます。
foreach ステートメントを使用して、クライアント コードから反復子を呼び出します。
反復子の戻り値の型には、IEnumerable、IEnumerable<T>、IAsyncEnumerable<T>、IEnumerator または IEnumerator<T> を指定できます。
詳細については、「 反復子」を参照してください。
C# 言語仕様
詳細については、「C# 言語の仕様」を参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。
関連項目
.NET