2016-11-07 4 views
1

私は様々なビヘイビアクラスを取得し、それらをまとめて一連のクエリとコマンドにリンクすることができます。私が取ったアプローチは以下の通りですが、型を派生させて型を基底クラスにキャスティングする問題が発生しています。私はジェネリックパラメータを扱う必要があります。このアプローチが不可能なのか、何らかの暗黙的または明示的な変換演算子を定義する必要があるのか​​どうかはわかりません。より少ない派生型へのキャストとジェネリックの扱い

ここには基本タイプのセットがあります。

public interface IInvoker 
{ 
    void Invoke(); 
} 

この抽象クラスは、そのメンバーのうちの1つを介してコマンドまたはクエリを行うものである「件名」を持っている機能を追加します:まず、最も基本的なインタフェースです。これは、Invokeメソッドを実装延期:

public abstract class AbstractInvoker<TSubject> : IInvoker 
{ 
    protected TSubject Subject; 

    public void SetSubject(TSubject subject) 
    { 
     Subject = subject; 
    } 

    public abstract void Invoke(); 
} 

この次の抽象クラスは、任意の具体的なクエリクラスによって実装タイプ(TResultタイプを持っていないコマンドとは対照的に)なります。これは、サクセサを介してクエリを連鎖させる機能を設定します。

public abstract class AbstractQueryInvoker<TSubject, TResult> : AbstractInvoker<TSubject> 
{ 
    protected AbstractInvoker<TResult> Successor; 

    public void SetSuccessor(AbstractInvoker<TResult> successor) 
    { 
     Successor = successor; 
    } 

    public override void Invoke() 
    { 
     var result = DoQuery(); 

     Successor.SetSubject(result); 
     Successor.Invoke(); 
    } 

    protected abstract TResult DoQuery(); 
} 

実際のクエリロジックは、DoQuery()メソッドを使用して具象クラスに実装されています。

私はこのようにそれを設定するので、私はこのように一緒にクエリをチェーンできます

private List<IInvoker> _invokers; 

// Build the list of various concrete classes 

for (int i = 0; i < _invokers.Count - 1; i++) 
{ 
    ((AbstractQueryInvoker<dynamic, dynamic>)_invokers[i]).SetSuccessor(
                     (AbstractInvoker<dynamic>) 
                     _invokers[i + 1]); 
} 

その後継者は、私はすべてのように、ここまで連鎖している私の目的は、最後のものを除いて、各呼び出しを持っていることでした最初の要素にInvoke()と呼ぶことが必要です。しかし、forループの最初のキャストは機能しません(そして2番目のキャストはどちらでもないかもしれません、私は推測しています)。私はそれぞれの具体的な実行者に特定の何かを実装することなく、これを回避するにはいくつかの方法がある願ってい

:エラーメッセージは次のようになります。私はこれらの具体的なクラスの数十を持っている可能性があり、それぞれジェネリック型のパラメータに異なるタイプを使用します。ですから、これを改善する方法はありますか?

+1

名前あなたの例では、一貫していません。例えば、 'IActionInvoker'はどこにも表示されません。コンパイル可能な実際のコードを投稿してください。 –

+0

@JesseGood - 修正されました。 – bubbleking

答えて

0

だけdynamicに各呼び出しをキャスト:

// Build the list of various concrete classes 
for (int i = 0; i < _invokers.Count - 1; i++) 
{ 
    ((dynamic)_invokers[i]).SetSuccessor((dynamic)_invokers[i + 1]); 
} 

あなたの元のコードが動作しない理由: AbstractQueryInvoker<dynamic, dynamic>dynamicキーワードを使用していますが、それはダイナミック型のみdynamicそれだけで、実際にはありません動的です。実行時に、dynamic型引数はobjectに置き換えられ、キャストは失敗します。

+0

興味深い。これは機能します。私は動的ではなくオブジェクトを使用していたとすれば、それもうまくいったと言っていますか?いずれにせよ、ありがとう。 – bubbleking

+1

いいえ、 '' AbstractQueryInvoker'2 [System.Object、System.Object] ''が 'dynamic'が' object 'に置き換えられたため、実行時例外に言及しました。 –

+0

初めての誤解です。もう一度読んだので、動的ではなくオブジェクトを使用しても、オブジェクトへのキャストが失敗するという事実は変わりません。 – bubbleking

1

私があなたがしようとしていることを理解していれば、あなたのコードを少し複雑にして、壊れやすいものにしてしまったと思います。

はこのような何かがあなたのために優れている場合を教えてください:

各ステップを実行した後 "11!"を出力
IInvoker<string> invoker = 
    5 
     .CreateBehaviour() 
     .AddBehaviour(n => n * 2) 
     .AddBehaviour(n => n + 1) 
     .AddBehaviour(n => n.ToString()) 
     .AddBehaviour(n => n + "!"); 

Console.WriteLine(invoker.Invoke()); 

それを行うためのコードはこれです:

public static class BehaviorsEx 
{ 
    private class Subject<TSubject> : IInvoker<TSubject, TSubject> 
    { 
     private TSubject _subject; 

     public Subject(TSubject subject) 
     { 
      _subject = subject; 
     } 

     public TSubject Invoke() { return _subject; } 
    } 

    private class Invoker<TSubject, TResult> : IInvoker<TSubject, TResult> 
    { 
     private IInvoker<TSubject> _inner; 
     private Func<TSubject, TResult> _behaviour; 

     public Invoker(IInvoker<TSubject> inner, Func<TSubject, TResult> behaviour) 
     { 
      _inner = inner; 
      _behaviour = behaviour; 
     } 

     public TResult Invoke() 
     { 
      var x = _inner.Invoke(); 
      return _behaviour(x); 
     } 
    } 

    public static IInvoker<TSubject> CreateBehaviour<TSubject>(this TSubject @this) 
    { 
     return new Subject<TSubject>(@this); 
    } 

    public static IInvoker<TResult> AddBehaviour<TSubject, TResult>(this IInvoker<TSubject> @this, Func<TSubject, TResult> behaviour) 
    { 
     return new Invoker<TSubject, TResult>(@this, behaviour); 
    } 
} 

public interface IInvoker<TResult> 
{ 
    TResult Invoke(); 
} 

public interface IInvoker<TSubject, TResult> : IInvoker<TResult> 
{ 
} 
+0

興味深い。私は今日これをどのように動作するか見るために遊んでみることにして、私はあなたに戻ってきます。 – bubbleking

+0

これはおそらく私の状況ではうまくいかないと思いますが、特にFuncとActionで同様のバリエーションがあるかどうかを確認します。私がやっていることは本質的にマクロを実行することです。マクロがとる各ステップの定義は、データベースに格納されます。各マクロには、1つまたは複数のクエリとそれに続くコマンドがあります。すべてのクエリには件名、結果、後継者があります。後継者は別のクエリか最終的なコマンドです。最後のコマンドは、クエリ文字列の結果に対してvoid操作を行います。実際にはプロセスは次のようになります。 – bubbleking

+0

タイプBのオブジェクトのクエリコレクションAは、結果を次のクエリに渡します。 タイプCのオブジェクトのクエリコレクションBを渡します。 。 DのクエリーCは、それを渡します。 Dの各要素に対してコマンドを実行します。 コマンドには件名のみが含まれています。結果や後継者はありません。しかし、(異なるタイプのパラメータを使用して)コマンドの後に一連のクエリを実行するには、IInvoker が入っている共通のインターフェイスが必要でした。この方法で、DBから命令レコードを取得し、それらをアセンブルできますチェーンを起動します。 – bubbleking

関連する問題