То, что выглядит очень похожим, можно сделать с помощью .NET
отражения и пользовательских атрибутов. Я думаю, что производительность будет хорошей для производственного использования, но не стоит, так как для этого все еще требуется переопределение WndProc
для вызова настраиваемого диспетчера, а после ввода WndProc
требуется одна строка кода для вызова настраиваемого диспетчера ИЛИ 3 строки кода для написания правильного оператора switch
. Если код вызывается из «базового» класса, который вы наследуете, он может стоить того.
На всякий случай, если вам интересно, я делаю это, потому что я запускаю C # + .NET и мне было любопытно, что можно сделать.
Вот как будет выглядеть код после выполнения «сантехники»:
public partial class Form1 : Form
{
private const int WM_MOUSEMOVE = 0x0200;
// This is the Delphi-lookalike declaration for the WM_MOUSEMOVE handler.
// I'd say it looks very much "alike!"
[WinMessageHandler(WM_MOUSEMOVE)]
public bool UnHandler(ref Message X)
{
this.Text = "Movement";
return false;
}
// While simple, this is unfortunately a deal-breaker. If you need to go through the
// trouble of writing this stub WndProc, might as well write a proper switch statement
// and call the handler directly.
protected override void WndProc(ref Message m)
{
if (!WinMessageDispatcher.Dispatch(this, ref m)) base.WndProc(ref m);
}
}
А вот и «сантехника». много больше кода, который реализует код для идентификации всех подпрограмм обработчика сообщений Windows (на основе пользовательского атрибута) и кеширует все эти результаты, используя пару словарей (поэтому тяжелую работу нужно выполнить только один раз ).
// Custom attribute to set message ID
class WinMessageHandler : System.Attribute
{
public int Msg;
public WinMessageHandler(int Msg) { this.Msg = Msg; }
}
class WinMessageDispatcher
{
// This is cached for the life of the application, it holds the required per-type
// dispatching information.
private class WinMessageDispatcher_PerType
{
private Dictionary<int, System.Reflection.MethodInfo> dict;
// generic handler
public bool HandleMessage(object OnInstance, ref Message msg)
{
System.Reflection.MethodInfo method;
if (dict.TryGetValue(msg.Msg, out method))
{
// Set up the call
object[] p = new object[1];
p[0] = msg;
return (bool)method.Invoke(OnInstance, p);
msg = p[0];
}
else
{
return false;
}
}
// Constructor, initializes the "dict"
public WinMessageDispatcher_PerType(Type t)
{
dict = new Dictionary<int, System.Reflection.MethodInfo>();
foreach (var method in t.GetMethods())
{
var attribs = method.GetCustomAttributes(typeof(WinMessageHandler), true);
if (attribs.Length > 0)
{
// Check return type
if (method.ReturnParameter.ParameterType != typeof(bool)) throw new Exception(string.Format("{0} doesn't return bool", method.Name));
// Check method parameters
var param = method.GetParameters();
if (param.Length != 1) throw new Exception(string.Format("{0} doesn't take 1 parameter", method.Name));
// Ooops! How do I check the TYPE of the "ref" parameter?
if (!param[0].ParameterType.IsByRef) throw new Exception(string.Format("{0} doesn't take a ref parameter of type System.Windows.Forms.Message but a parameter of type {1}", method.Name, param[0].ParameterType.ToString()));
// Add the method to the dictionary
dict.Add(((WinMessageHandler)attribs[0]).Msg, method);
}
}
}
}
// Dictionary to link "Types" to per-type cached implementations
private static Dictionary<Type, WinMessageDispatcher_PerType> dict;
// Static type initializer
static WinMessageDispatcher()
{
dict = new Dictionary<Type, WinMessageDispatcher_PerType>();
}
// Message dispatcher
public static bool Dispatch(object ObjInstance, ref Message msg)
{
if (ObjInstance == null) return false;
else
{
WinMessageDispatcher_PerType PerType;
lock (dict)
{
if (!dict.TryGetValue(ObjInstance.GetType(), out PerType))
{
PerType = new WinMessageDispatcher_PerType(ObjInstance.GetType());
dict.Add(ObjInstance.GetType(), PerType);
}
}
return PerType.HandleMessage(ObjInstance, ref msg);
}
}
}