なぜ、メソッドが存在するのか、その理由

この記事では、当社の新人エンジニア研修の参考に C# を解説します。

前回は「 文字列を扱ってユーザーにメッセージを伝える」について学びました。いろいろなメソッドがありましたね。

今回はそのメソッドの作り方について解説します。

まず、メソッドとは何か、というところからお話を始めたいと思います。

1. メソッドとは

メソッドを一言で説明すると、「一連の処理に名前を付けたもの」 です。
プログラムが大きくなると、同じようなコードを何度も書くことになりがちです。そうすると冗長になり修正箇所も増えてしまいます。これを避けるために「DRY (Don't Repeat Yourself) 原則」が重要とされます。

【method】とは、直訳すれば「方法、やり方」といった意味です。メソッドは他の言語では関数と呼ばれることもあります。

直線を表す

y = 2x + 1

という関数のようにxに何かをインプットしたらyがアウトプットされるブラックボックスのようなものが関数(メソッド)だ、と捉えてください。

メソッドを活用することで、共通の処理を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;
        }
    }
}

最終的に1つのメソッド呼び出しにつき1つの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

なお、参考までに上記のisEven()メソッドは簡略化して以下のように書くこともできます。

static boolean isEven(int num) {
  return num % 2 == 0;
}

戻り値のboolean型はtrueかfalseのいずれかの値を持つものであればOKだからです。

早期return

さらにreturn文には「早期return文」と呼ばれる処理の流れを制御する使われ方があります。

早期return文とは、戻り値がvoidであるメソッドの実行中に条件を満たした場合に、その時点でメソッドを終了し、戻り値を返すためのreturn文のことを指します。

例えば、以下のメソッドは、引数として与えられた整数が正の場合に限り、その2倍の値を出力し、それ以外の場合は何も出力せずにメソッドを打ち切ります。以前学んだbreak文と似ていると感じると感じる新人エンジニアの方もいらっしゃるかもしれませんが、break文がブロックを抜けるのに対して、return文はメソッドを抜けます。

早期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. メソッドを使うメリット

  1. 定形処理の再利用
    • 同じ処理を何度も書く代わりに、メソッド化して呼び出すだけにすれば、コード量削減&保守性向上。
  2. コードの意図が明確になる
    • 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. メソッドチェーン

メソッドの戻り値に対して、さらにメソッドを呼び出すという連続的な書き方をメソッドチェーン(method chain)と呼びます。C#の文字列処理などでよく見かけます。

Console.WriteLine("hello".ToUpper().Substring(0, 4)); 
// => "HELL"

  1. "hello".ToUpper()"HELLO"
  2. "HELLO".Substring(0, 4)"HELL"

このように、都度インスタンスを生成(または取得)し、その戻り値に対してさらにメソッドを呼ぶ連鎖を行う書き方です。変数をいちいち作らなくても処理をまとめられる利点があります。

新人エンジニア
Substring(0, 4)の意味

6. static キーワードの意味

static修飾子は「インスタンスを生成しなくても呼び出せるメンバ」を示します。

C#ではクラス名.メソッド名() の形で呼び出します(例: Math.Abs(-3))。

インスタンスを介さないので、一度クラスが読み込まれた時点でメモリに割り当てられ、プログラム終了まで保持されます。

メモリ上でのイメージ

  1. スタティック領域(もしくは静的領域): staticメンバが置かれ、アプリ終了まで生き続ける
  2. スタック領域: ローカル変数・メソッド呼び出し(LIFO管理)
  3. ヒープ領域: インスタンス(new)で動的に生成されるオブジェクト

staticメンバは唯一無二の存在としてプログラム中どこからでもアクセスできます。一方、非staticメソッドやフィールドは各インスタンスごとに存在するため、インスタンスの作成/破棄タイミングに応じてヒープ領域を使います。

新人エンジニア
メモリの3つの領域のイメージ

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□ メソッドは一連の処理に名前を付けたものであり、引数(インプット)・戻り値(アウトプット)を持てる

□ メソッド名が同じで、引数の型や数、並び順が異なる複数のメソッドを定義することをオーバーロード(多重定義)と呼ぶ

□ メソッドを使うことで、定形処理を再利用でき、コードの意図が分かりやすくなる

staticメソッドはクラスが読み込まれた時点でメモリに置かれ、インスタンス不要で呼び出せる

次回は、「9章. インスタンスでデータと処理を再利用可能部品にする」を学びます。

最後までお読みいただきありがとうございます。