2

この動的二重ディスパッチの例を C#に移植しようとしています。私は動作する例を持っていますが、リフレクションを使用して必要なハンドラーを作成し、適切なメソッドを呼び出すことDynamicDispatchで、クラス内のメソッドを少しショートカットしたように感じます。MessageBase変更方法についてアドバイスをいただけますか?この例では C++ の dynamic_cast 演算子を使用していますが、C# の同等の演算子が何であるかはわかりません。私の解決策が正しい/最善の方法であるかどうかはわかりません。

注: 3.5 を使用しているため、dynamic キーワードを使用できません

コードは次のとおりです。

メッセージ

public interface IMessage
{
   void Dispatch(IHandler handler);
}

MessageBase

public abstract class MessageBase : IMessage
{
   public abstract void Dispatch(IHandler handler);

   // This is my concern, doesnt feel like the right way to do this
   protected void DynamicDispatch<MessageType>(IHandler handler, MessageType self)
   {
      // Get the messages derived type
      Type self_type = self.GetType();   
      // Create actual message specific handler
      Type message_handler = typeof(IMessageHandler<>).MakeGenericType(self_type);
      // Get the ProcessMessage method
      MethodInfo minfo = message_handler.GetMethod("ProcessMessage");
      try
      {
         // Invoke it with the message
         minfo.Invoke(handler, new object[] { self });
      }
      catch (TargetException ex)
      {
         // Ignore if method doesnt exist
      }
   }
}

メッセージ

public class Message : MessageBase
{
   public override void Dispatch(IHandler handler)
   {
      DynamicDispatch(handler, this);
   }
}

IHandler

public interface IHandler
{
}

IMessageHandler

public interface IMessageHandler<MessageType> : IHandler
{
   void ProcessMessage(MessageType message);
}

DerivedMessageOne

public class DerivedMessageOne : Message
{
   public int MessageOneField;
}

派生メッセージ 2

public class DerivedMessageTwo : Message
{
   public int MessageTwoField;
}

DerivedMessageHandlerOne

public class DerivedMessageHandlerOne : IMessageHandler<DerivedMessageOne>,
   IMessageHandler<DerivedMessageTwo>
{
   #region IMessageHandler<MessaegType> Members

   // ************ handle both messages *************** //
   public void ProcessMessage(DerivedMessageOne message)
   {
      // Received Message one, do soemthing with i
      int do_something_with_it = message.MessageOneField;
   } 

   public void ProcessMessage(DerivedMessageTwo message)
   {
      // Received Message two, do soemthing with i
   } 

   #endregion
}

DerivedMessageHandlerTwo

public class DerivedMessageHandlerTwo : IMessageHandler<DerivedMessageOne>
{
   #region IMessageHandler<MessaegType> Members

   // ************ handle just MessageOne *************** //
   public void ProcessMessage(DerivedMessageOne message)
   {
      // Received Message one, do soemthing with i
   }

   #endregion
}

テストケース

IMessage messageOne = new DerivedMessageOne();
IMessage messageTwo = new DerivedMessageTwo();
IHandler handlerOne = new DerivedMessageHandlerOne();
IHandler handlerTwo = new DerivedMessageHandlerTwo();

messageOne.Dispatch(handlerOne);
messageOne.Dispatch(handlerTwo);

messageTwo.Dispatch(handlerOne);
messageTwo.Dispatch(handlerTwo);
4

2 に答える 2

2

asC# では、演算子 が必要です。http://msdn.microsoft.com/en-us/library/vstudio/cscsdfbt.aspx

C++ とまったく同じように動作しますdynamic_cast

ここでは、次のように使用されます。

IMessageHandler<MessageType> handlerTarget = handler as IMessageHandler<MessageType>;
handlerTarget.ProcessMessage(message);

ご指摘のとおり、次のようなメッセージ タイプをメッセージ ベースに渡す必要があります。

class MessageBase
{
    protected void DoDispatch<T>(T m)
    {
        // ...
    }
}
class Message<T> : MessageBase where T : class
{
    public void Dispatch()
    {
        DoDispatch<T>(this as T);
    }
}
class MyMessage : Message<MyMessage>
{
}
于 2013-03-28T16:31:16.573 に答える
0

動的なしで正常に動作するこの DoubleDispatch を見つけました:

namespace DoubleDispatch
{
public interface IMessage
{
    void Dispatch(HandlerBase handler);
}

public interface IHandler
{
    void HandleDefault(IMessage message);
}

public interface IHandler<in TMessage> : IHandler
    where TMessage : class, IMessage
{
    void HandleSpecific(TMessage message);
}

public interface IMessage<TMessage, THandler> : IMessage
    where TMessage : class, IMessage<TMessage, THandler>
    where THandler : class, IHandler<TMessage>
{
}

public abstract class HandlerBase : IHandler
{
    public void HandleDefault(IMessage message)
    {
        Console.WriteLine("HandlerBase process {0}", message.GetType().Name);
    }
}

public abstract class MessageBase<TMessage, THandler>
    : IMessage<TMessage, IHandler<TMessage>>
    where TMessage : class, IMessage, IMessage<TMessage, IHandler<TMessage>>
    where THandler : class, IHandler, IHandler<TMessage>
{
    public void Dispatch(HandlerBase handler)
    {
        var runtimeHandler = handler as THandler;
        if (runtimeHandler != null)
        {
            var runtimeMessage = this as TMessage;
            if (runtimeMessage != null)
            {
                runtimeHandler.HandleSpecific(runtimeMessage);
                return;
            }
        }
        handler.HandleDefault(this);
    }
}

public class FirstMessage : MessageBase<FirstMessage, IHandler<FirstMessage>>
{
}

public class SecondMessage : MessageBase<SecondMessage, IHandler<SecondMessage>>
{
}

public class FirstHandler : HandlerBase, IHandler<FirstMessage>
{
    public void HandleSpecific(FirstMessage message)
    {
        Console.WriteLine("FirstHandler process {0}", message.GetType().Name);
    }
}

public class SecondHandler : HandlerBase, IHandler<SecondMessage>
{
    public void HandleSpecific(SecondMessage message)
    {
        Console.WriteLine("SecondHandler process {0}", message.GetType().Name);
    }
}

public class ThirdHandler : HandlerBase, IHandler<FirstMessage>, IHandler<SecondMessage>
{
    public void HandleSpecific(FirstMessage message)
    {
        Console.WriteLine("ThirdHandler process {0}", message.GetType().Name);
    }

    public void HandleSpecific(SecondMessage message)
    {
        Console.WriteLine("ThirdHandler process {0}", message.GetType().Name);
    }
}

public class EmptyHandler : HandlerBase
{
}

public static class DoubleDispatch
{
    public static void Test()
    {
        var handlers = new HandlerBase[]
        {
            new FirstHandler(),
            new SecondHandler(),
            new ThirdHandler(), 
            new EmptyHandler(), 
        };

        var messages = new IMessage[]
        {
            new FirstMessage(),
            new SecondMessage(), 
        };

        Array.ForEach(messages, m =>
        {
            Array.ForEach(handlers, m.Dispatch);
            Console.WriteLine();
        });
    }
}
}
于 2014-11-02T10:57:00.680 に答える