简体   繁体   English

处理创建大量子类型对象的最佳方法

[英]Best way to handle creation of large number of subtype objects

I have a base Message class, and around 100 different subtype classes of Message that represent each type of message that can be processed. 我有一个基本的Message类,以及大约100个不同的Message子类型类,它们代表可以处理的每种消息类型。 What I am currently considering doing is using a giant switch statement to create the message object. 我目前正在考虑使用巨型switch语句创建消息对象。 For example: 例如:

switch (MsgType)
{
   case MessageType.ChatMsg:
      Msg = new MsgChat(Buf);
      break;
   case MessageType.ResultMsg:
      Msg = new MsgResult(Buf);
      break;
   ... // 98 more case statements
}
Msg.ProcessMsg(); // Use a polymorphic call to process the message.

Is there a better way to do this? 有一个更好的方法吗? If so, can you show an easy code example. 如果是这样,您可以显示一个简单的代码示例。

EDIT 编辑

So, I tried doing this: 因此,我尝试这样做:

public class Test
{
   public Test()
   {
      IEnumerable<Type> myEnumerable = GetTypesWith<MyAttribute>(true);
   }

   IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit)
      where TAttribute : System.Attribute
   {
      return from a in AppDomain.CurrentDomain.GetAssemblies()
             from t in a.GetTypes()
             where t.IsDefined(typeof(TAttribute), inherit)
             select t;
   }
}

This appears to work in that myEnumerable now contains all 100 of the message subtypes, plus the base Message type as well. 这似乎起作用,因为myEnumerable现在包含所有100个消息子类型,以及基本消息类型。 However, while I don't mind using reflection at the beginning of the program to load the types, using it to access the proper object in real time might be too slow. 但是,尽管我不介意在程序开始时使用反射来加载类型,但是使用它来实时访问适当的对象可能太慢了。 So, I would like to try out using a delegate. 因此,我想尝试使用委托。

The example in the comment below from @Mark Hildreth: @Mark Hildreth在下面的注释中的示例:

"So, you'd have a dictionary of >. Then, your mappings would be mappings[MessageType.ChatMsg] = x => new MsgChat(x);" “所以,您将有一个>的字典。那么,您的映射将是mappings [MessageType.ChatMsg] = x => new MsgChat(x);”

There are a couple of ways to interpret this code. 有两种方法可以解释此代码。 One idea is to remove all 100 subclasses and just use one massive class with 100 delegate methods. 一种想法是删除所有100个子类,而只使用一个具有100个委托方法的大型类。 That is a distant 2nd choice. 那是遥远的第二选择。 The other idea and my first choice is for the above code to somehow create a message subclass object. 另一个想法也是我的第一选择,就是让上述代码以某种方式创建消息子类对象。 But, I don't quite understand how it would do this. 但是,我不太了解它将如何做到这一点。 Also, it would be nice to keep the above technique in my Test class of getting all the types or delegates without having to write all 100 of them. 同样,将上述技术保留在我的Test类中来获取所有类型或委托,而不必编写所有100个类型或委托,将是很好的。 Can you or anyone else explain how this can be done? 您或其他任何人都可以解释如何做到这一点吗?

Instead using a giant switch statement, you can define a Dictionary to map each MessageType value to its defined Message derived class and creates an instance using this mapping data. 您可以定义一个Dictionary将每个MessageType值映射到其定义的Message派生类,并使用此映射数据创建一个实例,而不是使用巨型switch语句。

Dictionary definition: 字典定义:

Dictionary<int, Type> mappings = new Dictionary<int, Type>();
mappings.Add(MessageType.ChatMsg, typeof(MsgChat));
mappings.Add(MessageType.ResultMsg, typeof(MsgResult));

... ...

Dictionary consumption: 词典消耗:

ConstructorInfo ctor = mappings[MessageType.ChatMsg].GetConstructor(new[] { typeof(Buf) });
Message message = (Message)ctor.Invoke(new object[] { Buf });

Note that I don't compiled this code to verify if is correct or not. 请注意,我没有编译此代码来验证是否正确。 I only want to show you the idea. 我只想告诉你这个主意。

EDIT 编辑

There is my new answer to improve the first one. 我有新的答案可以改善第一个答案。 I'm thinking on your edited question, using given ideas from @MikeSW and @Mark Hildreth . 我正在考虑使用@MikeSW@Mark Hildreth给出的想法来解决您编辑过的问题。

public class FactoryMethodDelegateAttribute : Attribute
{
    public FactoryMethodDelegateAttribute(Type type, string factoryMethodField, Message.MessageType typeId)
    {
        this.TypeId = typeId;
        var field = type.GetField(factoryMethodField);
        if (field != null)
        {
            this.FactoryMethod = (Func<byte[], Message>)field.GetValue(null);
        }
    }

    public Func<byte[], Message> FactoryMethod { get; private set; }
    public Message.MessageType TypeId { get; private set; }
}

public class Message
{
    public enum MessageType
    {
        ChatMsg,
    }
}

[FactoryMethodDelegate(typeof(ChatMsg), "FactoryMethodDelegate", Message.MessageType.ChatMsg)]
public class ChatMsg : Message
{
    public static readonly MessageType MessageTypeId = MessageType.ChatMsg;
    public static readonly Func<byte[], Message> FactoryMethodDelegate = buffer => new ChatMsg(buffer);
    public ChatMsg(byte[] buffer)
    {
        this.Buffer = buffer;
    }

    private byte[] Buffer { get; set; }
 }

public class TestClass
{
    private IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) where TAttribute : Attribute
    {
        return from a in AppDomain.CurrentDomain.GetAssemblies()
               from t in a.GetTypes()
               where t.IsDefined(typeof(TAttribute), inherit)
               select t;
    }

    [Test]
    public void Test()
    {
        var buffer = new byte[1];
        var mappings = new Dictionary<Message.MessageType, Func<byte[], Message>>();
        IEnumerable<Type> types = this.GetTypesWith<FactoryMethodDelegateAttribute>(true);
        foreach (var type in types)
        {
            var attribute =
                (FactoryMethodDelegateAttribute)
                type.GetCustomAttributes(typeof(FactoryMethodDelegateAttribute), true).First();

            mappings.Add(attribute.TypeId, attribute.FactoryMethod);
        }

        var message = mappings[Message.MessageType.ChatMsg](buffer);
    }
}

You're on a right track and using a dictionary is a good idea. 您走的路正确,使用字典是个好主意。 If reflection is too slow you can use expressions, like this (I'm assuming you decorate the Messages classes with a MessageTypeAttribute). 如果反射太慢,则可以使用这样的表达式(我假设您使用MessageTypeAttribute装饰Messages类)。

public class Test
{
 public Test()
  {
     var dict=new Dictionary<MessageType,Func<Buffer,Mesage>>();
     var types=from a in AppDomain.CurrentDomain.GetAssemblies()
         from t in a.GetTypes()
         where t.IsDefined(MessageTypeAttribute, inherit)
         select t;
    foreach(var t in types) {
      var attr = t.GetCustomAttributes(typeof (MessageTypeAttribute), false).First();
       dict[attr.MessageType] = CreateFactory(t);
       }

      var msg=dict[MessageType.Chat](Buf);
  }

 Func<Buffer,Message> CreateFactory(Type t)
 {
      var arg = Expression.Parameter(typeof (Buffer));
        var newMsg = Expression.New(t.GetConstructor(new[] {typeof (Buffer)}),arg);
        return Expression.Lambda<Func<Buffer, Message>>(newMsg, arg).Compile();
}

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM