Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding casting multiple times


I have a method which receives a parameter of base type and performs some pre-processing depending on actual parameter type.
Here is my code:


public void OnMessageReceived(QuickFix42.Message message)
{
    if (message is QuickFix42.ExecutionReport)
    {
        ProcessExecutionReport(message as QuickFix42.ExecutionReport);
    }
    else if (message is QuickFix42.AllocationACK)
    {
        ProcessAllocationAck(message as QuickFix42.AllocationACK);
    }
    else if (message is QuickFix42.OrderCancelReject)
    {
        ProcessOrderCancelReject(message as QuickFix42.OrderCancelReject);
    }
    // ...
}

Everything works fine, but I get the following warning from Visual Studio:

Warning 760 CA1800 : Microsoft.Performance : 'message', a parameter, is cast to type 'ExecutionReport' multiple times in method 'MessageProcessor.OnMessageReceived(Message)'. Cache the result of the 'as' operator or direct cast in order to eliminate the redundant isint instruction.

What's the best way to avoid these redundant casts?

like image 391
mr goose Avatar asked Oct 11 '10 10:10

mr goose


5 Answers

Given the repeating structure of your code you can clean it up in this way:

public void OnMessageReceived(QuickFix42.Message message)
{
    ExecuteOnlyAs<QuickFix42.ExecutionReport>(message, ProcessExecutionReport);
    ExecuteOnlyAs<QuickFix42.AllocationACK>(message, ProcessAllocationAck);
    ExecuteOnlyAs<QuickFix42.OrderCancelReject>(message, ProcessOrderCancelReject);
}

private void ExecuteOnlyAs<T>(QuickFix42.Message message, Action<T> action)
{
    var t = message as T;
    if (t != null)
    {
        action(t);
    }
}

This does assume that the types don't inherit from each other. If they do you'll need to change ExecuteOnlyAs to return a bool indicating success and do the if statements as before.

like image 28
Enigmativity Avatar answered Nov 08 '22 13:11

Enigmativity


Don't use both is and as. You just have to use as and check if the result is null :

QuickFix42.ExecutionReport execReport = message as QuickFix42.ExecutionReport
if (execReport != null)
{
  ProcessExecutionReport(execReport);
}
like image 185
Julien Hoarau Avatar answered Nov 08 '22 13:11

Julien Hoarau


As others have posted, a singleasfollowed by anull-test will be quite efficient. I do notice though, that this appears to be a QuickFix app. QuickFix provides a purpose-builtMessageCrackerclass, which I am sure is efficiently implemented, to 'crack' generic FIX messages into specific strongly-typed message objects, which it appears is precisely what you want to do. The benefits of doing it this way will be more than just (probably) improved performance; the code will be cleaner (less checks and casts, and the per-message handling code will be naturally moved to the appropriate handler) and more robust in the face of invalid messages.

EDIT: A look at the source of MessageCracker, as prompted by John Zwinck, puts paid to the idea that using this class will probably result in better performance.

Example: (make your class inherit fromMessageCracker)

public void OnMessageReceived(QuickFix42.Message message, SessionID sessionID)
{
  crack (message, sessionID);
}

public override void onMessage(QuickFix42.ExecutionReport message, SessionID sessionID)
{
   ...
}

public override void onMessage(QuickFix42.OrderCancelReject message, SessionID sessionID)
{
   ...
}
like image 5
Ani Avatar answered Nov 08 '22 12:11

Ani


QuickFix42.ExecutionReport executionReportMessage = message as QuickFix42.ExecutionReport;
if (executionReportMessage != null) 
{ 
  ProcessExecutionReport(executionReportMessage); 
} 
like image 2
vc 74 Avatar answered Nov 08 '22 14:11

vc 74


In my opinion I think you have something missing from your design. Typically I've seen something similar to this before, whereby this could happen:

public interface IResult
{
  // No members
}

public void DoSomething(IResult result)
{
  if (result is DoSomethingResult)
    ((DoSomethingResult)result).DoSomething();
  else if (result is DoSomethingElseResult)
    ((DoSomethingElseResult)result.DoSomethingElse();
}

Because the contract doesn't define any operations, it forces you to perform type casting to get any benefits from it. I don't think this is correct design. A contract (be it an interface or an abstract class) should define an intended operation, and it should be clear how to use it. Your example above is essentially the same issue, Message isn't defining the way in which it should be used... you should modify Message to enforce some operation:

public abstract class Message
{
  public abstract void Process();
}

public class ExecutionReportMessage : Message
{
  public override void Process()
  {
    // Do your specific work here.
  }
}

With that, you greatly simplify your implementation:

public void OnMessageReceived(QuickFix42.Message message)
{
    if (message == null)
      throw new ArgumentNullException("message");

    message.Process();
}

It's much cleaner, more testable, and no type casting.

like image 2
Matthew Abbott Avatar answered Nov 08 '22 14:11

Matthew Abbott