2012-01-20 12 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 
    } 
} 

をはい、これは時期尚早な最適化である、と私はおそらく枝葉末節の上に心配だけど、私はカバーの下に何が起こっているのかを知るのが好きコーダの一種だとそうでした両者のパフォーマンスの違いが不思議です。 RTTIがオーバーヘッドを導入した私のC++バックグラウンドからの型比較に対して偏見があると思うし、.Netに類似点があるのか​​どうか疑問に思っただけです。

+1

可能重複あなたの説明については(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 [C#がオペレータのパフォーマンス 'です'] /仮定なぜvistorがここに必要かもしれない - そうでなければそれは過度のように見えるだろう;-) –

+0

それは事実でない限り、確かに過度に同意する!それはかなり一般的なケースだと思われる。あなたは、(クライアント/サーバ間で共有されていても、別の抽象化レベルでのみ共有されていてもよい)場所に転送するだけのダムオブジェクト(POCO /エンティティ)があります。あなたはしばしばそれの異なる処理を望んでいますが、それを仮想メソッドに押し込むことはあまりにも多くを暴露します。訪問者のパターンは、これらのメソッドの実装を取り除くために素晴らしいです。 –

+0

うん、あなたが関係するタイプとメソッドの名前が与えられれば、あなたは合理的な前提です。 –

2

戻り値の型はvoidであるため、コードは構文的には有効ではありません。

あなたが表示する2つの選択肢のパフォーマンスの違いについてはあまりよく分かりません。しかし、少なくとも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 
} 

、保守性、わかりやすさなど のようにここにかかわる他の問題は、おそらく「提供する方が良いだろうがあります仮想void ProcessMessage() "、" MessageType "ごとに上書きする" CommsMessage "クラス。その後、CLRがあなたのために働きます。そこに行うには他に何も存在しない場合、あなたが今、ProcessIncomingMessageを呼び出すところ

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()を呼び出すことができます。上記の優れた答えに追加するには

+1

私から+1 - 'as'の後にヌルチェックが続き、' is'と 'as' –

1

:パフォーマンスのプロファイリングでは

私はas続いisを使用して、実際にはnullチェックが続く単一asより低いパフォーマンスをもたらしたことに気づきました。コンパイラが何かを自動的に最適化するとは思わないでください。メッセージングコード(またはパフォーマンスクリティカルセクション)では、スピードの設計が最も重要であると想定するのは間違いありません。

これまでのところ最も速いキャストは、よりも優れた、すなわちvar message = (SpecificType)baseMessageを上回る静的キャストです。これは、静的なキャストがあなたのケースであなたを助けることができないので、興味のあるポイントです。

2つの答えは、すでに仮想メソッド呼び出しを追加するだけで、設計パターンを使用して上記を多態的に実行することが最良の解決策であると言及しています。共通メソッドを抽象クラス(または共通メソッドシグネチャとインタフェース)に抽出することは、はるかに洗練されたソリューションです。仮想メソッドを呼び出すオーバーヘッドがありますが、これはsealedキーワードを使用して派生型に特定のメソッドをマーキングすることで軽減できます。

最終的にジェネリックメソッドを使用するのは、ランタイムキャストではなくコンパイル時の最適化です。

敬具、の

関連する問題