.NET Visual C#言語入門 > メソッド
のぶ亭『プログラミングの相談窓口』 … 様々なプログラミング問題を個別対応致します |
メソッド
メソッドは、一連のステートメントを含むコード・ブロックです。C# では、実行されるすべての命令がメソッドのコンテキストで実行されます。
メソッドは、クラスまたは構造体の中で、アクセス レベル、戻り値、メソッドの名前、およびメソッド パラメータを指定して宣言します。メソッド パラメータはかっこで囲み、コンマで区切って指定します。メソッドでパラメータが不要な場合は、かっこ内を空にします。次に 3 つのメソッドを含むクラスの例を示します。
class Motorcycle { public void StartEngine() { } public void AddGas(int gallons) { } public int Drive(int miles, int speed) { return 0; } }
オブジェクトでメソッドを呼び出すのは、フィールドにアクセスするのと似ています。オブジェクト名の後に、ピリオド、メソッド名、かっこを追加します。引数はかっこの中に記述し、コンマで区切ります。
Motorcycle クラスのメソッドは、次のように呼び出すことができます。
Motorcycle moto = new Motorcycle();
moto.StartEngine();
moto.AddGas(15);
moto.Drive(5, 20);
メソッド・パラメータ
上のコード スニペットに示されているように、メソッドに引数を渡す場合は、メソッドを呼び出すときにかっこの中に引数を入力するだけで済みます。呼び出されるメソッドの側から見た場合、入ってくる引数をパラメータと言います。
メソッドが受け取るパラメータもかっこ内に指定しますが、パラメータごとに型と名前を指定する必要があります。この名前は、引数と同じにする必要はありません。次に例を示します。
public static void PassesInteger() { int fortyFour = 44; TakesInteger(fortyFour); } static void TakesInteger(int i) { i = 33; }
この例では、PassesInteger というメソッドが TakesInteger というメソッドに引数を渡します。PassesInteger では、引数は fortyFour という名前ですが、TakeInteger では、この引数は i という名前のパラメータです。このパラメータは、TakesInteger メソッドの中だけに存在します。このメソッドの内部で宣言されたパラメータまたは変数でない限り、他の任意の数の変数に i という名前を付け、任意の型を指定できます。
TakesInteger は、指定された引数に新しい値を代入します。この変更は、TakeInteger から制御が戻ると PassesInteger メソッドに反映するように思われますが、実際には、fortyFour 変数の値は変更されません。これは、int が値型であるためです。既定では、値型がメソッドに渡されるときは、オブジェクト自体ではなく、そのコピーが渡されます。そのため、パラメータに対して行われた変更は、呼び出し元のメソッドに影響しません。値型という名前は、オブジェクト自体ではなく、オブジェクトのコピーが渡されるという事実に基づくものです。値は渡されますが、同じオブジェクトではありません。
値型は、参照渡しされる参照型と異なります。参照型に基づくオブジェクトがメソッドに渡される場合、オブジェクトのコピーは作成されません。代わりに、メソッドの引数として使用されるオブジェクトへの参照が作成され、渡されます。そのため、この参照を通じて行われた変更は、呼び出し元のメソッドに反映されます。参照型を作成するときは、class キーワードを使用します。この例を次に示します。
public class SampleRefType { public int value; }
この型に基づくオブジェクトがメソッドに渡される場合は、参照渡しされます。次に例を示します。
public static void TestRefType() { SampleRefType rt = new SampleRefType(); rt.value = 44; ModifyObject(rt); System.Console.WriteLine(rt.value); } static void ModifyObject(SampleRefType obj) { obj.value = 33; }
この例は、基本的に前の例と同じです。しかし、参照型を使用しているため、ModifyObject によって行われた変更は、TestRefType メソッドで作成されたオブジェクトに反映されます。そのため、TestRefType メソッドは、値として 33 を表示します。
戻り値
メソッドは、呼び出し元に値を返すことができます。戻り値の型 (メソッド名の前に記述されている型) が void でない場合、メソッドは、return キーワードを使用して値を返すことができます。return キーワードと、その後に戻り値の型に一致する値が記述されたステートメントは、その値をメソッドの呼び出し元に返します。また、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);
obj.SquareANumber(result);
obj.SquareANumber(obj.AddTwoNumbers(1, 2));
中間変数 (上の例では、result) を使用して値を格納する処理は、省略可能です。中間変数を使用すると、コードが読みやすくなり、また値を繰り返し使用する場合は、必要になることもあります。
パラメータの引き渡し
C# では、パラメータは、値または参照で渡されます。パラメータを参照で渡すと、関数メンバ (メソッド、プロパティ、インデクサ、演算子、およびコンストラクタ) はパラメータの値を変更し、その変更を永続化できます。パラメータを参照で渡すには、ref キーワードまたは out キーワードを使用します。ここでは、説明を簡単にするために、例に ref キーワードだけを使用しています。
// Passing by value static void Square(int x) { // code... }
// Passing by reference static void Square(ref int x) { // code... }
値型のパラメータの引き渡し
参照型の変数はデータへの参照を持つのに対し、値型の変数はデータを直接格納します。このため、値型変数をメソッドに渡すことは、メソッドに変数のコピーを渡すことを意味します。メソッド内でパラメータが変更されても、変数に格納されている元のデータには影響しません。呼び出されたメソッドでパラメータの値を変更する場合は、ref キーワードまたは out キーワードを使用して、パラメータを参照で渡す必要があります。説明を簡単にするために、次の例では ref だけを使用しています。
例 : 値による値型の引き渡し
値によって値型を渡す方法を次の例で示します。変数 n は、SquareIt メソッドに値で渡されます。メソッド内で変更があっても、元の変数値には影響しません。
class PassingValByVal { // The parameter x is passed by value. // Changes to x will not affect the original value of x. static void SquareIt(int x) { x *= x; System.Console.WriteLine("The value inside the method: {0}", x); } static void Main() { int n = 5; System.Console.WriteLine("The value before calling the method: {0}", n); SquareIt(n); // Passing the variable by value. System.Console.WriteLine("The value after calling the method: {0}", n); } }
出力
The value inside the method: 25
The value after calling the method: 5
コードの説明
値型の変数 n には、データ (値は 5) が格納されています。SquareIt が呼び出されると、n の内容がパラメータ x にコピーされ、値はメソッド内で 2 乗されます。ただし、Main では SquareIt メソッドの呼び出し前後で n の値は変わりません。実際、メソッド内での変更は、ローカル変数 x だけに影響します。
例 : 参照による値型の引き渡し
ref キーワードを使用してパラメータを渡すことを除けば、次の例は前の例と同じです。パラメータの値は、メソッドの呼び出し後に変更されています。
class PassingValByRef { static void SquareIt(ref int x) // The parameter x is passed by reference. // Changes to x will affect the original value of x. { x *= x; System.Console.WriteLine("The value inside the method: {0}", x); } static void Main() { int n = 5; System.Console.WriteLine("The value before calling the method: {0}", n); SquareIt(ref n); // Passing the variable by reference. System.Console.WriteLine("The value after calling the method: {0}", n); } }
出力
The value inside the method: 25
The value after calling the method: 25
コードの説明
この例では、n の値ではなく、n への参照が渡されています。パラメータ x は int ではなく、int への参照 (例では、n への参照) です。このため、メソッド内で x が 2 乗されると、x が参照している n が 2 乗されます。
例 : 値型の交換
渡されたパラメータの値を変更する例として一般的なのは、Swap メソッドです。このメソッドに x と y の 2 つの変数を渡すと、内容が交換されます。Swap メソッドにはパラメータを参照で渡す必要があります。参照で渡さないと、メソッド内ではパラメータのローカル コピーを処理することになります。参照のパラメータを使用する Swap メソッドの例を次に示します。
static void SwapByRef(ref int x, ref int y) { int temp = x; x = y; y = temp; }
このメソッドを呼び出すときは、ref キーワードを次のように使用します。
static void Main() { int i = 2, j = 3; System.Console.WriteLine("i = {0} j = {1}" , i, j); SwapByRef (ref i, ref j); System.Console.WriteLine("i = {0} j = {1}" , i, j); }
出力
i = 3 j = 2
参照型のパラメータの引き渡し
参照型の変数には、データが直接格納されず、データへの参照が格納されます。参照型のパラメータを値で渡すときには、クラス メンバの値など、参照によって指されるデータを変更できます。ただし、参照自身の値は変更できません。つまり、同じ参照を使用して、新しいクラスのメモリを割り当て、ブロックの外で永続化させることはできません。参照自身の値を変更するには、ref キーワードまたは out キーワードを使用してパラメータを渡します。説明を簡単にするために、次の例では ref だけを使用しています。
例 : 値による参照型の引き渡し
参照型のパラメータ arr を値で Change メソッドに渡す方法を、次の例で示します。パラメータは arr への参照であるため、配列要素の値を変更できます。ただし、他のメモリ位置へのパラメータの再割り当ては、メソッド内だけで有効です。元の変数 arr には影響しません。
class PassingRefByVal { static void Change(int[] pArray) { pArray[0] = 888; pArray = new int[5] {-3, -1, -2, -3, -4}; System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]); } static void Main() { int[] arr = {1, 4, 5}; System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]); Change(arr); System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]); } }
出力
Inside Main, before calling the method, the first
element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is:
888
コードの説明
上の例では、参照型の配列 arr は、ref パラメータを指定せずにメソッドに渡されています。このような場合は、arr を指す参照のコピーがメソッドに渡されます。出力は、メソッドが配列要素の内容を (この場合は 1 から 888 へ) 変更できることを示しています。ただし、Change メソッド内で new 演算子を使用して新しいメモリ領域を割り当てると、変数 pArray は新しい配列を参照します。したがって、新しい領域の割り当ての後に変更があっても、Main 内で作成されている元の配列 arr は影響を受けません。この例では Main メソッド内と Change メソッド内で 2 つの配列が作成されています。
例 : 参照による参照型の引き渡し
メソッド ヘッダーと呼び出しで ref キーワードを使用していることを除けば、この例は前の例と同じです。メソッド内での変更は、呼び出しプログラム内の元の変数に影響します。
class PassingRefByRef { static void Change(ref int[] pArray) { pArray[0] = 888; pArray = new int[5] {-3, -1, -2, -3, -4}; System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]); } static void Main() { int[] arr = {1, 4, 5}; System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]); Change(ref arr); System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]); } }
出力
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: -3
コードの説明
例 : 2 つの文字列の交換
文字列の交換は、参照によって参照型のパラメータを渡す方法の良い例です。次の例では、str1 と str2 の 2 つの文字列が Main で初期化され、ref キーワードを使用してパラメータとして SwapStrings メソッドに渡されています。2 つの文字列はこのメソッド内で交換され、Main 内でもその結果が反映されています。
class SwappingStrings { static void SwapStrings(ref string s1, ref string s2) // The string parameter is passed by reference. // Any changes on parameters will affect the original variables. { string temp = s1; s1 = s2; s2 = temp; System.Console.WriteLine("Inside the method: {0} {1}", s1, s2); } static void Main() { string str1 = "John"; string str2 = "Smith"; System.Console.WriteLine("Inside Main, before swapping: {0} {1}", str1, str2); SwapStrings(ref str1, ref str2); // Passing strings by reference System.Console.WriteLine("Inside Main, after swapping: {0} {1}", str1, str2); } }
出力
Inside Main, before swapping: John Smith
Inside the method: Smith John
Inside Main, after swapping: Smith John