なぜ、メソッドが存在するのか、その理由
この記事では、当社の新人エンジニア研修の参考に C# を解説します。
前回は「文字と文字列の扱い」について学びました。今回はいよいよメソッドについて解説します。
まず、メソッドとは何か、というところからお話を始めたいと思います。
1. メソッドとは
メソッドを一言で説明すると、「一連の処理に名前を付けたもの」 です。
プログラムが大きくなると、同じようなコードを何度も書くことになりがちです。そうすると冗長になり修正箇所も増えてしまいます。これを避けるために「DRY (Don't Repeat Yourself) 原則」が重要とされます。
メソッドを活用することで、共通の処理を1か所にまとめ、複数箇所で再利用できます。また、「朝のルーティン」などの名前を付けることでコードの意図も分かりやすくなります。
メソッドの基本形
一番シンプルな形として、引数なし・戻り値なしのメソッドを以下のように定義できます。
void メソッド名()
{
// 命令文;
}
void
は「戻り値を返さない」ことを意味します。
2. メソッドの例
C#ではMain()
メソッドもメソッドとして定義されます。これはプログラムのエントリーポイントとしてランタイムから直接呼び出されるため、インスタンスがなくても呼び出せるメソッドである必要があるからです。
ここではシンプルなメソッドを例示します。以下のサンプルでは「1~10まで数える」処理をCount10()
というメソッドにまとめ、Main()
から呼び出しています。
using System;
namespace Chap08
{
public class Example01
{
static void Count10()
{
for (int i = 1; i <= 10; i++)
{
Console.Write(i + " ");
}
}
public static void Main(string[] args)
{
Count10();
}
}
}
<実行結果>
1 2 3 4 5 6 7 8 9 10
Count10();
の呼び出しからCount10()
メソッド本体へ処理がジャンプし、終わるとMain()
に戻ります。
コードの配置
public static void Main(string[] args)
{
Count10();
}
static void Count10()
{
...
}
のように Count10()
を後ろに書いてもOKです。一般的に、Main()
はクラスの一番下に書くのが好ましいというスタイルもありますが、必須ではありません。
メソッドの引数(仮引数と実引数)
メソッドには引数(インプット)と戻り値(アウトプット)を定義できます。ここまでの例では、引数も戻り値もありませんでした。
void メソッド名(型 仮引数)
{
// 処理
}
using System;
namespace Chap08
{
public class Example03
{
static void Count(int num)
{
for (int i = 1; i <= num; i++)
{
Console.Write(i + " ");
}
}
public static void Main(string[] args)
{
Count(5);
Console.WriteLine();
Count(10);
}
}
}
<実行結果>
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
Count(5)
やCount(10)
で呼び出す時の値を「実引数(argument)」と呼び、- メソッド定義の
Count(int num)
のnum
を「仮引数(parameter)」と呼びます。
メソッドの戻り値
戻り値(アウトプット)を持つメソッドは次のように書きます。
戻り値の型 メソッド名(仮引数列)
{
// 処理
return 値;
}
return
文によって呼び出し元に値を返します。void
以外の型を指定した場合、必ずreturn
で対応する型の値を返さなければなりません。
例:正方形の面積を求めるメソッド
using System;
namespace Chap08
{
public class Example04
{
public static void Main()
{
double area = GetAreaOfSquare(3.8);
Console.WriteLine(area); // 14.44
}
static double GetAreaOfSquare(double length)
{
return length * length;
}
}
}
戻り値を複数回 return
する
if
などで分岐し、複数のreturn
文を書いてもOKです。ただし最終的に1つのメソッド呼び出しにつき、どれか1つのreturn
が実行されます。
using System;
namespace Chap08
{
public class Example05
{
public static void Main()
{
Console.WriteLine(IsEven(71)); // false
}
static bool IsEven(int num)
{
if (num % 2 == 0)
{
return true;
}
else
{
return false;
}
}
}
}
<実行結果>
false
- ちなみに
return (num % 2 == 0);
のように省略可能です。
早期return
戻り値が void
のメソッドでも、途中でメソッドを終了させたい場合 return;
と書きます。
static void PrintDoublePositiveNumber(int num)
{
if (num <= 0)
return; // ここでメソッド終了、何も返さない
Console.WriteLine(num * 2);
}
3. メソッドのオーバーロード (Overload)
オーバーロードとは、「同じメソッド名で、引数の数や型が異なる複数のメソッド」 を定義することです。
例: Console.WriteLine
using System;
namespace Chap08
{
public class Example06
{
public static void Main()
{
Console.WriteLine("Hello"); // 引数はstring
Console.WriteLine('A'); // 引数はchar
Console.WriteLine(256); // 引数はint
Console.WriteLine(3.14); // 引数はdouble
}
}
}
WriteLine()
はオーバーロードされており、string
, char
, int
, double
など様々な型を引数として受け取れるようになっています。
自前でオーバーロード
static void Method()
{
Console.WriteLine("引数なしメソッド");
}
static void Method(int i)
{
Console.WriteLine("int型の引数: " + i);
}
static void Method(double d)
{
Console.WriteLine("double型の引数: " + d);
}
- 引数の型・数・並び順が異なれば、同じメソッド名
Method
で定義可能です。 - 戻り値の型が違うだけではオーバーロードにはなりません。必ず引数シグネチャが違う必要があります。
4. メソッドを使うメリット
- 定形処理の再利用
- 同じ処理を何度も書く代わりに、メソッド化して呼び出すだけにすれば、コード量削減&保守性向上。
- コードの意図が明確になる
- 「
Count10()
」と書かれていれば「10まで数える処理」だとすぐ分かる。 - もし処理が複雑でも、名前を見れば機能が推測できる。
- 「
例:同じ処理を3回呼ぶだけ
// メソッドなし
for (int i = 1; i <= 10; i++) { ... }
for (int i = 1; i <= 10; i++) { ... }
for (int i = 1; i <= 10; i++) { ... }
// メソッドあり
Count10();
Count10();
Count10();
後者のほうが見やすく、変更があれば1箇所修正で済みます。
5. メソッドの再帰処理 (Recursion)
メソッドから「自分自身」を呼び出す手法を再帰処理と言います。例えば「階乗(factorial)」の計算: 5!=5×4×3×2×1=120
再帰的に書くと
n! = n * (n-1)!
1! = 1
例: 再帰で階乗を計算
using System;
namespace Chap08
{
public class Example12
{
static int Factorial(int n)
{
if (n <= 1)
return n; // 1または0を返す
return n * Factorial(n - 1); // 自分自身を呼び出し
}
public static void Main()
{
int ans = Factorial(3);
Console.WriteLine(ans); // 6
}
}
}
<実行結果>
6
Factorial(n)
の中でFactorial(n-1)
を呼んでいますが、n==1
になったら再帰を打ち切るので、無限ループにはなりません。
6. メソッドチェーン
メソッドの戻り値に対して、さらにメソッドを呼び出すという連続的な書き方をメソッドチェーン(method chain)と呼びます。C#の文字列処理などでよく見かけます。
Console.WriteLine("hello".ToUpper().Substring(0, 4));
// => "HELL"
"hello".ToUpper()
→"HELLO"
"HELLO".Substring(0, 4)
→"HELL"
このように、都度インスタンスを生成(または取得)し、その戻り値に対してさらにメソッドを呼ぶ連鎖を行う書き方です。変数をいちいち作らなくても処理をまとめられる利点があります。
7. static
キーワードの意味
static修飾子は「インスタンスを生成しなくても呼び出せるメンバ」を示します。
- C#ではクラス名
.
メソッド名() の形で呼び出します(例:Math.Abs(-3)
)。 - インスタンスを介さないので、一度クラスが読み込まれた時点でメモリに割り当てられ、プログラム終了まで保持されます。
メモリ上でのイメージ
- スタック: ローカル変数・メソッド呼び出し(LIFO管理)
- ヒープ: インスタンス(new)で動的に生成されるオブジェクト
- 静的領域(もしくはクラスメンバ領域): staticメンバが置かれ、アプリ終了まで生き続ける
静的メンバは唯一無二の存在としてプログラム中どこからでもアクセスできます。一方、非staticメソッドやフィールドは各インスタンスごとに存在するため、インスタンスの作成/破棄タイミングに応じてヒープ領域を使います。
これらの基礎をしっかり押さえておくことで、C#プログラミングの理解が深まります。
まとめができたら、アウトプットとして演習問題にチャレンジしましょう。
次回は「インスタンスでデータと処理を部品化する」など、よりオブジェクト指向らしいプログラムの書き方を学びます。楽しみにしていてください。