2013-02-05 114 views
0

我有一個基類消息類和大約100個不同的消息子類,它們表示可以處理的每種消息類型。我目前正在考慮的是使用巨大的switch語句來創建消息對象。例如:處理創建大量子類型對象的最佳方法

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. 

有沒有更好的方法來做到這一點?如果是這樣,你可以展示一個簡單的代碼示例。

編輯

於是,我試着這樣做:

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

這似乎在myEnumerable工作現在包含消息亞型的所有100個,加上基本消息類型爲好。但是,雖然我並不介意在程序開始時使用反射來加載類型,但使用它來實時訪問適當的對象可能會太慢。所以,我想嘗試使用委託。

在下面從@馬克希爾德雷思註釋的例子:那麼,有

」你>然後,你的映射會映射[MessageType.ChatMsg] = X =>新MsgChat的字典( X);」

有幾種方法來解釋這段代碼。一個想法是刪除所有100個子類,並使用100個委託方法使用一個大型類。這是一個遙遠的第二選擇。另一個想法和我的第一選擇是讓上面的代碼以某種方式創建一個消息子類對象。但是,我不太明白它會如何做到這一點。另外,在我的Test類中保留上面的技巧並獲得所有類型或委託而不必寫出所有類型或委託,這將是非常好的。你或其他人可以解釋如何做到這一點嗎?

+0

爲什麼你需要100個類時,類型屬性可能足夠類? – tschmit007

+0

利用多態性並橫向擴展系統的智能。 –

回答

3

而不是使用一個巨型switch聲明,你可以定義一個Dictionary映射每個MessageType價值,其定義Message派生類,並創建使用該地圖數據的實例。

字典的定義:

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

...

字典消費:

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

請注意,我不編譯這個代碼,以驗證是否正確,或者不。我只想告訴你這個想法。

編輯

還有就是我的新的答案,以改善第一個。我正在考慮你編輯的問題,使用@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); 
    } 
} 
+3

這是我會使用的一般想法。但是,我會建議通過使用函數作爲字典的值避免反射。所以,你需要一個'>'的字典。然後,你的映射將是'映射[MessageType.ChatMsg] = x => new MsgChat(x);' –

+2

另外,請記住像這樣的東西可以通過添加屬性到你的Message類和使用代碼找到所有這些類並自動構建字典。查看[這個問題](http://stackoverflow.com/questions/720157/finding-all-classes-with-a-particular-attribute)如何搜索屬性。 –

+0

非常有趣。所以,這聽起來像我應該能夠在基類中指定一個屬性以及Inherited = True開關,以便所有的子類都能夠接受它。然後可以在基類中指定第二個屬性,這樣基類將不會被鏈接中定義的GetTypesWith方法拾取。一個問題是,Func 定義了一個帶有2個參數的泛型委託方法,或者只有一個?我所有的子類構造函數都只有一個參數--Buffer,它被定義爲一個Byte數組。我不確定第二個參數會是什麼。 –

2

你在正確的軌道上,使用字典是一個好主意。如果反射太慢,你可以使用表達式,就像這樣(我假設你使用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(); 
} 

} 
+0

有趣的想法,但我看到你的代碼在他們的編譯方法。不知道這是否會比反思更好。 我現在正在看的是簡單地擺脫我寫的100個課程,並用方法替換它們。然後消息將變成超類的東西,但它將支持靜態鏈接,並且將是處理消息傳遞到網絡的最有效方式。 –

+0

因爲它是輕型代碼生成,所以它比反射更好。它幾乎與手動編寫新消息(buf)一樣快。 compile()僅被調用一次以創建Func <>。一旦創建,就像其他任何Func一樣。我正在使用類似的方法來爲SqlFu(我的microOrm)生成映射代碼並且它是FAST。恕我直言,將這些課程組合成一個巨人班不是一種明智的做法。多類方法是可擴展的,上面的代碼適用於1或1000個類,而無需更改。 – MikeSW

相關問題