[C#] クラスの基本をわかりやすく解説

C#におけるクラスは、オブジェクト指向プログラミングの基本構造であり、データとメソッドをまとめて扱うためのテンプレートです。

クラスは、フィールド(データ)とメソッド(操作)を含むことができ、これによりデータのカプセル化と再利用が可能になります。

クラスは「型」として機能し、インスタンス化することでオブジェクトを生成します。

クラスの定義には、アクセス修飾子(例:public, private)を用いてメンバーの可視性を制御し、コンストラクタを使用してオブジェクトの初期化を行います。

クラスは継承を通じて他のクラスの機能を拡張することも可能です。

この記事でわかること
  • C#におけるクラスの基本的な構成要素とその役割
  • クラスの継承と多態性の概念と実装方法
  • クラスを用いたデータモデルの設計とデザインパターンの実装例
  • クラスを活用したコードの再利用性向上の方法

目次から探す

クラスとは何か

クラスの基本

クラスは、C#におけるオブジェクト指向プログラミングの基盤となる概念です。

クラスは、データとそのデータを操作するメソッドをまとめたテンプレートとして機能します。

クラスを定義することで、同じ構造を持つオブジェクトを複数生成することができます。

以下は、基本的なクラスの定義例です。

// 人を表すクラス
public class Person
{
    // 名前を格納するフィールド
    private string name;
    // 年齢を格納するフィールド
    private int age;
    // コンストラクタ
    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
    // 名前を取得するメソッド
    public string GetName()
    {
        return name;
    }
    // 年齢を取得するメソッド
    public int GetAge()
    {
        return age;
    }
}

この例では、Personというクラスを定義し、nameageというフィールドを持たせています。

また、コンストラクタを用いてオブジェクトの初期化を行い、GetNameGetAgeというメソッドでフィールドの値を取得できるようにしています。

オブジェクト指向プログラミングにおけるクラスの役割

オブジェクト指向プログラミング(OOP)において、クラスは以下のような役割を果たします。

  • データのカプセル化: クラスはデータとその操作を一つにまとめ、外部からの不正なアクセスを防ぎます。
  • 再利用性の向上: 一度定義したクラスは、他のプログラムでも再利用可能です。
  • コードの整理: クラスを使うことで、関連するデータとメソッドを一つの単位として整理できます。

クラスとオブジェクトの違い

クラスとオブジェクトは、オブジェクト指向プログラミングにおいて重要な概念ですが、異なる役割を持っています。

スクロールできます
用語説明
クラスオブジェクトの設計図。データとメソッドを定義するテンプレート。
オブジェクトクラスから生成された実体。クラスのインスタンスとも呼ばれる。

クラスは設計図であり、オブジェクトはその設計図に基づいて生成された具体的な実体です。

例えば、Personクラスを使って複数のPersonオブジェクトを生成することができます。

以下は、Personクラスを使ってオブジェクトを生成する例です。

// Mainメソッド内でのオブジェクト生成
Person person1 = new Person("太郎", 30);
Person person2 = new Person("花子", 25);
// オブジェクトの情報を出力
Console.WriteLine("名前: " + person1.GetName() + ", 年齢: " + person1.GetAge());
Console.WriteLine("名前: " + person2.GetName() + ", 年齢: " + person2.GetAge());
名前: 太郎, 年齢: 30
名前: 花子, 年齢: 25

この例では、Personクラスからperson1person2という2つのオブジェクトを生成し、それぞれの情報を出力しています。

クラスは設計図としての役割を果たし、オブジェクトはその設計図に基づいて具体的なデータを持つ実体となります。

クラスの構成要素

フィールドとプロパティ

フィールドは、クラス内でデータを保持するための変数です。

フィールドはクラスのインスタンスごとに異なる値を持つことができます。

以下はフィールドの例です。

// 人を表すクラス
public class Person
{
    // 名前を格納するフィールド
    private string name;
    // 年齢を格納するフィールド
    private int age;
}

プロパティは、フィールドへのアクセスを制御するためのメカニズムです。

プロパティを使うことで、フィールドの値を取得したり設定したりする際に追加のロジックを実行できます。

// 人を表すクラス
public class Person
{
    // 名前を格納するフィールド
    private string name;
    // 年齢を格納するフィールド
    private int age;
    // 名前を取得・設定するプロパティ
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    // 年齢を取得・設定するプロパティ
    public int Age
    {
        get { return age; }
        set { age = value; }
    }
}

メソッド

メソッドは、クラス内で特定の動作を実行するための関数です。

メソッドはクラスのインスタンスに対して操作を行うことができます。

// 人を表すクラス
public class Person
{
    private string name;
    private int age;
    // コンストラクタ
    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
    // 名前を取得するメソッド
    public string GetName()
    {
        return name;
    }
    // 年齢を取得するメソッド
    public int GetAge()
    {
        return age;
    }
    // 自己紹介を行うメソッド
    public void Introduce()
    {
        Console.WriteLine("こんにちは、私の名前は" + name + "です。年齢は" + age + "歳です。");
    }
}

コンストラクタ

コンストラクタは、クラスのインスタンスが生成される際に呼び出される特別なメソッドです。

コンストラクタは、オブジェクトの初期化を行うために使用されます。

// 人を表すクラス
public class Person
{
    private string name;
    private int age;
    // コンストラクタ
    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
}

コンストラクタはクラス名と同じ名前を持ち、戻り値を持ちません。

上記の例では、Personクラスのコンストラクタがnameageを初期化しています。

アクセス修飾子

アクセス修飾子は、クラスやそのメンバー(フィールド、プロパティ、メソッドなど)のアクセス範囲を制御するために使用されます。

C#で一般的に使用されるアクセス修飾子には以下のものがあります。

スクロールできます
アクセス修飾子説明
publicどこからでもアクセス可能
privateクラス内からのみアクセス可能
protectedクラス内および派生クラスからアクセス可能
internal同一アセンブリ内からアクセス可能
protected internal同一アセンブリ内または派生クラスからアクセス可能

以下は、アクセス修飾子を使用した例です。

// 人を表すクラス
public class Person
{
    // privateフィールド
    private string name;
    // publicプロパティ
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    // publicメソッド
    public void Introduce()
    {
        Console.WriteLine("こんにちは、私の名前は" + name + "です。");
    }
}

この例では、nameフィールドはprivateとして定義されており、クラス外から直接アクセスすることはできませんが、Nameプロパティを通じてアクセスが可能です。

Introduceメソッドpublicとして定義されており、クラス外からも呼び出すことができます。

クラスの作成と使用

クラスの定義方法

C#でクラスを定義するには、classキーワードを使用します。

クラスの定義には、クラス名、フィールド、プロパティ、メソッドなどを含めることができます。

以下は、基本的なクラスの定義方法の例です。

// 車を表すクラス
public class Car
{
    // 車のモデル名を格納するフィールド
    private string model;
    // 車の速度を格納するフィールド
    private int speed;
    // コンストラクタ
    public Car(string model, int speed)
    {
        this.model = model;
        this.speed = speed;
    }
    // モデル名を取得するプロパティ
    public string Model
    {
        get { return model; }
        set { model = value; }
    }
    // 速度を取得するプロパティ
    public int Speed
    {
        get { return speed; }
        set { speed = value; }
    }
    // 車の情報を表示するメソッド
    public void DisplayInfo()
    {
        Console.WriteLine("モデル: " + model + ", 速度: " + speed + " km/h");
    }
}

この例では、Carというクラスを定義し、modelspeedというフィールドを持たせています。

また、コンストラクタを用いてオブジェクトの初期化を行い、ModelSpeedというプロパティでフィールドの値を取得・設定できるようにしています。

インスタンス化とオブジェクトの生成

クラスを使用するためには、まずそのクラスのインスタンスを生成する必要があります。

これをインスタンス化と呼びます。

インスタンス化は、newキーワードを使用して行います。

// Mainメソッド内でのオブジェクト生成
Car car1 = new Car("トヨタ", 120);
Car car2 = new Car("ホンダ", 150);

この例では、Carクラスからcar1car2という2つのオブジェクトを生成しています。

それぞれのオブジェクトは、Carクラスのインスタンスです。

メンバーへのアクセス

クラスのインスタンスを生成した後は、そのインスタンスのメンバー(フィールド、プロパティ、メソッド)にアクセスすることができます。

メンバーへのアクセスは、ドット演算子.を使用して行います。

// オブジェクトのメンバーにアクセス
car1.DisplayInfo();
car2.DisplayInfo();
// プロパティを使用して値を取得・設定
Console.WriteLine("car1のモデル: " + car1.Model);
car1.Speed = 130;
Console.WriteLine("car1の新しい速度: " + car1.Speed);
モデル: トヨタ, 速度: 120 km/h
モデル: ホンダ, 速度: 150 km/h
car1のモデル: トヨタ
car1の新しい速度: 130

この例では、car1car2DisplayInfoメソッドを呼び出して、それぞれの車の情報を表示しています。

また、Modelプロパティを使用してcar1のモデル名を取得し、Speedプロパティを使用して速度を変更しています。

これにより、クラスのメンバーにアクセスして操作を行うことができます。

クラスの継承と多態性

継承の基本

継承は、既存のクラス(基底クラスまたは親クラス)の機能を引き継いで、新しいクラス(派生クラスまたは子クラス)を作成するための機能です。

継承を使用することで、コードの再利用性を高め、クラス間の関係を明確にすることができます。

以下は、継承の基本的な例です。

// 基底クラス
public class Animal
{
    public void Eat()
    {
        Console.WriteLine("食べる");
    }
}
// 派生クラス
public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("吠える");
    }
}

この例では、Animalクラスが基底クラスであり、DogクラスAnimalクラスを継承しています。

DogクラスAnimalクラスEatメソッドを引き継ぎつつ、新たにBarkメソッドを追加しています。

オーバーライドとオーバーロード

オーバーライドは、基底クラスのメソッドを派生クラスで再定義することです。

オーバーライドを行うには、基底クラスのメソッドにvirtualキーワードを付け、派生クラスのメソッドにoverrideキーワードを付けます。

// 基底クラス
public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("動物の声");
    }
}
// 派生クラス
public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("ワンワン");
    }
}

この例では、AnimalクラスSpeakメソッドDogクラスでオーバーライドしています。

DogクラスSpeakメソッドは、AnimalクラスSpeakメソッドを上書きしています。

オーバーロードは、同じ名前のメソッドを異なる引数リストで定義することです。

オーバーロードは、メソッドの引数の数や型を変えることで実現します。

public class Calculator
{
    // メソッドのオーバーロード
    public int Add(int a, int b)
    {
        return a + b;
    }
    public double Add(double a, double b)
    {
        return a + b;
    }
}

この例では、Addメソッドが整数と浮動小数点数の両方に対してオーバーロードされています。

抽象クラスとインターフェース

抽象クラスは、インスタンス化できないクラスであり、他のクラスに継承されることを目的としています。

抽象クラスは、少なくとも一つの抽象メソッドを持ちます。

抽象メソッドは、派生クラスで実装される必要があります。

// 抽象クラス
public abstract class Animal
{
    public abstract void Speak();
}
// 派生クラス
public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("ワンワン");
    }
}

この例では、Animalクラスが抽象クラスであり、Speakメソッドが抽象メソッドとして定義されています。

DogクラスAnimalクラスを継承し、Speakメソッドを実装しています。

インターフェースは、クラスが実装すべきメソッドのシグネチャを定義するためのものです。

インターフェースは、クラスに特定の機能を持たせるための契約のようなものです。

// インターフェース
public interface IFlyable
{
    void Fly();
}
// クラスがインターフェースを実装
public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("飛ぶ");
    }
}

この例では、IFlyableインターフェースがFlyメソッドを定義しており、Birdクラスがこのインターフェースを実装しています。

インターフェースを使用することで、異なるクラスに共通の機能を持たせることができます。

クラスの応用例

クラスを使ったデータモデルの設計

クラスは、データモデルを設計する際に非常に有用です。

データモデルとは、特定のドメインにおけるデータの構造を表現するものです。

クラスを使うことで、データとその操作を一つの単位としてまとめることができます。

以下は、クラスを使ったシンプルなデータモデルの例です。

// 商品を表すクラス
public class Product
{
    // 商品名を格納するフィールド
    private string name;
    // 価格を格納するフィールド
    private decimal price;
    // コンストラクタ
    public Product(string name, decimal price)
    {
        this.name = name;
        this.price = price;
    }
    // 商品名を取得するプロパティ
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    // 価格を取得するプロパティ
    public decimal Price
    {
        get { return price; }
        set { price = value; }
    }
    // 商品情報を表示するメソッド
    public void DisplayInfo()
    {
        Console.WriteLine("商品名: " + name + ", 価格: " + price + "円");
    }
}

この例では、Productクラスを使って商品データをモデル化しています。

namepriceというフィールドを持ち、商品情報を表示するDisplayInfoメソッドを提供しています。

クラスを用いたデザインパターンの実装

クラスは、デザインパターンを実装する際にも重要な役割を果たします。

デザインパターンは、特定の問題に対する一般的な解決策を提供するもので、クラスを使って実装されることが多いです。

以下は、シングルトンパターンの実装例です。

シングルトンパターンは、クラスのインスタンスが一つだけであることを保証するデザインパターンです。

// シングルトンパターンを実装したクラス
public class Singleton
{
    // 唯一のインスタンスを保持するフィールド
    private static Singleton instance;
    // コンストラクタをprivateにして外部からのインスタンス化を防ぐ
    private Singleton() { }
    // インスタンスを取得するメソッド
    public static Singleton GetInstance()
    {
        if (instance == null)
        {
            instance = new Singleton();
        }
        return instance;
    }
    // シングルトンの動作を確認するメソッド
    public void ShowMessage()
    {
        Console.WriteLine("シングルトンインスタンスです。");
    }
}

この例では、Singletonクラスがシングルトンパターンを実装しています。

GetInstanceメソッドを通じて唯一のインスタンスを取得し、ShowMessageメソッドでメッセージを表示します。

クラスを活用したコードの再利用性向上

クラスを活用することで、コードの再利用性を向上させることができます。

クラスを一度定義すれば、異なるプロジェクトやコンポーネントで再利用することが可能です。

以下は、クラスを使って共通の機能を提供するユーティリティクラスの例です。

// 数学的な操作を提供するユーティリティクラス
public static class MathUtilities
{
    // 2つの数値の最大公約数を計算するメソッド
    public static int GCD(int a, int b)
    {
        while (b != 0)
        {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
    // 2つの数値の最小公倍数を計算するメソッド
    public static int LCM(int a, int b)
    {
        return (a / GCD(a, b)) * b;
    }
}

この例では、MathUtilitiesクラスが数学的な操作を提供しています。

GCDメソッドで最大公約数を計算し、LCMメソッドで最小公倍数を計算します。

このようなユーティリティクラスを作成することで、共通の機能を簡単に再利用することができます。

よくある質問

クラスと構造体の違いは何ですか?

クラスと構造体は、どちらもデータをまとめるための型ですが、いくつかの重要な違いがあります。

  • メモリの管理: クラスは参照型であり、ヒープにメモリが割り当てられます。

一方、構造体は値型であり、スタックにメモリが割り当てられます。

  • 継承: クラスは継承をサポートしますが、構造体は継承をサポートしません。

ただし、構造体はインターフェースを実装することができます。

  • デフォルトコンストラクタ: クラスはデフォルトコンストラクタを持ちますが、構造体はデフォルトコンストラクタを持ちません。

構造体のすべてのフィールドは、明示的に初期化される必要があります。

  • 用途: クラスは複雑なデータ構造やオブジェクト指向プログラミングに適していますが、構造体は軽量なデータ構造やパフォーマンスが重要な場面で使用されます。

なぜクラスを使うべきですか?

クラスを使用する理由は以下の通りです。

  • オブジェクト指向プログラミング: クラスはオブジェクト指向プログラミングの基本単位であり、データとその操作を一つにまとめることができます。

これにより、コードの整理と再利用が容易になります。

  • カプセル化: クラスはデータのカプセル化を可能にし、データの不正なアクセスを防ぎます。

アクセス修飾子を使用して、クラスのメンバーへのアクセスを制御できます。

  • 継承と多態性: クラスは継承をサポートしており、コードの再利用性を高めることができます。

また、多態性を利用することで、異なるクラスのオブジェクトを同じインターフェースで扱うことができます。

  • 柔軟性: クラスは複雑なデータ構造やビジネスロジックを表現するのに適しており、柔軟な設計が可能です。

クラスの継承はどのように行いますか?

クラスの継承は、既存のクラス(基底クラス)を拡張して新しいクラス(派生クラス)を作成するために行います。

継承を行うには、派生クラスの定義でコロン:を使用して基底クラスを指定します。

// 基底クラス
public class Animal
{
    public void Eat()
    {
        Console.WriteLine("食べる");
    }
}
// 派生クラス
public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("吠える");
    }
}

この例では、DogクラスAnimalクラスを継承しています。

DogクラスAnimalクラスEatメソッドを引き継ぎつつ、新たにBarkメソッドを追加しています。

継承を利用することで、共通の機能を基底クラスにまとめ、派生クラスで特有の機能を追加することができます。

まとめ

この記事では、C#におけるクラスの基本から、その構成要素、継承と多態性、さらには応用例までを詳しく解説しました。

クラスを用いることで、オブジェクト指向プログラミングの利点を活かし、コードの再利用性や保守性を高めることが可能です。

これを機に、実際のプロジェクトでクラスを活用し、より効率的で柔軟なプログラム設計に挑戦してみてください。

当サイトはリンクフリーです。出典元を明記していただければ、ご自由に引用していただいて構いません。

関連カテゴリーから探す

  • URLをコピーしました!
目次から探す