0

私は抽象基本クラスを持っていPます。Pと呼ばれるを継承する2つのクラスがIありOます。

Get()リポジトリに を返すメソッドがありますP。このメソッドは常に、値に応じてIまたはのいずれかのインスタンスを返します。O

public P Get(string pci)
{
     var a = GetAByPCI(string pci);

     if(a == 1)
         return new I();
     else if(a == 2)
         return new O();
     else
         // throw exception here.
}

このメソッドを別の場所から呼び出すときは、型を確認してキャストする必要があります。

P p = PRepository.Get("200");

if(p is I)
    I i = (I)p;

p上記を使用するたびにチェックしてダウンキャストする必要は避けたいと思います。ここで根本的に間違ったことをしているように感じますが、確かではありません。

更新: 派生型I&Oには、使用する必要があるがその特定の型にのみ関連する追加のプロパティがあります。P継承型で実装される多くの抽象プロパティがあります。ダウンキャストは、必要に応じて具象型に到達できる唯一の方法ですか?

たぶん、論理は完全に間違っています。

4

6 に答える 6

2

基本クラスを持つことの全体的なポイントは、サブクラス自体を処理する必要がないようにすることです。基本クラスに抽象メソッドを追加し、それをサブクラスに実装するだけです。このように、呼び出し元のコードは、キャストを実行せずにメソッドを呼び出すだけです。これが基本的なポリモーフィズムです。

于 2013-02-06T21:02:43.340 に答える
1

あなたの意図は何ですか?継承を適切に使用していないようです。メソッドから P オブジェクトを返す場合は、ダウンキャストするか、派生型を返す必要があります。

于 2013-02-06T21:05:11.640 に答える
1

あなたがダウンキャスティングの疑いがあるのは正しいです。

テンプレート メソッドのデザイン パターンを見てみましょう。基本クラスがある場合は、サブクラスの詳細を非表示にするために使用する必要があります。

したがって、基本クラスに Execute() というメソッドがあるとします。基本クラス内では、execute メソッドがいくつかの保護されたメソッド、たとえば Method1() および Method2() を呼び出します。メソッド 1 には共有コードが含まれ、メソッド 2 は抽象化され、子クラスはそれを実行する必要があります。その後、インスタンスを取得したら、Execute を呼び出すだけです。下向きにキャストしなくても、適切な Method2() が実行されます。

public abstract class MyBaseClass
{
    public void DoSomething()
    {
        //Here's a bunch of stuff I have to do everytime
        DoSomethingTypeSpecific();
        //I could do more stuff if I needed to
    }

    protected abstract void DoSomethingTypeSpecific();
}

class MyBaseClassOne : MyBaseClass
{
    protected override void DoSomethingTypeSpecific()
    {
        Console.WriteLine(1);
    }
}

class MyBaseClassTwo : MyBaseClass
{
    protected override void DoSomethingTypeSpecific()
    {
        Console.WriteLine(2);
    }
}
于 2013-02-06T21:12:33.883 に答える
0

また、継承を使用する代わりに、 Decorator パターンと構成を使用することも考えます。

ただし、基本クラスの一部ではないプロパティにアクセスする場合は、キャストが必要です。

于 2013-02-07T12:59:04.747 に答える
-1

プロパティを基本クラスに入れずにキャストを削除することはできませんが、戦略パターンを使用してオブジェクトの作成を切り離すことができます (これを行う方法の例については、こちらの質問と回答を参照してください)。この例では、switch-case を分離する方法を示していますが、コンテキストの結果を分析するための戦略パターンでも同じことができるため、キャストを非表示にすることができます。

public static class A
{
    // please note: some example in related to your question
    // and the strategy pattern provided at the link
    public static P GetI(int a)
    {
        if (a == 1)
            return new I();
        else
            return null; // <- this is important for evaluation
    }

    // can put this method anywhere else, also into another class
    public static P GetO(int a)
    {
        if (a == 2)
            return new O();
        else
            return null; // <- this is important for evaluation
    }

    public static bool Condition(string pci)
    {
        return !string.IsNullOrEmpty(pci); // could eval different states, values
    }
}

次に、as/is ロジックを個別のメソッド/クラスに分割することが可能であり、呼び出しクラス (コントローラー、クライアントなど) はすべての異なる特殊化を知る必要はありません。次のコードは、コンテキストを作成する方法を示しています。Strategy および Context クラスの実装の詳細については、私の投稿を参照してください。

var strategyI = new Strategy<int, P>(A.Condition, A.GetI);
var strategyO = new Strategy<int, P>(A.Condition, A.GetO);

var strategies = new List<Strategy<int, P>> {strategyI, strategyO};
var context = new Context<int, P>(strategies.ToArray());

var result = context.Evaluate(1); // should return a new I(), with 2 as param a new O()

// once you get the result you can create a new context to work with the result

var strategyWorkI = new Strategy<P, P>(Condition, WorkWithI);
var stragegiesForWork = new List<Strategy<P, P>> {strategyWorkI} // could be more than one
var workContext = new Context<P, P>(strategiesForWork.ToArray());

var p = workContext.Evaluate(result); // does the right thing, but
                                      // no need to know class I here (only P)
if (p == null)
  // seems to be something went wrong, throw? ...         

そして、これがキャストのデカップリングですが、この時点ではまだキャストが必要です。

public static P WorkWithI(P p)
{
    var i = p as I; // I would prefer as instead of is (see boxing/unboxing in .net)
    if (i != null)
    {
       // do what you want with concrete class I in i
    }

    return i; // <- this is important for Evaluation
}

とにかく... is/as を含むコードにはまだ Point がありますが、コントローラー/マネージャーなどには影響しません (依存性注入などにより、コードを柔軟でプラグ可能に保ちます)

... 私の簡単な説明が理解できない場合は、完全な例を提供できます。

于 2013-02-06T21:25:45.057 に答える
-1

上記のすべてのソリューションが要件に適合せず、基本クラスを変更したくない場合 (派生クラスからプロパティを追加する) は、ほとんどのコメントで言及されているように、継承の設計だけでなく、次の点も検討する必要があります。ファクトリ/リポジトリの実装。このリポジトリは必要なことを行っていますか? GetI() と GetO() を追加してみませんか?

たとえば、Linq を使用すると、.OfType を使用してリストをクエリおよびフィルター処理でき、タイプ T の要素のみを取得できます。

型情報 (たとえば、私の例で行ったようにジェネリック) と追加の条件を使用して、特定の Get メソッドを追加できます。

public T Get<T>(string pci, int c)
{
     var a = GetAByPCI(string pci);

     if(a == c)
         return new T();
     else
         // throw exception here.
}

ジェネリック パラメータを指定して Get メソッドを呼び出します。ここでのこの解決策の問題は、呼び出し時に条件を知る必要があり、これは「特別な構造」(たとえば、T のパラメーターを持つコンストラクター) を処理しないことです。とにかく、これもファクトリーパターンで解決できます。

I i = PRepository<I>.Get(1, "200"); // create a new I(), only if 1 fits GetAByPCI()
O o = PRepository<O>.Get(2, "123"); // create a new O(), only if 2 fits GetAByPCI()
// can do anything with the concrete type

戦略パターンを使用してリポジトリ/ファクトリ呼び出しから条件を移動することもできます (上記の私の回答で既に言及されています)。

ジェネリック メソッドの背後にある "as" をラップすることもできます (ほとんどの場合はお勧めしませんが、その方法を示します)。

public T Get<T>(string pci)
{
     return this.Get(pci) as T; // your method with trying to cast to concrete type
}

このようにして、次のように実装を取得できます。

I i = PRepository<I>.Get("200");
if (i != null)
{
  // do I specific tasks
}
于 2013-02-06T22:59:30.873 に答える