C
C#2y ago
Dark

❔ Dictionary of delegates for non-static functions. (Task & Action)

I'm trying to write a dictionary for delegates that when specific values are passed so I can invoke the delegate. Currently code is written with a switch wanted todo with delegates though. EX: Socks 5 packet is received, It calls the packet handler, Packet handle will check if packet exists in dictionary and invoke the delegate associated with it. When this delegate is invoked I need it to know the object of the client so it's executing on correct client. So I need a way to pass what client/connection it is currently invoking this on, Non-static objects that are instantiated when a connection is made. I would like to do this without using static functions and passing objects as parameter. Sorry if this don't make sense not sure how to explain what I'm trying todo.
11 Replies
Dark
DarkOP2y ago
Also if any one has alternative methods I would love to know them as well! 🙂
Anton
Anton2y ago
do you mean remote procedure call?
Kouhai
Kouhai2y ago
@Dark You want to call the the Client's function instead of calling a static function delegate, right?
Sossenbinder
Sossenbinder2y ago
I feel like an example would really help here (What do you have right now etc., maybe some pseudo code of where you want to get to)
Dark
DarkOP2y ago
here is an example of kind of what I'm trying todo.
public class anythinggoes
{
static Dictionary<_MAP_LOGIN_PROTOCOL, Delegate> loginPacketDef = new Dictionary<_MAP_LOGIN_PROTOCOL, Delegate>()
{
{LOGIN_PROTOCOL.LOGIN, new Action<_rcmRequestGateLogin>(PacketHandler.OnLoginPacket)},

};
}
public class PacketHandler//Packet handling
{
private SocketClient connection;
PacketHandler handler = null;
public PacketHandler(SocketClient connection)
{
//Code
}
public void SendPacket(byte[] data)
{
//Code
}
public void OnLoginPacket(_rcmRequestGateLogin request)
{
//Code
}
}
public class anythinggoes
{
static Dictionary<_MAP_LOGIN_PROTOCOL, Delegate> loginPacketDef = new Dictionary<_MAP_LOGIN_PROTOCOL, Delegate>()
{
{LOGIN_PROTOCOL.LOGIN, new Action<_rcmRequestGateLogin>(PacketHandler.OnLoginPacket)},

};
}
public class PacketHandler//Packet handling
{
private SocketClient connection;
PacketHandler handler = null;
public PacketHandler(SocketClient connection)
{
//Code
}
public void SendPacket(byte[] data)
{
//Code
}
public void OnLoginPacket(_rcmRequestGateLogin request)
{
//Code
}
}
when doing this code, I should be able to invoke OnLoginPacket for the correct PacketHandler object. I want to call from a static Dictionary. I will be running the actual directory lookup from the PacketHandler to Invoke the code. I just need a way to write it so it's valid and to pass PacketHandler/this.
Kouhai
Kouhai2y ago
@Dark You can do
var handlers = BuildHandlers();
var packet = new PacketHandler(null);
handlers[Opcode._MAP_LOGIN_PROTOCOL].Invoke(packet, new _rcmRequestGateLogin());
handlers[Opcode._MAP_LOGOUT_PROTOCOL].Invoke(packet, null);

Dictionary<Opcode, Action<PacketHandler, object>> BuildHandlers()
{
var handlers = new Dictionary<Opcode, Action<PacketHandler, object>>();
foreach(var methodInfo in typeof(PacketHandler).GetMethods())
{
var handlerAttribute = methodInfo.GetCustomAttribute<HandlerAttribute>();
if (handlerAttribute == null)
continue;
var methodParamType = methodInfo.GetParameters()[0].ParameterType;
var thisParam = Expression.Parameter(typeof(PacketHandler));
var requestParam = Expression.Parameter(typeof(object));
var cast = Expression.Convert(requestParam, methodParamType);
var call = Expression.Call(thisParam, methodInfo, cast);
var lambda = Expression.Lambda<Action<PacketHandler, object>>(call, thisParam, requestParam);
var action = lambda.Compile();
handlers.Add(handlerAttribute.Opcode, action);
}
return handlers;
}

enum Opcode
{
_MAP_LOGIN_PROTOCOL,
_MAP_LOGOUT_PROTOCOL,
}

public class PacketHandler//Packet handling
{
private SocketClient connection;
PacketHandler handler = null;
public PacketHandler(SocketClient connection)
{

}
public void SendPacket(byte[] data)
{
//Code
}
[Handler(Opcode._MAP_LOGIN_PROTOCOL)]
public void OnLoginPacket(_rcmRequestGateLogin request)
{
Console.WriteLine("OnLoginPacket");
//Code
}

[Handler(Opcode._MAP_LOGOUT_PROTOCOL)]
public void OnLogoutPacket(object _)
{
Console.WriteLine("OnLogoutPacket");
}
}
class HandlerAttribute : Attribute
{
public Opcode Opcode { get; }

public HandlerAttribute(Opcode opcode)
{
Opcode = opcode;
}
}
var handlers = BuildHandlers();
var packet = new PacketHandler(null);
handlers[Opcode._MAP_LOGIN_PROTOCOL].Invoke(packet, new _rcmRequestGateLogin());
handlers[Opcode._MAP_LOGOUT_PROTOCOL].Invoke(packet, null);

Dictionary<Opcode, Action<PacketHandler, object>> BuildHandlers()
{
var handlers = new Dictionary<Opcode, Action<PacketHandler, object>>();
foreach(var methodInfo in typeof(PacketHandler).GetMethods())
{
var handlerAttribute = methodInfo.GetCustomAttribute<HandlerAttribute>();
if (handlerAttribute == null)
continue;
var methodParamType = methodInfo.GetParameters()[0].ParameterType;
var thisParam = Expression.Parameter(typeof(PacketHandler));
var requestParam = Expression.Parameter(typeof(object));
var cast = Expression.Convert(requestParam, methodParamType);
var call = Expression.Call(thisParam, methodInfo, cast);
var lambda = Expression.Lambda<Action<PacketHandler, object>>(call, thisParam, requestParam);
var action = lambda.Compile();
handlers.Add(handlerAttribute.Opcode, action);
}
return handlers;
}

enum Opcode
{
_MAP_LOGIN_PROTOCOL,
_MAP_LOGOUT_PROTOCOL,
}

public class PacketHandler//Packet handling
{
private SocketClient connection;
PacketHandler handler = null;
public PacketHandler(SocketClient connection)
{

}
public void SendPacket(byte[] data)
{
//Code
}
[Handler(Opcode._MAP_LOGIN_PROTOCOL)]
public void OnLoginPacket(_rcmRequestGateLogin request)
{
Console.WriteLine("OnLoginPacket");
//Code
}

[Handler(Opcode._MAP_LOGOUT_PROTOCOL)]
public void OnLogoutPacket(object _)
{
Console.WriteLine("OnLogoutPacket");
}
}
class HandlerAttribute : Attribute
{
public Opcode Opcode { get; }

public HandlerAttribute(Opcode opcode)
{
Opcode = opcode;
}
}
You would need to add Handler attribute on your handler methods
Anton
Anton2y ago
yes that's just RPC use a library
Dark
DarkOP2y ago
By looks of it if I'm understanding code correctly this is exactly what I needed, Thank you so much! I will let you know if it works for me later today 🙂 This code does work! Thank you @Kouhai, I realize now that I did miss some details I did have to modify some of this but I managed to make it work for Actions. I do have a question though. Is there a way to use this with Delegate instead of Action? I will have times when I'm trying todo a Func or Action because I have some that have return types.
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Kouhai
Kouhai2y ago
@Dark Sorry was away You can do something like this
public static class HandlersManager
{
public static Dictionary<Opcode, Func<PacketHandler, object, object>> returnHandlers = new();

public static T? Invoke<T>(Opcode opcode, PacketHandler packetHandler, object args) where T : class
{
return Invoke(opcode, packetHandler, args) as T;
}

public static object? Invoke(Opcode opcode, PacketHandler packetHandler, object args)
{
return returnHandlers.TryGetValue(opcode, out var handler) ? handler(packetHandler, args) : null;
}

public static void BuildHandlers()
{
var handlers = new Dictionary<Opcode, Action<PacketHandler, object>>();
foreach (var methodInfo in typeof(PacketHandler).GetMethods())
{
var handlerAttribute = methodInfo.GetCustomAttribute<HandlerAttribute>();
if (handlerAttribute == null)
continue;
var methodParamType = methodInfo.GetParameters()[0].ParameterType;
var thisParam = Expression.Parameter(typeof(PacketHandler));
var requestParam = Expression.Parameter(typeof(object));
var cast = Expression.Convert(requestParam, methodParamType);
var call = Expression.Call(thisParam, methodInfo, cast);

Func<PacketHandler, object, object>? func;

if (methodInfo.ReturnType != typeof(void))
{
func = Expression.Lambda<Func<PacketHandler, object, object>>(call, thisParam, requestParam).Compile();
}
else
{
func = Expression.Lambda<Func<PacketHandler, object, object>>(Expression.Block(call, Expression.Constant(null)), thisParam, requestParam).Compile();
}

returnHandlers.Add(handlerAttribute.Opcode, func);
}
}
}
public static class HandlersManager
{
public static Dictionary<Opcode, Func<PacketHandler, object, object>> returnHandlers = new();

public static T? Invoke<T>(Opcode opcode, PacketHandler packetHandler, object args) where T : class
{
return Invoke(opcode, packetHandler, args) as T;
}

public static object? Invoke(Opcode opcode, PacketHandler packetHandler, object args)
{
return returnHandlers.TryGetValue(opcode, out var handler) ? handler(packetHandler, args) : null;
}

public static void BuildHandlers()
{
var handlers = new Dictionary<Opcode, Action<PacketHandler, object>>();
foreach (var methodInfo in typeof(PacketHandler).GetMethods())
{
var handlerAttribute = methodInfo.GetCustomAttribute<HandlerAttribute>();
if (handlerAttribute == null)
continue;
var methodParamType = methodInfo.GetParameters()[0].ParameterType;
var thisParam = Expression.Parameter(typeof(PacketHandler));
var requestParam = Expression.Parameter(typeof(object));
var cast = Expression.Convert(requestParam, methodParamType);
var call = Expression.Call(thisParam, methodInfo, cast);

Func<PacketHandler, object, object>? func;

if (methodInfo.ReturnType != typeof(void))
{
func = Expression.Lambda<Func<PacketHandler, object, object>>(call, thisParam, requestParam).Compile();
}
else
{
func = Expression.Lambda<Func<PacketHandler, object, object>>(Expression.Block(call, Expression.Constant(null)), thisParam, requestParam).Compile();
}

returnHandlers.Add(handlerAttribute.Opcode, func);
}
}
}
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server