ICommandHandler<>
の特定の実装を処理するために、それぞれいくつかの実装を持つ汎用インターフェイスがありますICommand
。
public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }
オブジェクトが与えられたとき、ICommand
それを動的に正しいにディスパッチしようとしていますICommandHandler
。現時点でInvoke
は、ディスパッチャ クラスで を使用して、非常に単純なリフレクション アプローチを使用しています。
public void Dispatch<T>(T command) where T : ICommand
{
Type commandType = command.GetType();
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
object handler = IoC.Get(handlerType);
MethodInfo method = handlerType.GetMethod("Handle");
method.Invoke(handler, new object[] { command });
}
このアプローチには 2 つの問題があります。まず、低速反射を使用します。第 2 に、メソッドが何らかの例外をスローすると、例外がラップされ、TargetInvocationException
再スローするとスタック トレースが失われます。
デリゲートを作成して使用することで呼び出しを行う方法を考え出しましたDynamicInvoke
が、これは例外の問題を解決しません (DynamicInvoke
実際に よりも優れているかどうかはわかりませんInvoke
):
public void Dispatch<T>(T command) where T : ICommand
{
Type commandType = command.GetType();
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
object handler = IoC.Get(handlerType);
MethodInfo method = handlerType.GetMethod("Handle");
Type actionType = typeof(Action<>).MakeGenericType(commandType);
Delegate action = Delegate.CreateDelegate(actionType, handler, method);
action.DynamicInvoke(command);
}
私の質問は、私がやろうとしていることを達成するためのより良い方法はありますか? できれば、 を取得して を検索する代わりに、強く型付けされた呼び出しを行うことができobject
ますMethodInfo
。ただし、コンパイル時に型がわからないため、それは不可能だと思います。
それが不可能な場合は、より「ネイティブに」例外をスローする効率的なソリューションが次善の選択肢になります。
編集:コードサンプルを更新して、最初に書いたのではなく、IoC(Ninject)を使用しICommandHandler
て実行時に作成していることを明確にしましActivator.CreateInstance()
た。これが要求に応じてどのように使用されるかの例が含まれています。
var command = new CreateUserCommand() { Name = "Adam Rodger" };
var dispatcher = new CommandDispatcher();
dispatcher.Dispatch(command);
// this would send the message to CreateUserCommandHandler.Handle(command)
// dynamically and any exceptions would come back 'natively'
編集2 :以下に示すように、実行時に取得するためIoC.Get(handlerType)
、の結果をキャストできません。これは、実行時に が実際に であるためです。コマンド クラスが WCF を介して到着し、何らかの形で強力な型指定が失われるためだと思います。ディスパッチャーを呼び出すコードは次のようになります。ICommandHandler<T>
InvalidCastException
T
ICommand
[ServiceContract]
public class CommandService
{
[OperationContract]
public void Execute(ICommand command) // no type information
{
var dispatcher = new CommandDispatcher(); // injected by IoC in real version
dispatcher.Dispatch(command);
}
}