42

このコード:

abstract class C
{
    protected abstract void F(D d);
}

class D : C
{
    protected override void F(D d) { }

    void G(C c)
    {
        c.F(this);
    }
}

次のエラーが生成されます。

タイプ 'C' の修飾子を介して保護されたメンバー 'CF(D)' にアクセスできません。修飾子はタイプ 'D' (またはそれから派生したもの) でなければなりません

彼らはいったい何を考えていたのでしょうか。(そのルールを変更すると何かが壊れるでしょうか?) そして、F を公開する以外にそれを回避する方法はありますか?


編集:これがなぜなのか理由がわかりました(Gregに感謝します)が、合理性についてはまだ少し当惑しています。与えられた:

class E : C
{
    protected override void F(D d) { }
}  

D が EF を呼び出せないのはなぜですか?


エラーメッセージは編集されているため、タイプミスがある可能性があります。

4

7 に答える 7

45

これが機能しない理由は、C# が保護されたメソッドの階層間の呼び出しを許可していないためです。Eからも派生したクラスがあったとしCます:

  C
 / \
D   E

次に、メソッドを呼び出そうとしている参照は、実際には型のインスタンスである可能性があるEため、メソッドは実行時に に解決される可能性がありますE.F。は階層の別のブランチにあるため、 の保護されたメソッドをD呼び出すことができないため、これは C# では許可されていません。EE

var d = new D();
var e = new E();
d.G(e); // oops, now this will call E.F which isn't allowed from D

protectedキーワードは、メンバーが「そのクラス内および派生クラス インスタンスによってアクセス可能」であることを意味し、EF は D のメンバーではないため、これは理にかなっています。

于 2009-02-19T23:34:36.467 に答える
18

「保護された」キーワードは、型とその型から派生した型のみがメンバーにアクセスできることを意味します。D は C と関係がないため、メンバーにアクセスできません。

そのメンバーにアクセスできるようにしたい場合は、いくつかのオプションがあります

  • 公開する
  • 内部にします。これにより、任意の型が同じアセンブリ (またはフレンドを追加する場合は他のアセンブリ) 内のメンバーにアクセスできます。
  • C から D を導き出す

編集

このシナリオは、C# 仕様のセクション 3.5.3 で呼び出されます。

これが許可されない理由は、階層間の呼び出しが許可されるためです。D に加えて、E と呼ばれる C の別の基本クラスがあったと想像してください。コードをコンパイルできれば、D はメンバー EF にアクセスできます。このタイプのシナリオは C# では許可されていません ( CLRは信じていますが、私はそうではありません)。 t 100% 知っている)。

EDIT2なぜこれが悪いのか

注意、これは私の意見です

これが許可されるようになった理由は、クラスの動作について推論するのが非常に難しくなるためです。アクセス修飾子の目的は、開発者が特定のメソッドにアクセスできるユーザーを正確に制御できるようにすることです。次のクラスを想像してください

sealed class MyClass : C {
  override F(D d) { ... } 
}

F がやや時間的に重要な関数である場合に何が起こるかを考えてみましょう。現在の動作で、クラスの正しさについて推論できます。結局、MyClass.F が呼び出されるケースは 2 つしかありません。

  1. Cで呼び出される場所
  2. MyClass で明示的に呼び出す場所

これらの呼び出しを調べて、MyClass がどのように機能するかについて妥当な結論に達することができます。

現在、C# が階層間で保護されたアクセスを許可している場合、そのような保証はできません。まったく異なるアセンブリにいる人なら誰でも、C から派生することができます。その後、MyClass.F を自由に呼び出すことができます。これにより、クラスの正確性について推論することは完全に不可能になります。

于 2009-02-19T23:29:58.707 に答える
12

D は C から継承されていますが、D は C の保護されたメンバーにアクセスできません。D は D の保護 (およびプライベート!) メンバーにアクセスできるため、C の代わりに D の別のインスタンスをそこに配置すると、すべてが機能します。しかし、Greg が述べたように、C は実際にはまったく異なるものである可能性があり、コンパイラーは C が実際に何であるかを知らないため、D が実際にはアクセスできない可能性があるものに D がアクセスするのを防ぐ必要があります。

C# コンパイラの観点からこれを説明する一連の投稿:

于 2009-02-19T23:43:08.067 に答える
5

この制限は、静的な保護されたメソッドを使用してバイパスできます。

abstract class C
{
    protected abstract void F (D d);

    // Allows calling F cross-hierarchy for any class derived from C
    protected static void F (C c, D d)
    {
        c.F(d);
    }
}

class D : C
{
    protected override void F (D d) { }

    void G (C c)
    {
        // c.F(this);
        F(c, this);
    }
}

これはセキュリティの観点からは完全ではありません (誰でも から派生できます)が、 class のパブリック インターフェイスからCメソッドを隠すことだけを考えている場合は、このトリックが役立つ可能性があります。FC

于 2014-06-27T17:08:52.807 に答える
3

簡単に言えば、インスタンスの保護されたメンバーへのアクセスは、派生クラス内からアクセスしようとしても、パブリック アクセスと見なされます。したがって、それは否定されます。


あちこちに多くの答えがありますが、「子から親クラスの保護されたメンバーにアクセスできない理由」を明確にするものはありませんでした。上記は、これらの紛らわしい回答を読んだ後、コードをもう一度見て理解したものです。

例:

class Parent
{
    protected int foo = 0;
}

// Child extends from Parent
class Child : Parent
{
    public void SomeThing(Parent p)
    {
        // Here we're trying to access an instance's protected member.
        // So doing this...
        var foo = p.foo;
    }
}

// (this class has nothing to do with the previous ones)
class SomeoneElse
{
    public void SomeThing(Parent p)
    {
        // ...is the same as doing this (i.e. public access).
        var foo = p.foo++;
    }
}

p.foo子クラス内にいるのでアクセスできると思うかもしれませんが、インスタンスからアクセスしていて、それはパブリック アクセスのようなものなので、拒否されます。

インスタンスからではなく、クラス内からメンバーにアクセスできprotectedます (はい、これはわかっています)。

class Child : Parent
{
    public void SomeThing()
    {
        // I'm allowed to modify parent's protected foo because I'm
        // doing so from within the class.
        foo++;
    }
}

最後に、完全を期すために、同じクラス内でアクセスしている場合にのみ、実際にはインスタンスprotectedやメンバーにさえアクセスできます。private

class Parent
{
    protected int foo = 0;

    private int bar = 0;

    public void SomeThing(Parent p)
    {
        // I'm allowed to access an instance's protected and private
        // members because I'm within Parent accessing a Parent instance
        var foo = p.foo;
        p.bar = 3;
    }
}
于 2019-03-17T04:32:07.020 に答える
1

この種の動作が理にかなっている理由を理解するために、オブジェクト指向プログラミング言語でアクセス修飾子が必要な理由を考えてみましょう。特定のクラス メンバーを使用できるスコープを制限する必要があります。これにより、使用箇所の検索が簡素化されます。

要約する:

  • publicメンバーのすべての使用箇所を見つけるには、プロジェクト全体を検索する必要があります(独立した開発者が使用するライブラリの場合、これは十分ではありません)。
  • 保護されたメンバーのすべての使用箇所を見つけるには、コンテナ クラスとそのすべてのサブクラスを検索する必要があります
  • プライベートメンバーのすべての使用箇所を見つけるには、コンテナ クラスを検索する必要があります。

したがって、コンパイラが説明されている方法でスーパークラスから保護されたメソッドを呼び出すことを許可した場合、この回答で説明されているように、保護されたメソッドの階層間呼び出しが発生する可能性があります。このような状況では、メンバーを定義する最上位の親クラスのすべての子を検索する必要がありました。そして、それは範囲を拡大します。

PS。同じ動作が Java に実装されています。

于 2010-04-20T20:15:06.840 に答える