WisdomSoft - for your serial experiences.

継承禁止

完成されたクラスやメソッドが、派生クラスによる予期せぬ拡張で振る舞いが変化してしまうことが不都合な場合があります。そこで、クラスやメソッドの拡張を制限することができます。シールクラスは継承を禁止し、シールメソッドは派生クラスによるオーバーライドを禁止します。

クラスの継承を禁止する

クラスがそれ自体で完結し、それ以上の派生が必要ない場合、不用意に外部のプログラムがクラスを継承できないようにする仕組みが用意されています。クラスの継承を禁止するには、クラス宣言で sealed 修飾子(sealed modifier)を指定します。sealed 修飾子を持つ継承が禁止されたクラスをシールクラス(sealed class)と呼びます。

シールクラス宣言
sealed class クラス名 ...

シールクラスが他のクラスの基本クラスとして指定された場合はコンパイルエラーとなります。

sealed class A {}
class B : A {} //エラー

シールクラスは、他のクラスによって継承されることがないという点を除けば通常のクラスと同じです。ただし、派生クラスを持たないため仮想メソッドを新たに宣言することはできません。同様の理由から保護された protected メンバを宣言することも意味がありませんが、この場合は警告となります。

sealed class A
{
	public virtual void VirtualMethod() { } //エラー
	protected void ProtectedMethod() { } //警告
}

また、クラス修飾子に static 修飾子と sealed 修飾子を組み合わせることはできません。静的クラスは、その時点でインスタンスを持たず継承できないためです。

基本的にクラスは拡張できる余地を残すべきで、むやみにシールクラスにする必要はありません。オブジェクト指向設計やソフトウェアの再利用性などの観点からも、シールクラスを多用するべきではありません。クラスの継承を制限する必要があると判断した場合に活用してください。

コード1
sealed class MagicalGirl
{
    public string Name { get; set; }
}
//class MidStyleMage : MagicalGirl { } //エラー

class Sample
{
    static void Main(string[] args)
    {
        MagicalGirl hayate = new MagicalGirl();
        hayate.Name = "八神はやて";
        System.Console.WriteLine("Name=" + hayate.Name);
    }
}
実行結果
コード2 実行結果

コード1の MagicalGirl クラスは sealed 修飾子を持つためシールクラスとしてマークされています。シールクラスは継承できないため、このクラスの派生クラスを作成することはできません。クラス宣言でシールクラスを基本クラスに設定した場合はエラーとなります。

メソッドのオーバーライドを禁止する

シールクラスはクラス全体を継承禁止にしますが、クラス内の一部の仮想メソッドをオーバーライドできなくすることも可能です。これは、基本クラスで宣言された仮想メソッドを派生クラスでオーバーライドすると同時に、オーバーライドしたメソッドを、さらなる派生クラスでオーバーライドできないように設定できます。

仮想メソッドのオーバーライドを制限するには sealed 修飾子を用いてメソッドを宣言します。sealed 修飾子で宣言されたメソッドをシールメソッド(sealed method)と呼びます。

class A
{
	public virtual void M() {}
}
class B : A
{
	public sealed override void M() {} //OK
}
class C : B
{
	public override void M() {} //エラー
}

上のコードの A クラスは仮想メソッド M() を宣言しています。B クラスは A クラスを継承し、この M() メソッドをオーバーライドしていますが、それと同時に sealed 修飾子でシールメソッドとしてマークしています。よって B クラスを継承するクラスは M() メソッドをオーバーライドすることができなくなります。このように、sealed 修飾子は override 修飾子と一緒に使うことになります。

コード2
class IntelligentDevice
{
    public virtual void Action() { }
}

class RaisingHeart : IntelligentDevice
{
    public sealed override void Action()
    {
        System.Console.WriteLine("Stand by Ready! Set up.");
    }
}

class RaisingHeartExelion : RaisingHeart
{
    //public override void Action() { } //エラー
    public new virtual void Action()
    {
        System.Console.WriteLine("Order of the setup was accepted.");
    }
}

class Sample
{
    static void Main(string[] args)
    {
        IntelligentDevice device = new RaisingHeart();
        device.Action();

        device = new RaisingHeartExelion();
        device.Action();
        ((RaisingHeartExelion)device).Action();
    }
}
実行結果
コード2 実行結果

コード2の IntelligentDevice クラスで宣言されている Action() メソッドは仮想メソッドです。これを IntelligentDevice クラスを継承する RaisingHeart クラスでオーバーライドし、同時にシールメソッドとして宣言しています。よって RaisingHeart クラスの派生クラスは、これ以上 Action() メソッドをオーバーライドすることはできません。

ただし、派生クラスで基本クラスのシールされたメソッドを隠蔽して再宣言することは可能です。コード2の  RaisingHeartExelion クラスでは new 修飾子を用いて Action() メソッドを隠蔽しています。