2012-01-20 69 views
6

我正在解碼來自二進制流的通信消息。我根據消息到達的情況創建不同類型的消息對象。它們都來自基地CommsMessage類型。所有的罰款和花花公子。類型比較的性能成本

在我的代碼中的其他地方,我需要對這些消息作出反應,所以我需要知道它是什麼類型的消息。

目前我做的:

void ProcessIncomingMessage(CommsMessage msg) 
{ 
    if (msg is MessageType1) 
     return ProcessMessageType1(msg as MessageType1); 

    if (msg is MessageType2) 
     return ProcessMessageType2(msg as MessageType2); 

    //etc 
} 

我想知道什麼比較這些類型的性能代價是,是否我應該包括在基類MessageType屬性。然後,我可以這樣做:

void ProcessIncomingMessage(CommsMessage msg) 
{ 
    switch (msg.MessageType) 
    { 
     case MessageType.Type1: return ProcessMessageType1(msg as MessageType1); 
     case MessageType.Type2: return ProcessMessageType2(msg as MessageType2); 

     //etc 
    } 
} 

是的,這是不成熟的優化,我可能擔心在不起眼的細節,但我是那種編碼器誰喜歡知道發生了什麼事情在幕後,所以是想知道兩者之間的表現差異。我想我對我的C++背景中的類型比較有偏見,RTTI引入了開銷,只是想知道.Net是否有任何相似之處。

+1

的可能重複[C# '是' 經營者業績(http://stackoverflow.com/questions/686412/c-sharp-is-operator-performance) –

回答

7

您是否考慮過消除類型轉換?

我猜你認爲將虛擬方法放在Message類型本身會破壞分層抽象(例如,您可能希望將消息處理與消息本身完全分開)。也許考慮visitor pattern。這將允許您從Message本身的處理中分離出Message類。

如果你有這種結構的東西。

abstract class CommsMessage {} 
class Message1 : CommsMessage {} 
class Message2 : CommsMessage {} 

你可以重構爲

abstract class CommsMessage 
{ 
    public abstract void Visit(CommsMessageVisitor v); 
} 

class Message1 : CommsMessage 
{ 
    public void Visit(CommsMessageVisitor v) { v.Accept(this); } 
} 

class Message2 : CommsMessage 
{ 
    public void Visit(CommsMessageVisitor v) { v.Accept(this); } 
} 

interface CommsMessageVisitor 
{ 
    void Accept(Message1 msg1); 
    void Accept(Message1 msg2); 
} 

在這一點上,你已經消除了類型轉換。現在,您可以重寫代碼爲

void ProcessIncomingMessage(CommsMessage msg) 
{ 
    new MyVisitor().Visit(msg); 
} 

class MyVisitor : CommsMessageVisitor 
{ 
    void Accept(Message1 msg1) { ProcessMessageType1(msg1); } 
    void Accept(Message1 msg2) { ProcessMessageType2(msg2); } 
} 

當然也有可能是你不能這樣做的原因,但它總是更好的避免類型轉換,如果你能!

+1

+1感謝你的解釋/假設爲什麼vistor可能需要在這裏 - 否則它似乎矯枉過正;-) –

+0

我同意,除非是這種情況絕對是過度殺傷!這似乎是一個相當普遍的情況,但。你有一些愚蠢的對象(PO​​CO /實體),你只是想轉移到地方(可能在客戶端/服務器之間共享,或者只是在不同的抽象層次上)。你經常需要對它進行不同的處理,但是用虛擬方法拋出它太多了。訪問者模式非常適合移出這些方法的實現。 –

+0

是的,並給出所涉及的類型和方法的名稱,你的是一個合理的假設。 –

2

請注意,您的代碼在語法上不是有效的,因爲返回類型爲void,但無論如何。

嗯,我不太清楚你顯示的兩個選擇的性能差異。然而,至少FxCop的會「suggest」,而不是下面的第一個解決方案:

void ProcessIncomingMessage(CommsMessage msg) 
{ 
    MessageType1 msg1 = msg as MessageType1; 

    if (msg1 != null) 
    { 
     ProcessMessageType1(msg1); 
     return; 
    } 

    MessageType2 msg2 = msg as MessageType2; 

    if (msg2 != null) 
    { 
     ProcessMessageType2(msg2); 
     return; 
    } 


    //etc 
} 

當然,這裏涉及到像維修性,可理解性等 可能會更好,以提供「等問題虛擬無效ProcessMessage()「在您的」CommsMessage「類,您覆蓋每個」MessageType「。然後讓CLR爲你工作。

public class CommsMessage 
{ 
    public virtual void ProcessMessage() 
    { 
     // Common stuff. 
    } 
} 

public class MessageType1 : CommsMessage 
{ 
    public override void ProcessMessage() 
    { 
     base.ProcessMessage(); 
     // type 1 specific stuff. 
    } 
} 

// ... 

void ProcessIncomingMessage(CommsMessage msg) 
{ 
    msg.ProcessMessage(); 
} 

可以說,你可以直接調用msg.ProcessMessage(),在那裏你現在打電話ProcessIncomingMessage,如果沒有別的在那裏做。

+1

+1 from' - as'然後是null檢查優於'is'然後'as' –

1

要添加到上面的優秀答案:

在性能分析我發現,隨後用is通過as實際上導致了較低的性能比單as其次是空檢查。不要指望編譯器自動優化任何東西。假設在消息代碼(或其他性能關鍵部分)中,設計速度非常重要,您是對的。

到目前爲止,最快的演員陣容是一個超過as的靜態演員陣容,即var message = (SpecificType)baseMessage將超越var message = baseMessage as SpecificType。這只是一個感興趣的問題,因爲靜態投射不能幫助你。

由於兩個答案已經提到使用設計模式以多態方式執行上述可能是最好的解決方案,因爲它只添加了虛擬方法調用。將常用方法提取到抽象類(或通用方法簽名接口)是迄今爲止最優雅的解決方案。調用虛擬方法的開銷很大,但可以通過使用關鍵字sealed在派生類型上標記具體方法來緩解這種情況。

最後在可能的地方使用泛型來消除強制轉換,因爲泛型方法是編譯時優化,而不是運行時強制轉換。

最好的問候,