C# クラスの定義とメソッドの動作原理

クラスの基本概念

クラスはオブジェクト指向プログラミングにおける設計図であり、具体的なデータ(状態)とそれを操作する機能(行動)をカプセル化するための単位です。クラスを元に生成された具体的な実体を「インスタンス」と呼びます。クラス定義を通じて、インスタンスがどのようなプロパティを持ち、どのような振る舞いをするかを規定します。

例えば、自動車情報を管理する「Vehicle」クラスを定義し、それを元に複数の車両オブジェクトを生成するケースを考えます。以下のコード例では、コンストラクタによる初期化、プロパティによるデータアクセス、およびインスタンスメソッドと静的メソッドの違いを示しています。

using System;

namespace CoreConcepts
{
    public class Vehicle
    {
        // フィールド
        private string _model;
        private int _year;

        // コンストラクタ(パラメータなし)
        public Vehicle()
        {
        }

        // コンストラクタ(パラメータあり)
        public Vehicle(string model, int year)
        {
            _model = model;
            _year = year;
        }

        // プロパティ:Model
        public string Model
        {
            get { return _model; }
            set { _model = value; }
        }

        // プロパティ:Year
        public int Year
        {
            get { return _year; }
            set { _year = value; }
        }

        // インスタンスメソッド
        public string GetDescription()
        {
            return $"{_year}年式 {_model}";
        }

        // 静的メソッド
        public static string GetFactoryInfo()
        {
            return "工場情報:東京第 1 工場";
        }
    }

    public class Program
    {
        static void Main()
        {
            // インスタンスの生成(デフォルトコンストラクタ)
            Vehicle car1 = new Vehicle();
            car1.Model = "Sedan";
            car1.Year = 2020;

            // インスタンスの生成(パラメータ付きコンストラクタ)
            Vehicle car2 = new Vehicle("SUV", 2022);

            Console.WriteLine(car1.Model);
            Console.WriteLine(car1.Year);
            Console.WriteLine(car2.GetDescription());
            
            // 静的メソッドの呼び出し(インスタンス不要)
            Console.WriteLine(Vehicle.GetFactoryInfo());
        }
    }
}

上記の例において、public class Vehicle がクラスの宣言です。<_model>と<_year>は内部状態を保持するフィールドであり、Model と Year プロパティを通じて外部からアクセス可能になります。GetDescription はオブジェクトの動作を表すインスタンスメソッドです。

new キーワードを用いてクラスをインスタンス化すると、メモリ上に新しいオブジェクトが確保されます。コンストラクタはインスタンス生成時に自動的に実行され、オブジェクトの初期状態を設定する役割を果たします。また、static 修飾子がついたメソッドはクラス自体に紐付くため、インスタンス化せずにクラス名経由で直接呼び出すことが可能です。

メソッドの構造と呼び出し

メソッドは特定の処理を実行するコードブロックであり、アクセス修飾子、戻り値の型、メソッド名、パラメータリスト、および本体から構成されます。Main メソッドもその一例です。

メソッドを呼び出す際、インスタンスメソッドの場合はオブジェクト参照を通じてアクセスし、静的メソッドの場合はクラス名を通じてアクセスします。パラメータの渡し方については、値渡しと参照渡しの挙動を理解することが重要です。

パラメータの传递机制

パラメータは値によって渡されるか、参照によって渡されるかで挙動が異なります。値型(int など)はコピーされて渡されるため、メソッド内部での変更は外部に影響しません。一方、参照型(配列やクラスなど)は参照情報が渡されるため、内部での変更が外部の元データに反映されます。

using System;

namespace ParameterDemo
{
    public class DataHandler
    {
        static void ModifyData(int[] numbers, int value)
        {
            numbers[0] = 999; // 参照型なので外部に影響する
            value = 999;      // 値型なので外部に影響しない
        }

        static void Main()
        {
            int[] dataSet = { 1, 2, 3 };
            int singleValue = 50;

            Console.WriteLine($"Before: {dataSet[0]}, {singleValue}");
            ModifyData(dataSet, singleValue);
            Console.WriteLine($"After: {dataSet[0]}, {singleValue}");
        }
    }
}

このコードの実行結果は、配列の要素は 999 に変更されますが、singleValue は 50 のままです。これは配列が参照型であり、int が値型であるためです。

ref と out キーワード

値型であっても、ref キーワードを使用することで参照渡しを強制できます。この場合、メソッド呼び出し側で変数を事前に初期化する必要があります。一方、out キーワードも参照渡しですが、呼び出し側での初期化は不要であり、メソッド内部で必ず値を代入する義務があります。

using System;

namespace RefOutDemo
{
    public class Calculator
    {
        // ref の例
        static void IncrementRef(ref int num)
        {
            num += 10;
        }

        // out の例
        static void InitializeOut(out int num)
        {
            num = 100; // 内部で代入必須
        }

        static void Main()
        {
            int val1 = 5;
            IncrementRef(ref val1); // 呼び出し時にも ref 必要
            Console.WriteLine(val1); // 15

            int val2; 
            InitializeOut(out val2); // 初期化不要
            Console.WriteLine(val2); // 100
        }
    }
}

メソッドのオーバーロード

同じクラス内で同じ名前のメソッドを複数定義することをオーバーロードと呼びます。これは、パラメータの型や個数、順序が異なる場合に可能です。呼び出し時に渡された引数に基づいて、適切なメソッドが自動的に選択されます。

using System;

namespace OverloadDemo
{
    public class MathUtil
    {
        public static int Compute(int x)
        {
            return x * 2;
        }

        public static double Compute(double x)
        {
            return x * 2.5;
        }

        public static int Compute(int x, int y)
        {
            return x + y;
        }

        static void Main()
        {
            Console.WriteLine(MathUtil.Compute(10));       // int 版
            Console.WriteLine(MathUtil.Compute(5.5));      // double 版
            Console.WriteLine(MathUtil.Compute(3, 7));     // 2 引数版
        }
    }
}

オーバーロードを使用することで、同じ処理概念に対して異なる入力パターンを提供でき、コードの可読性と柔軟性が向上します。ただし、戻り値の型のみが異なる場合はオーバーロードとして認識されない点に注意が必要です。

タグ: C# class Method-Overloading Parameter-Passing OOP

6月13日 18:52 投稿