forked from connamara/quickfixn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMessageCracker.cs
85 lines (74 loc) · 3 KB
/
MessageCracker.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq.Expressions;
namespace QuickFix
{
/// <summary>
/// Helper class for delegating message types for various FIX versions to
/// type-safe OnMessage methods.
/// </summary>
public abstract class MessageCracker
{
private readonly Dictionary<Type, Action<Message, SessionID>> _callCache = new ();
protected MessageCracker()
{
Initialize(this);
}
private void Initialize(object messageHandler)
{
Type handlerType = messageHandler.GetType();
MethodInfo[] methods = handlerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach (MethodInfo m in methods)
{
TryBuildCallCache(m);
}
}
/// <summary>
/// build a complied expression tree - much faster than calling MethodInfo.Invoke
/// </summary>
/// <param name="m"></param>
private void TryBuildCallCache(MethodInfo m)
{
if (IsHandlerMethod(m))
{
var parameters = m.GetParameters();
var expParamMessage = parameters[0];
var expParamSessionId = parameters[1];
var messageParam = Expression.Parameter(typeof(Message), "message");
var sessionParam = Expression.Parameter(typeof(SessionID), "sessionID");
var instance = Expression.Constant(this);
var methodCall = Expression.Call(instance, m, Expression.Convert(messageParam, expParamMessage.ParameterType), Expression.Convert(sessionParam, expParamSessionId.ParameterType));
var action = Expression.Lambda<Action<Message, SessionID>>(methodCall, messageParam, sessionParam).Compile();
_callCache[expParamMessage.ParameterType] = action;
}
}
public static bool IsHandlerMethod(MethodInfo m)
{
ParameterInfo[] parameters;
return m.IsPublic
&& m.ReturnType == typeof(void)
&& m.Name.Equals("OnMessage")
&& (parameters = m.GetParameters()).Length == 2
&& parameters[0].ParameterType.IsSubclassOf(typeof(Message))
&& typeof(SessionID).IsAssignableFrom(parameters[1].ParameterType);
}
/// <summary>
/// Process ("crack") a FIX message and call the registered handlers for that type, if any
/// </summary>
/// <param name="message"></param>
/// <param name="sessionId"></param>
public void Crack(Message message, SessionID sessionId)
{
Type messageType = message.GetType();
if (_callCache.TryGetValue(messageType, out Action<Message, SessionID>? onMessage))
{
onMessage(message, sessionId);
}
else
{
throw new UnsupportedMessageType();
}
}
}
}