如何更改父类引用的子类以获得子类的引用类型?

时间:2013-07-18 20:24:53

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

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

3 个答案:

答案 0 :(得分:1)

  

有没有办法将ProcessMessage()中的eventDto类型从EventDto更改为其实际派生类型(例如,EventDtoSubclass2)?

没有。您要么提前知道类型(并且可以将其转换为或者将其用作泛型类型参数或其他类型),或者您必须在事实之后检测它并在其上进行分支。既然看起来你不知道你得到了什么,那么你需要检测它并在其上分支(我的意思是使用is / as或{{1} } / GetType或您最喜欢的任何检查方法是。)

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

编辑:由于I4V表示它不起作用,我将在这里放一个完整的程序,显示我的选择意味着什么。就像我多次说的那样,除非我们知道Process需要做什么,否则我们不知道这是否真的适用于OP。但它原则上确实有效。

typeof

输出:

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; }
    }
}

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

在我看来,您可以使用Command模式的变体,并让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();
}