Search code examples
c#network-programmingreflectionrpc

C# Pass any method as parameter


I'm making a networking application where I want to implement strongly typed RPC. As result I'd like to be able to pass methods no matter the parameters so I can get them and store them in a dictionary so I can construct request parameters properly as well once a packet arrive I am able to read it using the parameters the same remote method used

I'd like something like this:

Register(Enum key, [method with unknown parameters])

Solution

  • In C# 8.0 using generics and Delegate Constraint you can do something like this:

    using System;
    using System.Collections.Generic;
    
    namespace ConsoleApp
    {
    
        public class Program
        {
            public static void Main(params string[] args)
            {
                var app = new NetworkingApplication();
                app.Register<Action<int, string>>(PacketType.Type1, Method1, 1, "string1 argument");
                app.Register<Func<string, string>>(PacketType.Type2, Method2, "string2 argument");
                app.OnPacketReceived(PacketType.Type1);
                app.OnPacketReceived(PacketType.Type2);
            }
    
            public static void Method1(int arg1, string arg2)
            {
                Console.WriteLine($"Method1 Invoked with args: {arg1}, {arg2}");
            }
    
            public static string Method2(string arg1)
            {
                Console.WriteLine($"Method2 Invoked with args: {arg1}");
                return "Foo";
            }
        }
    
        public class NetworkingApplication
        {
            private readonly IDictionary<PacketType, DelegateInvoker> _registrations;
    
            public NetworkingApplication()
            {
                _registrations = new Dictionary<PacketType, DelegateInvoker>();
            }
    
            public void Register<TDelegate>(PacketType packetType, TDelegate @delegate, params object[] args)
                where TDelegate : Delegate
            {
                _registrations[packetType] = new DelegateInvoker(@delegate, args);
            }
    
            //invoke this when the packet is received
            public void OnPacketReceived(PacketType type)
            {
                if (_registrations.TryGetValue(type, out var invoker))
                {
                    invoker.Invoke();
                }
            }
    
            private class DelegateInvoker
            {
                public DelegateInvoker(Delegate @delegate, object[] args)
                {
                    Delegate = @delegate;
                    Arguments = args;
                }
    
                private Delegate Delegate { get; }
                private object[] Arguments { get; }
    
                public void Invoke()
                {
                    Delegate.Method.Invoke(Delegate.Target, Arguments);
                }
            }
        }
    
    
    
    
    
        public enum PacketType
        {
            Type1,
            Type2,
            Type3
        }
    }