2

次の継承ツリーが与えられた場合、機能する方法でそれを実装する最良の方法は何でしょうか?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

これは失敗します: 'Bar.Bar()' is inaccessible due to its protection level.

コンストラクターをパブリックにしたくありません。継承するクラスのみがsFooを作成できる必要がありますBarBarは特殊なFooであり、どのタイプの もFoo作成できるはずです。public internal は、ここでは「オプション」です。定義済みの拡張機能の大部分はFooDLL の内部にあるためです。ただし、後で独自の型のFooor Baz(これは発生する可能性が高い) は、デフォルトのCreateBar()実装に固執し、ニーズを満たす場合と満たさない場合があります。

おそらく、これをリファクタリングしてうまく機能させる方法はありますか?私はこれを設計しようとして壁に頭をぶつけていますが、うまくいきます。

編集 (詳細):

もう少し具体的に: Foo は IEnumerable を実装しており、簡単に言えば、Bar は同じインターフェイスを提供していますが、その列挙可能なオブジェクトの限られたサブセットに提供しています。すべての Foo は、自分自身のサブセット (つまり、Bar) を作成して返すことができる必要があります。しかし、Foo を実装したいすべての人にこれを心配させたくありません。なぜなら、Bar がプロキシを行い、範囲の制限などを心配するからです。

4

5 に答える 5

4

さて、新しい答え:

  1. Bar をインターフェイスと具象クラスに分割します。
  2. パブリック抽象メソッドを IBar で表現します。
  3. IBar を実装して、Bar を Foo のプライベート ネスト クラスにします。Foo から呼び出すことができる内部コンストラクターを指定します。
  4. それ自体から Bar のインスタンスを作成する保護されたメソッドを Foo に記述します。Foo から派生したクラスは、プロキシ処理だけで十分であれば、これを使用して抽象メソッドを実装できます。より複雑なニーズを持つクラスは、IBar を直接実装できます。抽象メソッドを仮想メソッドに変更して、デフォルトで「this」から新しいバーを作成することもできます。

編集: これに関する 1 つの変形は、パブリック コンストラクターを使用して、Bar をFoo 内で保護されたネストされたクラスにすることです。そうすれば、派生クラスはそれ自体をインスタンス化できますが、無関係なクラスはそれを「見る」ことはできません。インターフェイスを実装から分離する必要がありますが (インターフェイスを公開できるようにするため)、とにかく良いことだと思います。

于 2008-10-14T06:05:40.350 に答える
1

Foo 内のネストされた型を介して Bar のコンストラクターにアクセスできます。

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}
于 2008-10-14T05:56:16.373 に答える
1

Bar 内で Baz を入れ子型にすることは可能でしょうか? これが、他の方法よりも Bar へのアクセスを増やす唯一の方法です。同じ親クラスを持つだけで、Foo の保護されたメンバーへのアクセスのみが許可され、Foo は Bar への特別なアクセス権を持ちません。ネストされた型でこれを行うには他にも曲がりくねった方法があるのではないかと思いますが、実際にはメンテナンス エンジニアにとっては非常に不快なものになるでしょう。

ただし、ある派生クラスが同じ基本クラスから派生した別のクラスのインスタンスを作成するように強制するのは、かなり奇妙な設計です。それは本当にあなたが必要とするものですか?これをもっと具体的に言えば、別のデザインが思いつきやすいのではないでしょうか。

于 2008-10-14T05:32:14.583 に答える
1

少しの間、Bar が Foo から派生していることを忘れてください。これを行うと、「サブクラスが Bar のコンストラクターにアクセスできない場合でも、Foo のすべてのサブクラスが Bar を作成できるようにするにはどうすればよいですか?」という問題のように思えます。

これは非常に簡単に解決できる問題です。

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

Foo のサブクラスが Bar のコンストラクターにアクセスできないことを保証したい場合は、それらを別のアセンブリに配置します。

ここで、Foo から Bar を派生させるには、単純な変更を行います。

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

「コンストラクターを公開したくありません。」小切手。

「バーを作成できるのは、Foo を継承するクラスだけです。」小切手。

「あらゆるタイプの Foo で作成できるはずです。」小切手、

「後で独自のタイプの Foo や Baz を作成したいと考えている人は誰でも (実際にそうなる可能性があります)、デフォルトの CreateBar() 実装で行き詰まるでしょう。それは彼らのニーズを満たすかどうかわからないからです。」これは、Foo.CreateBar() メソッドで何が起こるかに大きく依存していると思います。

于 2008-10-14T06:19:30.430 に答える
0

C# は、C++ のフレンド キーワードに直接相当するものを提供していません。あなたのデザインにはこの種の構造が必要なようです。

C++ では、「friend」を使用して、特定のクラスが別のクラスのプライベート/保護されたメンバーにアクセスできるように指定できます。注: これは、同じアセンブリ内のすべてのクラスにアクセスできる C# 内部修飾子とは異なります。

あなたのデザインを見ると、C++ スタイルのフレンドを必要とする方向に沿って何かをしようとしているようです。Jon Skeet の言うとおりです。これを補うための C# の通常の設計は、ネストされたクラスを使用することです。

このフォーラムの投稿では、さらに詳しく説明し、これを行う方法の例をいくつか示しています。

于 2008-10-14T05:49:40.460 に答える