8

次のコードがあると考えてください。

public abstract class MenuItem
    {
        protected string m_Title;
        protected int m_Level;
        protected MenuItem m_ParentItem;
        public event ChooseEventHandler m_Click;

        protected MenuItem(string i_Title, int i_Level, MenuItem i_ParentItem)
        {
            m_Title = i_Title;
            m_Level = i_Level;
            m_ParentItem = i_ParentItem;
        }
}

public class ContainerItem : MenuItem
    {
    private List<MenuItem> m_SubMenuItems;

    public ContainerItem(string i_Title, int i_Level, MenuItem i_ParentItem)
                            :base(i_Title, i_Level, i_ParentItem)
    {
        m_SubMenuItems = new List<MenuItem>();
    }

    public string GetListOfSubItems()
    {
        string subItemsListStr = string.Empty;

        foreach (MenuItem item in m_SubMenuItems)
        {
           item.m_Title = "test";  // Cannot access protected member the qualifier   
                                  must be of type 'Ex04.Menus.Delegates.ContainerItem' 

        }

        return subItemsListStr;
    }
}
  1. 私はこのエラーの背後にあるロジックを本当に理解していません、そしてはい、私はすでに読んでいます:http: //blogs.msdn.com/b/ericlippert/archive/2005/11/09/491031.aspx
    しかし、私はまだそれが完全に非論理的であると思います保護されたアクセス修飾子の定義に従います。MenuItem定義されたのと同じクラスから、そしてそのすべての派生クラスからアクセスできるはずだと思います。(ContainerItemなど)

  2. (ポリモーフィズムの設計上の理由から)m_Titleへの参照を保持しながら、保護されたメンバーにどのようにアクセスしますか?MenuItem

4

2 に答える 2

20

なぜこれが起こるのですか?

議論の余地のない答えは、「仕様がそう言っているからです」です。

基本クラスのprotectedメンバーは 、派生クラスタイプを介してアクセスが発生する場合にのみ、派生クラスでアクセスできます。

しかし、舞台裏でこの制限を調べてみましょう。

説明

ここで起こることは、リンクしたブログ投稿でEricLippertが説明していることと同じです。あなたのコードはこれと同等のことをします:

public abstract class MenuItem
{
    protected string m_Title;
}

public class ContainerItem : MenuItem
{
    void Foo()
    {
        var derivedItem = new ContainerItem();
        derivedItem.m_Title = "test"; // works fine

        var baseItem = (MenuItem)derived;
        baseItem.m_Title = "test"; // compiler error!
    }
}

ここでの問題は、これが発生する可能性があるという事実に起因します。今のところ、この例ではフィールドの代わりにメソッドを使用しているという事実を無視してください。ここに戻ります。

public abstract class MenuItem
{
    protected void Foo() {}
}

public class SomeTypeOfItem : MenuItem
{
    protected override void Foo() {}
}

public class ContainerItem : MenuItem
{
    void Bar()
    {
        var baseItem = (MenuItem)something;
        baseItem.Foo(); // #1
    }
}

1行目を見てください。コンパイラはそれbaseItemが実際にはそうではないことをどのようにして知るのSomeTypeOfItemでしょうか。もしそうなら、あなたは確かにアクセスできないはずですFoo!したがって、Ericが説明しているように、コンパイラはアクセスが常に合法であることを静的に証明することはできず、そのため、このコードを禁止する必要があります。

場合によっては、たとえば次の場合に注意してください。

baseItem = (MenuItem)new ContainerItem();

あるいは

baseItem = (MenuItem)this;

コンパイラには、アクセスが合法であることを証明するのに十分な情報がありますが、それでもコードをコンパイルすることはできません。これは、コンパイラチームが、このような特殊なケースのハンドラーを実装することは問題の価値があると確信していないためだと思います(私が共感している観点)。

しかし、しかし...

これはすべて、メソッド(および実際にはメソッドであるプロパティ)にとっては問題ありません。フィールドについてはどうでしょうか。これはどうですか:

public abstract class MenuItem
{
    protected string m_Title;
}

public class SomeTypeOfItem : MenuItem
{
    protected new string m_Title;
}

public class ContainerItem : MenuItem
{
    void Foo()
    {
        var baseItem = (MenuItem)something;
        baseItem.m_Title = "Should I be allowed to change this?"; // #1
    }
}

フィールドはオーバーライドできないため、ここであいまいさがMenuItem.m_Titleなく、タイプに関係なくコードをコンパイルおよび設定する必要somethingがあります。

確かに、コンパイラーがこれを実行できなかった技術的な理由は考えられませんが、いずれにせよ、一貫性という正当な理由があります。エリック自身はおそらくもっと豊富な説明を提供することができるでしょう。

それで、なにかお手伝いできますか?

MenuItemへの参照を保持しながら(ポリモーフィズムの設計上の理由から)、m_Titleなどの保護されたメンバーにどのようにアクセスしますか?

あなたは単にそれをすることはできません。メンバーを作成する必要がありますinternal(またはpublic)。

于 2012-12-03T12:56:14.593 に答える
3

protected派生クラスはそれにアクセスできることを意味しますが、派生クラスはそれ自体のインスタンスのプロパティにアクセスできます。あなたの例では、this.m_Titleそれはインスタンス自体に属しているのでアクセスできますが、別のインスタンスの保護されたメンバー(つまりリスト内のインスタンスm_SubMenuItems)にアクセスしようとしています。

目的の方法でアクセスするには、getterメソッドとsetterメソッドが必要です。

うまくいけば、これはそれをより明確にします:

class Foo {
    protected int x;

    public void setX(int x) {
        this.x = x;
    }
}

class Bar : Foo {
    Foo myFoo = new Foo();

    public void someMethod() {
        this.x = 5;    // valid. You are accessing your own variable
        myFoo.x = 5;   // invalid. You are attempting to access the protected
                       // property externally
        myFoo.setX(5); // valid. Using a public setter
    }
}
于 2012-12-03T12:36:22.960 に答える