自作メソッド(関数)の定義
メソッドを自作する
メソッド(関数)は特定の処理をひとまとめにしたものです。
今までの説明ではC#に最初から用意されている「Console.WriteLine」と「Console.ReadLine」の二つのメソッドを使用してきましたが、メソッドは自分で作ることもできます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
//Mainメソッド
static void Main(string[] args)
{
int a = 10, b = 20;
int ret = Sum(a, b); //自作メソッドSumの実行
Console.WriteLine("メソッドSumの戻り値: " + ret);
Console.ReadLine();
}
//自作メソッドSum
static int Sum(int number1, int number2)
{
int sum = number1 + number2;
Console.WriteLine("メソッドSumの実行結果: " + sum);
return sum;
}
}
}
上のコード、24行目から29行目までが「Sum」という名前の自作メソッドの定義部分です。
このコードの実行結果は以下になります。
メソッドSumの実行結果: 30 メソッドSumの戻り値: 30
コードの実行順
C#のコードはMainメソッドから始まり、その後は基本的に上から順番に実行されていきます。
しかし途中でメソッドを実行すると、処理はメソッド内に移ります。
つまり、上のコードの実行順を行番号で記すと、
14、15→(メソッドSumへ)→23、24、25→(Mainメソッドへ)→15、16、17
となります。
15行目が二回出現していますが、これは「メソッドの実行」と「メソッドの実行結果の変数への代入」で同じ行が二回実行されるためです。
メソッドの作り方
メソッドを作るにはMainメソッドの隣に以下の書式で記述します。
(Mainメソッドの前後どちらでも良い)
static 戻り値のデータ型 メソッド名(引数1, 引数2,...)
{
//メソッドの処理
//.
//.
//.
return 戻り値;
}
このサンプルコードでの自作メソッドSumは、外部から値(引数)をふたつ受け取り、それを足し算した結果を返すメソッドです。
以下、順に用語の意味を説明します。
「Mainメソッドの隣に記述する」というのは必ずしも正しくはないのですが、今はそう考えておいてください。
static
staticというキーワードはメソッドの定義に必須なわけではありません。
しかし、C#ではとりあえずメソッド定義の最初にstaticを書く、と覚えてください。
これの意味はクラスの機能説明で改めて解説します。
戻り値のデータ型
メソッドは、メソッド内で行った処理の結果を戻り値という形でメソッドの呼び出し元に返すことができます。
「データ型」は変数の項で説明したデータ型と同じで、int型やstring型などの任意のデータ型を指定することができます。
サンプルコードのSumメソッドでは、int型同士の足し算の結果を得るために戻り値の型にint型を指定しています。
処理結果が必要ない場合
メソッドによっては処理結果を返す必要がないことがあります。
その場合はデータ型にはvoidという型を指定します。
「void」は「空」を意味し、何もないことを示す特殊なデータ型です。
//int型を返すメソッド
static int Test1()
{
}
//何も値を返さないメソッド
static void Test2()
{
}
メソッド名
メソッド名は基本的に自由につけることができます。
メソッドの処理内容を表すわかりやすい名前を付けると良いでしょう。
ただし、変数名の時と同じく「半角英数文字とアンダースコアが使える」「先頭文字に数値は使えない」「C#が使用しているいくつかのキーワードは使えない」というルールがあります。
変数やメソッドなどの「名前」は基本的に同じものは付けられません。
(例外はあります)
これは、コンパイラは「名前」でそれぞれの変数やメソッドを見分けているからです。
変数名やメソッド名のことを識別子といいます。
引数
メソッドは、外部から値を受け取ってその値をメソッド内で使用することができます。
これを引数といいます。
引数は「そのメソッド内でのみ使用できる、最初から初期化されている変数」と考えるといいでしょう。
初期化する値はメソッドの呼び出し側で指定します。
引数の定義の仕方は、メソッド名の後に丸括弧を書き、その中に記述します。
引数にもデータ型がありますから、データ型に続いて引数名を書きます。
これは変数の定義と同じようなものと考えて構いません。
引数が複数ある場合は「,」(セミコロン)で区切り、二つ目以降を書きます。
引数は何個定義しても構いません。
引数が複数ある場合、一つ目の引数を第一引数、二つ目を第二引数、以降は第三、第四...と言います。
メソッドが要求する「引数の数」「データ型の種類」「引数の並び順」を引数リストといいます。
サンプルコードのメソッドSumでは「number1」「number2」という二つのint型の引数を定義し、メソッド内で足し算に利用しています。
仮引数と実引数
メソッドの定義側で指定する引数を仮引数といいます。
メソッドの呼び出し側で指定する引数(実際の値)を実引数といいます。
メソッドの実行時に、実引数の値が仮引数にコピーされ、メソッド内で使用することができます。
static void Main(string[] args)
{
//「2」「4」が実引数
Test(2, 4);
}
//「num1」「num2」が仮引数
//メソッドの実行時に「2」と「4」の値が入る
static int Test(int num1, int num2)
{
return num1 + num2;
}
引数を必要としないメソッド
引数は必ず指定しなければならないものではありません。
必要がなければ空にしておきます。
ただし、中身が空でも丸括弧を省略することはできません。
//引数を取らず、int型を返すメソッド
static int Test()
{
return 0;
}
もちろん、引数のないメソッドを呼び出す側も丸括弧は省略することはできません。
//引数を取らず、int型を返すメソッド
static int Test()
{
return 0;
}
static void Main(string[] args)
{
//「Test」メソッドの実行
Test();
}
メソッドの呼び出し側で記述される丸括弧は関数呼び出し演算子と言います。
これがないとメソッドは実行されません。
(特定の場合を除き、丸括弧を書かずにメソッド名だけを記述してもエラーになります)
メソッド内の処理
引数を書き終えたら波括弧を書きます。
この波括弧の中にメソッドの処理を書いていきます。
この波括弧で囲われた領域をブロックというので覚えておいてください。
ブロックは処理の区切りを作成するもので、メソッドの定義以外でも度々使用されます。
ブロックの内部には複数の行を含めることができます。
return
最後に、return文で戻り値を指定します。
メソッドの定義で最初に書いた「戻り値のデータ型の指定」と同じデータ型の値をreturn文に指定します。
ここで指定した値がメソッドの処理結果として返されることになります。
static void Main(string[] args)
{
//メソッドTestの実行結果をint型変数retに代入
int ret = Test(2, 4);
//「6」を表示
Console.WriteLine(ret);
}
static int Test(int num1, int num2)
{
//num1とnum2を足した値を返す
return num1 + num2;
}
メソッドの戻り値は必要がなければ受け取らなくても構いません。
また、void型のメソッドは戻り値がないので受け取ることはできません。
static void Main(string[] args)
{
//戻り値は受け取らなくても良い
Test1(2, 4);
//void型メソッドの戻り値は受け取れない
//↓はエラー
//int ret = Test2(2, 4);
}
static int Test1(int num1, int num2)
{
int ret = num1 + num2;
Console.WriteLine(ret);
return ret;
}
//値を返さないvoid型
static void Test2(int num1, int num2)
{
int ret = num1 + num2;
Console.WriteLine(ret);
}
戻り値のデータ型とreturn文で指定するデータ型とが一致しないとエラーになります。
//int型を返すメソッド
static int Test1()
{
//戻り値に文字列を指定しているのでエラー
return "あいうえお";
}
return文はメソッドを終了させる
return文を実行すると、そこでメソッドは終了します。
return文以降にどれだけメソッドの処理を書いていようと関係なくメソッドの処理は終了します。
static int Test()
{
//何か処理
return 5; //ここでメソッドの処理は終わり
//return文以降は何を書いても
//プログラムの動作に影響しない
Console.WriteLine("この文字列は出力されない");
}
void型メソッドのreturn文
戻り値のデータ型にvoid型指定した場合はreturn文は必要ありません。
return文を書くことはできますが、何も値を指定できません。
//void型のメソッドはreturn文は必要ない
static void Test1()
{
//何か処理
}
//return文を書く場合は値を指定しない
static void Test2()
{
//何か処理
//↓これはエラー
//return 5;
return;
}
//return文を利用して処理を途中で強制終了することもできる
static void Test3()
{
//何か処理
return; //ここでメソッドの処理は終了
Console.WriteLine("この文字列は出力されない");
}
メソッドの使いどころ
今回のサンプルコードは単なる足し算ですので、わざわざメソッド化する意味はありません。
足し算程度の処理はそのまま書いたほうが早く、見た目にもわかりやすいでしょう。
メソッドは呼び出しにわずかながらコストがかかるので無駄も多くなります。
ある程度複雑なプログラムになってくると、コード中のいろいろなところで同じような処理が出現することがあります。
ほとんど同じ処理なのに毎回同じようなコードを書いていたのでは効率が悪いです。
コードの行数も多くなりますし、コンパイル後の実行ファイルのサイズも大きくなります。
そのような場合に、共通化できる処理をメソッドとしてまとめてしまえば、必要な場所からメソッドを呼び出すことができるようになります。
引数を変えればある程度処理内容も変えることができます。
仮にメソッド内の処理にバグがあっても、修正するのはそのメソッドだけで済みます。
もしメソッド化していないと、同じようなコードを書いた場所すべてを修正する必要があります。
作業効率が悪いだけでなく、修正漏れや記述ミスなどによりバグが混入する可能性も高くなってしまいます。
共通化できそうな処理があったらできるだけメソッドにしてしまうと良いでしょう。
また、メソッド「処理に名前(メソッド名)を付けることができる」というメリットもあります。
数行で済むような簡単な処理でも、あえてメソッド化して名前を付けコードの意味を明確にすることもあります。
ステップインによるメソッド内部のデバッグ
デバッグ機能の基本で軽く触れた通り、デバッグの実行方法にはステップインとステップオーバーの二種類があります。
ステップアウト実行は単純にコードを上から順番に実行します。
ステップイン実行は自作メソッドに差し掛かった時にそのメソッドの内部に処理を移し、メソッド内のコードも一行ずつ実行していきます。
イメージとしては下図の赤矢印の順に一行ずつ実行していくことができます。
ステップイン実行ができるのは自作メソッドなどのソースコードが手元にあるものだけです。
例えばConsole.WriteLineメソッドなどはソースコードがないのでステップインで内部に移動することはできません。
ちなみにステップイン実行のショートカットキーは「F11」です。
ステップアウト
ステップイン実行やメソッドの内部にブレークポイントを設定した場合などで「現在実行している行」が(Mainメソッド以外の)メソッド内にあるとき、ステップアウトを実行するとそのメソッドの終了直後まで一気に処理を進めることができます。
ステップアウトのショートカットキーは「Shift + F11」です。