【C#】クラスについてわかりやすく詳しく解説

C#では、クラスという概念を使ってオブジェクト指向プログラミングを行います。

この記事では、クラスの基本的な構文や使い方をわかりやすく解説し、サンプルコードも交えて紹介します。

目次

クラスとは

プログラミングにおいて、クラスとはオブジェクト指向プログラミングの基本的な概念の一つです。

クラスは、同じ種類のオブジェクトを作成するための設計図やテンプレートのようなものであり、その中にはオブジェクトが持つべき属性(変数)や振る舞い(メソッド)が定義されています。

C#では、以下のようにしてクラスを定義します。

class MyClass {
    // クラス内で使用する変数やメソッドを定義する
}

このようにして定義されたクラスは、MyClassという名前で呼び出すことができます。

また、このクラスからオブジェクトを生成することができます。

MyClass myObject = new MyClass();

このようにして生成されたmyObjectは、MyClassという設計図に従って作られたオブジェクトです。

このオブジェクトは、MyClassが持つ属性や振る舞いを利用することができます。

例えば、以下のようなコードでは、myObjectというオブジェクトが持つ属性(変数)に値を代入し、その値を表示しています。

myObject.myVariable = 10;
Console.WriteLine(myObject.myVariable);

また、以下のようなコードでは、myObjectというオブジェクトが持つ振る舞い(メソッド)を呼び出し、その結果を表示しています。

int result = myObject.MyMethod(5);
Console.WriteLine(result);

以上がC#における基本的なクラスの使い方です。

クラスの定義方法

クラスは、オブジェクト指向プログラミングにおいて非常に重要な概念です。

C#では、クラスを定義することで、複数のメソッドや変数をまとめて管理することができます。

クラス名

まず、クラスを定義するためには、クラス名を決める必要があります。

クラス名は、変数名と区別するために大文字から始めるキャメルケースで記述することが一般的です。

例えば、Personというクラスを定義する場合は以下のようになります。

class Person {
    // クラスの中身をここに書く
}

メンバー変数

次に、クラス内で使用する変数を宣言します。

これらの変数は「メンバー変数」と呼ばれます。

メンバー変数は、他のメソッドからもアクセス可能なため、インスタンスごとに異なる値を持つことができます。

class Person {
    public string name; // 名前
    public int age;     // 年齢
}

上記の例では、nameageがメンバー変数として宣言されています。

メソッド

次に、クラス内で使用する関数を宣言します。

「メソッド」と呼ばれるこれらの関数は、外部から呼び出すことができます。

class Person {
    public string name; // 名前
    public int age;     // 年齢
    
    public void SayHello() {
        Console.WriteLine("こんにちは!私の名前は" + name + "です!");
    }
}

上記の例では、SayHelloというメソッドが宣言されています。

SayHelloメソッドは、Console.WriteLine文を使って、「こんにちは!私の名前は○○です!」という文字列を表示します。

+演算子を使って、name変数の値を文字列に結合しています。

コンストラクタ

次に、「コンストラクタ」と呼ばれる特別な種類のメソッドを宣言します。

「コンストラクタ」は、インスタンス化された際に自動的に呼び出されるため、初期化処理などを行うことができます。

class Person {
    public string name; // 名前
    public int age;     // 年齢
    
    public Person(string _name, int _age) {
        name = _name;
        age = _age;
        
        Console.WriteLine("新しいPersonオブジェクトが作成されました!");
    }
    
    public void SayHello() {
        Console.WriteLine("こんにちは!私の名前は" + name + "です!");
    }
}

上記の例では、Personというコンストラクタが宣言されています。

_name_ageは引数として受け取った値でnameageそれぞれ初期化しています。

Console.WriteLine文で、「新しいPersonオブジェクトが作成されました!」という文字列も表示しています。

デストラクタ

最後に、「デストラクタ」と呼ばれる特別な種類のメソッドも紹介します。

「デストラクタ」はインスタンスが破棄される際(プログラム終了時や明示的な解放時)に自動的に呼び出されます。

class Person {
    ~Person() {
        Console.WriteLine("Personオブジェクトが破棄されました!");
    }
    
    ...
}

上記の例では、~Person()というデストラクタが宣言されています。

Console.WriteLine文で、「Personオブジェクトが破棄されました!」という文字列も表示しています。

以上がC#での基本的な「クラス定義方法」です。

クラスの継承

プログラミングにおいて、クラスの継承とは、既存のクラスを基に新しいクラスを作成することです。

継承の種類

C#における継承には以下の3つの種類があります。

  • 単一継承
  • 多重継承
  • インターフェース継承

単一継承は、1つの親クラスからしか派生できないことを意味します。

多重継承は、複数の親クラスから派生することができますが、構造が複雑化しやすい関係からか、C#ではサポートされていません。

インターフェース継承は、インターフェースを実装するために使用されます。

継承の実装方法

C#でクラスを継承する場合、以下のような構文を使用します。

class ChildClass : ParentClass
{
    // 子クラス固有のメンバー変数やメソッドを定義
}

上記コードでは、ChildClassという子クラスがParentClassという親クラスから派生しています。

ChildClass内で定義されたメンバー変数やメソッドは、ParentClassからも利用可能です。

また、ParentClass内で定義された非プライベートなメンバー変数やメソッドも、ChildClassから利用可能です。

ただし、private修飾子が付与されたものはアクセスすることはできません。

例えば、以下のようなコードではParentClass内で定義されたparentMethod()ChildClassから呼び出せます。

using System;

class ParentClass 
{
    public void parentMethod()
    {
        Console.WriteLine("This is a parent method.");
    }
}

class ChildClass : ParentClass 
{
    public void childMethod()
    {
        Console.WriteLine("This is a child method.");
    }
}

class Program {
    static void Main(string[] args)
    {
        ChildClass child = new ChildClass();
        
        // 親クラス(Parent)で定義したparentMethod()を呼び出すことができる。
        child.parentMethod(); 
    
        // 子クラス(Child)で定義したchildMethod()を呼び出すことができる。
        child.childMethod();  
    }
}
This is a parent method.
This is a child method.

クラスのアクセス修飾子

クラスのアクセス修飾子には、publicprivateprotectedinternalprotected internalの5つがあります。

public

publicは、どこからでもアクセス可能な修飾子です。

つまり、他のクラスからもアクセスできるようになります。

例えば以下のように定義されたPersonクラスを考えてみましょう。

public class Person {
    public string name;
    public int age;

    public void SayHello() {
        Console.WriteLine("こんにちは!私は" + name + "です。" + age + "歳です。");
    }
}

この場合、nameやageといったメンバー変数やSayHello()といったメソッドはどこからでもアクセス可能です。

private

privateは、同じクラス内からしかアクセスできない修飾子です。

つまり、他のクラスからはアクセスできなくなります。

例えば以下のように定義されたPersonクラスを考えてみましょう。

public class Person {
    private string name;
    private int age;

    public void SetName(string newName) {
        name = newName;
    }

    public void SetAge(int newAge) {
        age = newAge;
    }

    public void SayHello() {
        Console.WriteLine("こんにちは!私は" + name + "です。" + age + "歳です。");
    }
}

この場合、nameやageといったメンバー変数はprivateで定義されていますので、外部から直接アクセスすることができません。

代わりにSetName()やSetAge()といったメソッドを通じて値を設定できるようにしています。

これにより、変数に値を代入するルールを厳格化できるため、「0未満の数値は代入しない」といった事が可能になります。

またSayHello()というメソッド内ではnameやageに直接アクセスしていますが、これは同じPersonクラス内であるため問題ありません。

protected

protectedは、同じクラス内およびその派生したサブクラス(継承先)からしかアクセスできない修飾子です。

つまり、他の外部のクラスからはアクセスできなくなります。

ただし継承したサブクラスでは自由に使用することが出来ます。

以下の例を見てみましょう。

public class Person {
  protected string name;
  protected int age;

  public void SetName(string newName) {
      name = newName;
  }

  public void SetAge(int newAge) {
      age = newAge;
  }
}

public class Employee : Person {

  private string company;

  public Employee(string companyName) {
      company = companyName;
  }

  public void IntroduceMyself() {
      Console.WriteLine("私は" + company + "勤務の" + name + "さんです!");
  }
}

この場合、Employee クラスがPerson クラスを 継承しています。

Person クラス内のnameageフィールドに対して、Employee クラス内のIntroduceMyself メソッド内からアクセス可能です。

internal

internalは同一プロジェクト内からしかアクセ ス出来ない修飾子です。

つまり別プロジェ クト内では使用することが出来ず、同一プロジェ クト内だけ使用することが出来ます。

internal class InternalClass{
   // ...
}

class Program{
   static void Main(string[] args){
       InternalClass obj = new InternalClass();
   }
}

これは複数プロジェクトを組み合わせて行う大規模開発で使用することが多いため、1プロジェクトで完結する開発であれば使用する機会はありません。

protected internal

protected internal修飾子はprotectedかinternalどちらか一方以上当て嵌められる場合使われます。

つまり同一プロジェ クト内または継承した子クラス内でだけ使用することが出来ます。

class Base{
   protected internal int x; // xは、現在のプロジェクト内または派生クラスでのみアクセス可能
}

class Derived:Base{
   static void Main(){
       Base obj1=new Base(); 
       Derived obj2=new Derived();
       obj2.x=10; //同一プロジェクト内なら自由にアクセスできる
   }
}

クラスのポリモーフィズム

ポリモーフィズムとは

ポリモーフィズムとは、同じ名前のメソッドを複数のクラスで定義し、それぞれのクラスで異なる処理を行うことができる機能です。

つまり、同じ名前のメソッドでも、引数や戻り値が異なる場合には別々に定義することができます。

ポリモーフィズムの実装方法

ポリモーフィズムを実現するためには、オーバーロード(overload)またはオーバーライド(override)を使用します。

オーバーロード

オーバーロードとは、同じ名前のメソッドを複数定義し、引数の型や個数が異なる場合にそれぞれ別々に処理を行うことです。

例えば以下のようなコードです。

class SampleClass {
    public void Method(int arg) {
        Console.WriteLine("int型: " + arg);
    }

    public void Method(string arg) {
        Console.WriteLine("string型: " + arg);
    }
}

このように、引数が異なる同じ名前のメソッドを定義することで、オブジェクトから呼び出す際に適切なメソッドが自動的に選択されます。

オーバーライド

オーバーライドとは、親クラスで定義されたメソッドを子クラスで再定義することです。

子クラスでは親クラスで定義されたメソッドと同じ名前・引数・戻り値型を持つ新しいメソッドを作成します。

例えば以下のようなコードです。

class Animal {
    public virtual void Speak() {
        Console.WriteLine("...");
    }
}

class Dog : Animal {
    public override void Speak() {
        Console.WriteLine("わんわん");
    }
}

class Cat : Animal {
    public override void Speak() {
        Console.WriteLine("にゃー");
    }
}

このように、親クラスAnimalでSpeak()メソッドを仮想的に宣言しておき、子クラスDogやCatではその仮想的なメソッドを再定義することでポリモフィズムを実現します。

ポリモーフィズムの利用

ポリモーフィズムは主に以下のような場面で利用されます。

  • 複数種類あるデータ型やオブジェクトから共通した操作(関数)を行いたい場合。
  • 既存プログラムへ新しい機能追加時。
  • 同一インタフェースから派生した複数種類あるインスタンスから共通した操作(関数)を行いたい場合。

例えば以下のようなコードでは、Animal型変数animalがDogかCatか分からなくても、Speak()関数が呼び出せます。

Animal animal = new Dog();
animal.Speak(); // 「わんわん」と表示される

animal = new Cat();
animal.Speak(); // 「にゃー」と表示される

このようにポリモーフィズムは柔軟性が高く便利ですが、闇雲に派生クラスを定義してポリモーフィズムしてしまうと、可読性や保守性が低下する恐れもあります。

過剰使用しないよう注意しましょう。

目次