オブジェクトを返すメソッドとは?

2025年7月26日

C# プログラミングでは、クラスとインスタンスを使ったオブジェクト指向が基本です。その一例として「銀行口座(BankAccount)」クラスをサンプルに用います。口座の生成や預金といった身近な操作を例にすることで、オブジェクト返却メソッドの仕組みを直感的に理解できるようになります。


なぜ「銀行口座」をサンプルにするのか?

  1. 身近でイメージしやすいお金の預け入れ・残高照会は、日常的によく行われる操作です。初心者にもなじみがあるため、コードの動作を頭の中でシミュレーションしやすくなります。
  2. 属性と振る舞いがわかりやすい
    • 属性: 口座名義(Owner)、残高(balance)
    • 振る舞い: 預金(Deposit)、残高取得(GetBalance)オブジェクト指向の概念を学ぶ際に、クラスのプロパティやメソッドを明確に意識できます。
  3. 拡張例が豊富将来的には、引き出し機能や利息計算、口座履歴の記録など、さまざまな機能追加パターンを学習できます。

バージョン1:シンプルな口座生成

using System;

namespace ObjectReturnPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 単一の口座を生成し、預金して出力
            BankAccount bankAccount = new BankAccount();
            bankAccount.Owner = "山田太郎";
            bankAccount.Deposit(1000);

            Console.WriteLine($"Owner: {bankAccount.Owner}, Balance: {bankAccount.GetBalance()}");
            // → Owner: 山田太郎, Balance: 1000
        }
    }

    public class BankAccount
    {
        public string Owner { get; set; } = "";
        private int balance = 0;

        public void Deposit(int amount)
        {
            balance += amount;
        }

        public int GetBalance()
        {
            return balance;
        }
    }
}

ポイント

  • Main 内で直接 new BankAccount() を行い、操作を実装。
  • サンプルとして最小限のコード。

ここで、バージョン管理の学習の基礎を見ていきます


バージョン2:日本語表示+コメント追加&複数口座対応

using System;

namespace ObjectReturnPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 1人目の口座を生成
            BankAccount account1 = new BankAccount();
            account1.Owner = "山田太郎";
            account1.Deposit(1000);

            // 2人目の口座を生成
            BankAccount account2 = new BankAccount();
            account2.Owner = "鈴木花子";
            account2.Deposit(5000);

            // 各口座の情報を日本語で表示
            Console.WriteLine($"口座名義: {account1.Owner}、残高: {account1.GetBalance()} 円");
            Console.WriteLine($"口座名義: {account2.Owner}、残高: {account2.GetBalance()} 円");
        }
    }

    /// <summary>
    /// 銀行口座を表すクラス
    /// </summary>
    public class BankAccount
    {
        // 口座名義を保持するプロパティ
        public string Owner { get; set; } = "";

        // 残高を保持する非公開フィールド
        private int balance = 0;

        /// <summary>
        /// 指定した金額を預金する
        /// </summary>
        public void Deposit(int amount)
        {
            balance += amount;
        }

        /// <summary>
        /// 現在の残高を取得する
        /// </summary>
        public int GetBalance()
        {
            return balance;
        }
    }
}

改善点

  • 日本語コメントと XML ドキュメントで可読性向上。
  • 複数口座に対応し、サンプルの汎用性アップ。

バージョン3:オブジェクトを返すメソッド化(ファクトリメソッド導入)

using System;

namespace ObjectReturnPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // メソッドで口座生成~初期預金まで一括処理
            BankAccount account1 = CreateBankAccount("山田太郎", 1000);
            BankAccount account2 = CreateBankAccount("鈴木花子", 5000);

            Console.WriteLine($"口座名義: {account1.Owner}、残高: {account1.GetBalance()} 円");
            Console.WriteLine($"口座名義: {account2.Owner}、残高: {account2.GetBalance()} 円");
        }

        /// <summary>
        /// 銀行口座を生成し、初期預金まで完了したインスタンスを返すファクトリメソッド
        /// </summary>
        static BankAccount CreateBankAccount(string owner, int initialDeposit)
        {
            BankAccount account = new BankAccount();  // インスタンス生成
            account.Owner = owner;                   // オーナー名設定
            account.Deposit(initialDeposit);         // 初期預金
            return account;                          // 完成オブジェクトを返却
        }
    }

    /// <summary>
    /// 銀行口座を表すクラス
    /// </summary>
    public class BankAccount
    {
        public string Owner { get; set; } = "";
        private int balance = 0;

        public void Deposit(int amount) => balance += amount;
        public int GetBalance() => balance;
    }
}

以下のように、両者はまったく同じ動作をします。違いは「書き方(構文)」だけで、コンパイル後の動作(生成されるILコード)も同一です。


1. 構文の違い

書き方構文備考
ブロック式(従来)public void Deposit(int amount)
{
balance += amount;
}
複数行の処理やローカル変数の宣言も可能
式本体メンバーpublic void Deposit(int amount) => balance += amount;単一式(式1つ)のみ記述可C#6.0以降で利用可能

2. 動作の比較

  • 入力パラメーターどちらも int amount を受け取り、クラスフィールド balance に加算します。
  • 戻り値void 型なので戻り値はありません。
  • 例外処理や副作用ブロック式/式本体いずれも同じく balance を変更する副作用を持ちます。

3. コンパイル後の同等性

両者をコンパイルして生成される中間言語(IL)は 同一 です。

これは、式本体メンバーが「単一式メソッド用の構文糖衣(シンタックスシュガー)」に過ぎないためです。


4. どちらを使うべきか

  • 可読性重視
    • 処理が単一の短い式なら、式本体メンバー(=>)の方がシンプルで読みやすい
    • 複数行になる場合や将来処理が増える可能性がある場合は、ブロック式 { … } を使う
  • チームのコーディング規約
    • プロジェクトによっては式本体メンバーの使用を制限している場合もありますので、規約に従いましょう

まとめ

// ブロック式
public void Deposit(int amount)
{
    balance += amount;
}

// 式本体メンバー(C#6.0+)
public void Deposit(int amount) => balance += amount;
  • 動作・生成ILともに同一
  • 好みや可読性、プロジェクト規約で使い分け

以上のとおり、まったく同じ機能を持つ「書き方の違い」だけ、ということです。

大きな改善点

  • 初期化ロジックを CreateBankAccount に集約し、一元管理。
  • Main メソッドはシンプルかつ読みやすく。

まとめ:サンプルから学ぶオブジェクト返却メソッド

  1. 身近な銀行口座 を例にすることで、属性と振る舞いを明確にイメージできる。
  2. 段階的リファクタリング により、コードの再利用性・可読性・保守性が向上。
  3. プロジェクトでは、このパターンをユーザー生成やリソース管理など、さまざまな場面で活用可能。

ぜひ銀行口座サンプルを足がかりに、独自クラスのファクトリメソッドやオブジェクト返却メソッドを実装してみてください!


メモリ上の様子を解説


1. Stack(スタック)領域

  • 役割
    • メソッド呼び出し時に「引数」「ローカル変数」「戻り先情報」などを順番に積み上げる(LIFO)領域
  • 特徴
    • 高速に確保/解放される
    • スコープ({ } の範囲)を抜けると自動的に解放される
    • 小さめのデータ(参照型なら“参照”自体,値型なら値そのもの)が置かれる

イラスト中の taro, hanako

  • Person taro = new Person(“太郎"); の実行時
    • スタック上に taro という変数 (参照型変数) が作られ,
    • 実際のオブジェクトが置かれたヒープ領域への 参照(ポインタ) を保持します。
  • hanako も同様に,ヒープ上の2番目の Person オブジェクトを指しています。

2. Heap(ヒープ)領域

  • 役割
    • new キーワードで生成したオブジェクト(インスタンス本体)を置く領域
  • 特徴
    • スタックほど高速ではないが,必要なだけ大きな領域を確保できる
    • ガベージコレクション によって使われなくなったオブジェクトが自動的に回収される
    • クラスのフィールドや配列など“サイズが実行時に決まるもの”はこちらに配置

イラスト中の Person オブジェクト

  • 左側の箱:Name = 太郎
  • 右側の箱:Name = 花子
  • それぞれ別々のヒープ領域に確保されたインスタンスです。

3. static領域(静的領域)

  • 役割
    • クラス変数(static 修飾子のフィールド)を置く共有領域
  • 特徴
    • プログラム開始時に一度だけ確保され,プログラム終了まで存在
    • 全インスタンスでひとつだけ共有

イラスト中の Person.Population

  • public static int Population;
  • new Person(…) を呼ぶたびにコンストラクタ内で Population++ などすると,
  • 現在生成された Person の総数 がここに記憶されます。
  • この例では2つのインスタンスを作ったので Population = 2。

4. なぜこんな分け方をするのか?

領域何を置く?メリット
スタックローカル変数 / 参照確保・解放が高速 / スコープ管理が明確
ヒープnew で作るインスタンス本体大きなデータも格納可 / ライフサイクル自由
static領域クラス全体で共有するデータ (static)プログラム全体で1つだけ保持

まとめ

  • 参照型変数(Person taro など)はスタックに「ポインタ」を持ち,
  • 実際の インスタンス本体 はヒープに,
  • クラス変数(static)は static領域に置かれる
  • この仕組みを知ると,メモリ効率やガベージコレクション,スコープ管理の理解が深まります。

以上が,イラストを使った C# のメモリモデル(Stack/Heap/static領域)の初心者向け解説です。

訪問数 49 回, 今日の訪問数 1回