2

我有 20 个左右的事件类都继承自 EventDto。这些子类都被序列化/反序列化(使用 DataContractJsonSerializer,每个子类都被添加为 [KnownType(typeof(subclasstype))] 属性),反序列化方法如下所示:

private EventDto DeserializeMessage(byte[] body)
    {
        var stream = new MemoryStream(body);
        var serializer = new DataContractJsonSerializer(typeof(EventDto));

        var eventDto = (EventDto)serializer.ReadObject(stream);

        return eventDto;
    }

反序列化一个事件后,我需要根据它的类型来处理它。所以我有:

public void ProcessMessage(byte[] serializedEvent)
    {
        //Deserialize
        var eventDto = DeserializeMessage(serializedEvent);

        //Process
        Process(eventDto);
    }

public void Process(EventDto eventDto)
    {
        //Do nothing
    }

    public void Process(EventDtoSubclass1 eventDto)
    {
        //Do something
    }

    public void Process(EventDtoSubclass2 eventDto)
    {
        //Do something different
    }

问题是ProcessMessage()中的eventDto有一个EventDto的引用类型,所以调用的Process()方法总是一样的。我需要能够区分 EventDto 的不同子类并调用适当的方法。

有什么方法可以将 ProcessMessage() 中的 eventDto 类型从 EventDto 更改为其实际派生类型(例如 EventDtoSubclass2)?

4

3 回答 3

1

有什么方法可以将 ProcessMessage() 中的 eventDto 类型从 EventDto 更改为其实际派生类型(例如 EventDtoSubclass2)?

不。您要么提前知道类型(并且可以强制转换它或将其用作泛型类型参数或其他),要么您必须在事后检测它并对其进行分支。由于您似乎不提前知道您得到了什么,因此您需要检测它并对其进行分支(我的意思是使用is/asGetType/typeof或任何您最喜欢的检查方法)。

作为一种替代方案,如果您可以更改 EventDto 派生类,则可以潜在地将虚拟 Process 函数添加到基类中,并在每个派生类中执行正确的操作的覆盖版本。不过,这取决于 Process 实际上做了什么。

编辑:由于 I4V 说它不起作用,我将在这里放一个完整的程序来展示我对我的替代方案的意思。就像我多次说过的那样,除非我们知道 Process 需要做什么,否则我们不知道这是否真的适用于 OP。但它在原则上确实有效。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;

namespace SOTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();

            EventDtoA a = new EventDtoA() { AProperty = 0, BaseProperty = -1 };
            EventDtoB b = new EventDtoB() { BProperty = 1, BaseProperty = -1 };
            EventDtoC c = new EventDtoC() { CProperty = 2, BaseProperty = -1 };

            var aBytes = program.SerializeMessage(a);
            var bBytes = program.SerializeMessage(b);
            var cBytes = program.SerializeMessage(c);

            var aString = System.Text.Encoding.UTF8.GetString(aBytes);

            EventDto aNew = program.DeserializeMessage(aBytes);
            EventDto bNew = program.DeserializeMessage(bBytes);
            EventDto cNew = program.DeserializeMessage(cBytes);

            aNew.Process();
            bNew.Process();
            cNew.Process();

            Console.ReadKey();
        }

        private byte[] SerializeMessage(EventDto eventDto)
        {
            var stream = new MemoryStream();
            var serializer = new DataContractJsonSerializer(typeof(EventDto));
            serializer.WriteObject(stream, eventDto);
            var tempBytes = new Byte[stream.Length];
            Array.Copy(stream.GetBuffer(), tempBytes, stream.Length);
            return tempBytes;
        }

        private EventDto DeserializeMessage(byte[] body)
        {
            var stream = new MemoryStream(body);
            var serializer = new DataContractJsonSerializer(typeof(EventDto));

            var eventDto = (EventDto)serializer.ReadObject(stream);

            return eventDto;
        }

        public void ProcessMessage(byte[] serializedEvent)
        {
            //Deserialize
            var eventDto = DeserializeMessage(serializedEvent);

            //Process
            eventDto.Process();
        }


    }

    [KnownType(typeof(EventDtoA))]
    [KnownType(typeof(EventDtoB))]
    [KnownType(typeof(EventDtoC))]
    public class EventDto
    {
        public virtual void Process() 
        {
            Console.WriteLine("From EventDto Base Class");
        }

        public int BaseProperty { get; set; }
    }

    public class EventDtoA : EventDto
    {
        public override void Process()
        {
            Console.WriteLine("From EventDto A");
        }

        public int AProperty { get; set; }
    }

    public class EventDtoB : EventDto
    {
        public override void Process()
        {
            Console.WriteLine("From EventDto B");
        }

        public int BProperty { get; set; }
    }

    public class EventDtoC : EventDto
    {
        public override void Process()
        {
            Console.WriteLine("From EventDto C");
        }

        public int CProperty { get; set; }
    }
}

输出:

From EventDto A 
From EventDto B 
From EventDto C
于 2013-07-18T20:29:44.473 回答
1

最简单的方法是使用is运算符。

public void Process(EventDto eventDto)
{
   //Do nothing
   if (eventDto is EventDtoSubclass1)
   {
       // do something   
   }
   else if (eventDto is EventDtoSubclass2)
   {
       // do something else
   }
}
于 2013-07-18T20:31:59.670 回答
0

在不了解您的代码的情况下,是否有机会从架构的角度稍微重做一些事情?

在我看来,您可以使用命令模式的变体,并让 eventDTO 对象负责它们自己的处理。

public interface IEventDTO{
     void Process();
}

public class EventDTO1 : IEventDTO{
    public void Process(){
        //Glorious codde!
    }
}

public class EventDTO2 : IEventDTO{
   public void Process(){
        //More glorious code!!
   }
}

然后,您可以拥有一个不需要关心处理所有不同 EventDTO 的实现细节的处理器。将来,如果您需要添加更多 EventDTO,处理器将不受任何更改的影响。

public class Processor{
     public void Process(IEventDTO eventDTO){
          eventDTO.Process();
     }
}

这是即时编写的,请原谅任何错误。也请原谅我的任何天真,因为我对您的应用程序的了解很少,而且它不符合要求:)

编辑

我在这里可能有点累。如果您不想将方法附加到 dto,这是完全可以理解的,我们可以创建一个 IEventProcessor 接口并使用工厂来创建实现类型的实例,并使用这些实例进行处理:

public IEventProcessor GetEventProcessor(IEventDTO eventDTO){
      if(eventDTO is EventDTO1)
            return new EventProcessor1();

      if(eventDTO is EvenetDTO2)
            return new EventProcessor2();
}
于 2013-07-18T20:38:28.687 回答