【问题标题】:How to change subclass referenced by parent class to have a reference type of subclass?如何将父类引用的子类更改为子类的引用类型?
【发布时间】:2013-07-18 20:24:53
【问题描述】:

我有 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)?

【问题讨论】:

    标签: c# inheritance casting reference-type derived-types


    【解决方案1】:

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

    public void Process(EventDto eventDto)
    {
       //Do nothing
       if (eventDto is EventDtoSubclass1)
       {
           // do something   
       }
       else if (eventDto is EventDtoSubclass2)
       {
           // do something else
       }
    }
    

    【讨论】:

      【解决方案2】:

      有没有办法将 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
      

      【讨论】:

      • 添加virtual处理方法,即
      • 是的。我添加了更多解释。
      • alternative 之前一切都很好。 -1(如果你能说明它是如何工作的,我准备删除 -1)
      • 对不起?克里斯哈迪的回答是我的选择。如果不了解更多关于 Process 的作用和 EventDto 是什么,我无法真正充实它。
      • 好吧,我迷路了。我不明白为什么它不起作用。你能告诉我为什么吗?
      【解决方案3】:

      在对代码了解不多的情况下,是否有机会从架构的角度稍微修改一下?

      在我看来,您可以使用命令模式的变体,并让 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-03-01
        • 2023-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-21
        相关资源
        最近更新 更多