我正在从二进制流解码通信消息。我根据到达的消息创建不同类型的消息对象。它们都源自基本
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 是否有任何相似之处。
您是否考虑过消除类型转换?
我猜您已经考虑过将虚拟方法放在
Message
类型本身上会破坏分层抽象(例如,您可能希望将消息处理与消息本身完全分离)。 也许可以考虑“访问者模式”。 这将允许您将 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(Message2 msg2);
}
此时,您已经消除了类型转换。 您现在可以将代码重写为
void ProcessIncomingMessage(CommsMessage msg)
{
new MyVisitor().Visit(msg);
}
class MyVisitor : CommsMessageVisitor
{
void Accept(Message1 msg1) { ProcessMessageType1(msg1); }
void Accept(Message2 msg2) { ProcessMessageType2(msg2); }
}
当然,可能有一些原因你不能这样做,但如果可以的话,避免类型转换总是更好!
void
,但无论如何。
嗯,我不太确定您展示的两种替代方案的性能差异。但是,至少 FxCop 会“建议”以下内容,而不是您的第一个解决方案:
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
}
当然,这里还涉及到其他问题,比如可维护性、可理解性等。
可能最好在“CommsMessage”类上提供一个“virtual void ProcessMessage()”,并为每个“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
。
补充上面的优秀答案:
is
后接
as
实际上会导致性能低于单个
as
后接空检查。不要指望编译器会自动优化任何东西。您的假设是正确的,在消息传递代码(或其他性能关键部分)中,速度设计至关重要。 到目前为止,最快的转换是静态转换,其性能优于
as
,即var message = (SpecificType)baseMessage
将优于
var message = baseMessage as SpecificType
。这只是一个有趣的点,因为静态转换无法在您的情况下帮助您。 正如两个答案已经提到的那样,使用设计模式以多态方式执行上述操作可能是最好的解决方案,因为它只添加了虚拟方法调用。将通用方法提取到抽象类(或将通用方法签名提取到接口)是迄今为止最优雅的解决方案。调用虚拟方法会产生开销,但这可以通过使用
sealed
关键字在派生类型上标记特定方法来减轻。
最后尽可能使用泛型来消除强制转换,因为泛型方法是编译时优化而不是运行时强制转换。
致以最诚挚的问候,