web-dev-qa-db-ja.com

C#でメソッドをオーバーライドしているときにアクセス修飾子を変更できないのはなぜですか?

C#では、基本クラスのメソッドをオーバーライドしている間はアクセス修飾子を変更できません。例えば.

Class Base
{
   **protected** string foo()
   {
       return "Base";
   }
}

Class Derived : Base
{
   **public** override string foo()
   {
       return "Derived";
   }
}

これはC#では無効です。コンパイル時にエラーが発生します。

理由、許可されない理由を知りたい。技術的な問題はありますか、それともアクセス制限に関して一貫性のない何かにつながる可能性がありますか???

44
Rumit Parakhiya

派生型のメソッドのアクセス修飾子を変更しても意味がないため、これは許可されていません。

ケース1:より制限されたアクセスで上書きする

次の状況のた​​め、このケースは明らかに許可されていません:

_class Base
{
    public virtual void A() {}
}

class Derived: Base
{
    protected override void A()
}
_

今私たちは言うことができます:

_List<Base> list;
list.Add(new Derived());
list[0].A() //Runtime access exception
_

ケース2:制限の少ないアクセス修飾子でオーバーライドする

ポイントは?メソッドを非表示にすると完了です。明らかに、基本型を介して呼び出した場合、派生型で定義された新しいメソッドにアクセスできなくなりますが、基本型の作成者が望んでいたとおりであり、変更する「権利」はありません。派生クラスからの派生クラス呼び出しの詳細が必要な場合は、newメソッドが完全に機能します。

編集:拡張ケース2

ケース2で私が言おうとしているのは、アクセシビリティを変更したい場合は、メソッドのアクセシビリティを(仮想かどうかにかかわらず)変更する手段がすでにあるということです。

次のコードを検討してください。

_public class Base
{
    protected virtual string WhoAmI()
    {
        return "Base";
    }
}

public class Derived : Base
{
    public new virtual string WhoAmI()
    {
        return "Derived";
    }
}

public class AnotherDerived : Derived
{
    public override string WhoAmI()
    {
        return "AnotherDerived";
    }
}
_

newキーワードを使用して、同じ名前とシグニチャーでDerivedクラスの新しい仮想メソッドを効果的に作成しました。 newメソッドvirtualを宣言することは許可されているので、Derivedから派生したクラスはそれをオーバーライドできます。

許可されていないのは、誰かに次のことをさせることです。

_ Base newBaseObject = new Derived();
 newBaseObject.WhoAmI() //WhoAmI is not accessible.
_

しかし、この事実はWhoAmI()をオーバーライドできるかどうかとは関係ありません。 BasepublicWhoAmI()を宣言していないため、このような状況になることはありません。

したがって、Derived.WhoAmI()Base.WhoAmI()をオーバーライドできる理論的なC#では、基本クラスから仮想メソッドを呼び出すことができないため、newオプションはすでに要件を満たしています。

これがより明確になることを願っています。

30
InBetween

OK、Annotated C#リファレンスでEric Lippertからの小さなメモを見つけました。

オーバーライドされた仮想メソッドは、それを導入したクラスのメソッドと見なされます。場合によっては、オーバーロード解決ルールがより多くの派生型のメンバーを優先します...メソッドをオーバーライドしても、そのメソッドがこの階層に属する場所に「移動」されません。

したがって、これは「脆弱な基本クラス」の問題を防ぎ、より良いバージョン管理を提供するための意図的なルールです。つまり、基本クラスが変更されたときに問題が少なくなります。

ただし、セキュリティ、タイプセーフ、オブジェクト状態とは関係ありません。

9
Henk Holterman

可視性修飾子を制限の厳しい修飾子から制限の少ない修飾子に変更すると、クラスクライアントが内部使用のために指定されたメソッドにアクセスできるようになります。基本的に、安全ではない可能性があるクラスの状態を変更する手段を提供しました。

_Base.Member_が表示されていて_Derived.Member_が表示されていない場合、OOPの「Derived is a Base」の概念全体が壊れるため、可視性を下げることは不可能です。ただし、可視性の増加は許可されません多分言語開発者は、可視性の変更はほとんどの場合間違いであると考えているためです。ただし、newキーワードを使用して、同じ名前で異なる動作のメンバーを紹介することにより、基本クラスのメンバーを非表示にすることができます。この新しいメンバーは派生型のインターフェースに属しているため、基本型にキャストすることで、基本型のインターフェースにアクセスできます。サブクラスの記述方法によっては、newメンバーが基本クラスのプロパティの可視性を効果的に高める可能性があります。ただし、基本クラスのプロパティには引き続き直接アクセスできます(たとえば、サブクラスのサブクラスがthisからBaseに変更し、プロパティをバイパスします)。

ここでの質問は、サブクラスで同じ名前のメンバー(識別子)を両方overrideおよびnewする方法です。それは明らかに不可能です。少なくとも、実験を通してpublic new override string foo(){return "";}はそのための構文ではないと言えるでしょう。ただし、2つのサブクラスを使用して同じ効果を得ることができます。

_using System;
class Base
{
    protected virtual string foo()
    {
        return "Base";
    }
    public void ExhibitSubclassDependentBehavior()
    {
        Console.WriteLine("Hi, I am {0} and {1}.", GetType(), foo());
    }
}

abstract class AbstractDerived : Base
{
    protected virtual string AbstractFoo()
    {
        return base.foo();
    }
    protected override string foo()
    {
        return AbstractFoo();
    }
}

class Derived : AbstractDerived
{
    protected override string AbstractFoo()
    {
        return "Deprived";
    }
    public new string foo()
    {
        return AbstractFoo();
    }
}

static class Program
{
    public static void Main(string[] args)
    {
        var b = new Base();
        var d = new Derived();
        Base derivedAsBase = d;
        Console.Write(nameof(b) + " -> "); b.ExhibitSubclassDependentBehavior(); // "b -> Hi, I am Base and Base."
        Console.WriteLine(nameof(d) + " -> " + d.foo()); // "d -> Deprived"
        Console.Write(nameof(derivedAsBase) + " -> "); derivedAsBase.ExhibitSubclassDependentBehavior(); // "derivedAsBase -> Hi, I am Derived and Deprived."
    }
}
_

中間サブクラス(AbstractDerived)はoverrideを使用し、サブクラスとサブサブクラスがベースクラスのメンバーにoverrideを継続して継続できる、異なる名前の新しいメンバーを導入しますフィット。サブサブクラス(Derived)はnewを使用して新しいAPIを導入します。特定の識別子でのみnewまたはoverrideを使用できるため、サブクラス化のレベルごとにonceが使用されるため、同じ識別子で両方を効果的に使用するには、2つのレベルのサブクラス化が必要です。 。

したがって、ある方法では、メソッドをオーバーライドしながら可視性を変更できます。これは面倒なことであり、1つのレベルの継承だけでそれを実現するための構文はありません。ただし、実装しようとしているインターフェイスと基本クラスの外観によっては、このようなトリックを使用する必要がある場合があります。つまり、これは実際にやりたいことかもしれないし、そうでないかもしれません。しかし、なぜC#がそもそもこれをサポートしていないのか疑問に思います。 IOW、この「答え」は、回避策を伴うOPの質問の再表現にすぎません;-)。

1
binki

派生クラスのアクセスをベースのアクセスより少なくすることができますが、それ以上にすることはできません。そうしないと、ベースの定義に矛盾し、意図したものを超えてコンポーネントが公開されます。

1
Kon

アクセス修飾子が異なる場合は、同じメソッドと見なすことはできません。モデルの設計に問題があることを示唆しています。

より良い質問は、なぜアクセス修飾子を変更したいのでしょうか?

0
peteisace

オーバーライドとは、基本クラスのメソッドの動作を変更または拡張できるようにする用語です。オーバーライドすることで、既存のメソッドの新しいロジックを作成するためのコントロールが提供されます。

基本クラスのメソッドシグネチャを変更することは、既存のメソッドをオーバーライドするのではなく、新しいメソッドを作成することに似ています。メソッドをオーバーライドする目的と矛盾します。したがって、C#でメソッドをオーバーライドしているときにアクセス修飾子を変更できない理由は多分です。

0
Nikhil

理由は明白です。オブジェクトのセキュリティと整合性。

この特定の例では、外部エンティティが基本クラスに従って保護されているオブジェクトのプロパティの変更を開始するとどうなるでしょうか。物事はややこしくなるでしょう。すべての派生クラスが準拠する必要がある基本クラスに対して作成されたクライアントコードはどうですか?.