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

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

前回は「 基礎② 演算子」について学びました。今回はメソッドついて解説します。

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

1. メソッドとは

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

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

直線を表す方程式

y = 2x + 1

皆さんも数学でこんな式を習いましたよね?これをメソッドの形に直すと、こうなります。

int line(int x) {

return 2x + 1;

}

上の例は、x座標を渡すと、y座標を返してくれる、というメソッドです。何かを入力(インプット)したら何かを出力(アウトプット)するブラックボックスのようなものが関数(メソッド)だ、と捉えてください。

上記の例は、一つのint型の入力値(x)を持ち、int型の値(2x + 1)を返すメソッドの例です。メソッドの入力値、つまり()の中に書いてある変数を、引数(ひきすう)と呼びます。また、return文でメソッドが返す値のことを、戻り値又は返値と呼びます。returnは英語で「返る」という意味ですね。メソッド内てreturnと書くと、メソッドを呼び出した処理に値を渡すことが出来ます。

メソッドを活用することで、共通の処理を1か所にまとめ、複数箇所で再利用できます。また、「朝のルーティン」などの名前を付けることでコードの意図も分かりやすくなります。また、メソッドは、アプリケーションの二大構成要素である情報と処理のうち、処理の部分の大半を記述する大変重要な要素です。単純な例から少しずつ複雑なパターンをご紹介しますので、しっかり理解してください。

メソッドの基本形

一番シンプルな形として、引数なし・戻り値なしのメソッドを以下のように定義できます。

void メソッド名() {
  // 命令文;
}

void は「戻り値(メソッドの出力)を返さない」ことを意味します。

2. メソッドの例

まず、シンプルなメソッドを例示します。以下のサンプルでは「1~10まで数える」処理をCount10()というメソッドにまとめています。前回と同じように、コンソールアプリを選び、プロジェクト名は Chap04 としてプロジェクトを作成し、サンプルプログラムを実行する準備を行ってください。

namespace Chap04;
using System;

public class Example01 {
    static void Count10() {
        for (int i = 1; i <= 10; i++) {
            Console.Write(i + " ");
        }
    }
}

Program.csのトップレベルステートメントを以下のようにします。

// See https://aka.ms/new-console-template for more information
// Console.WriteLine("Hello, World!");
using Chap04;

Example01.Count10();

<実行結果>

1 2 3 4 5 6 7 8 9 10

Count10(); の呼び出しから Count10() メソッド本体へ処理がジャンプし、その処理が終われば、トップレベルステートメントに戻ってアプリケーションの実行が終了します。

メソッドの命名規則

C#では、パスカルケースでメソッドの命名を行うことが一般的です。パスカルケースとは、先頭文字を大文字にしたキャメルケースのことです。上の例でも、Count(10)と、cが大文字になっていますね。

メソッドの引数(仮引数と実引数)

次に引数のあるメソッドを見てみましょう。

<構文>

void メソッド名(型 仮引数) {
  // 処理
}

()カッコの中に型と変数名を書きます。

この変数名を仮の引数という意味で「仮引数」と呼びます。なぜなら、実際に渡される値はまだ決まっていないからです。仮引数という呼び名は、「このメソッドの中では、仮にこの変数名を使って処理を記述しますよ」という意味を持っているわけです。

namespace Chap04;
using System;

public class Example02 {
    static void Count(int num) {
        for (int i = 1; i <= num; i++) {
            Console.Write(i + " ");
        }
    }
}

トップレベルステートメント

using Chap08;
Example02.Count(5);
Console.WriteLine();
Example02.Count(10);

<実行結果>

1 2 3 4 5
1 2 3 4 5 6 7 8 9 10

  • Count(5)Count(10) の5や10を「実引数(argument)」と呼びます。
  • メソッド定義の Count(int num)num は、前述の「仮引数(parameter)」です。

メソッドの戻り値

戻り値(アウトプット)を持つメソッドは次のように書きます。

戻り値の型 メソッド名(仮引数列) {
  // 処理
  return 値;
}


return 文によって呼び出し元に値を返します。

void 以外の型のメソッドでは、必ず対応する型の値を返さなければなりません。

例:正方形の面積を求めるメソッド

namespace Chap04;
using System;

public class Example03 {
    public static void Run() {
        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が実行されることになります。return文が実行された時点で、そのメソッドの処理は終了するからです。

namespace Chap04;
using System;

public class Example04 {
    public static void Run() {
        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文は、条件分岐や繰り返しなど複雑な処理を行うメソッドで、処理を終了できると確定した箇所にreturn文を書くことで、無駄な処理を省いて高速化するために使われます。

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

早期return 文を使用すると、条件に合わない場合にメソッドの処理を即座に終了できます。これにより、無駄な処理を減らし、コードの可読性も向上します。

static void PrintDoublePositiveNumber(int num) {
    if (num <= 0)
        return; // ここでメソッド終了、何も返さない
    
    Console.WriteLine(num * 2);
}

3. メソッドのオーバーロード (Overload)

オーバーロードとは、「同じメソッド名で、引数の数や型が異なる複数のメソッド」 を定義することです。

例: Console.WriteLine

namespace Chap08;
using System;

public class Example05 {
    public static void run() {
        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メソッドはクラスが読み込まれた時点でメモリに置かれ、インスタンス不要で呼び出せる

次回は、「開発者の三種の神器① 条件分岐」を学びます。

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