C# 将任何方法作为参数传递

C# Pass any method as parameter

我正在制作一个网络应用程序,我想在其中实施强类型 RPC。因此,我希望无论参数如何都能传递方法,这样我就可以获取它们并将它们存储在字典中,这样我就可以正确地构造请求参数,一旦数据包到达,我就可以使用参数读取它使用相同的远程方法

我想要这样的东西:

Register(Enum key, [method with unknown parameters])
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{

    public class Program
    {
        public static void Main(params string[] args)
        {
            var app = new NetworkingApplication();
            app.Register(PacketType.Type1, () =>
            {
                Console.WriteLine("Type1 Packet is received!");
            });
        }
    }

    public class NetworkingApplication
    {
        private readonly IDictionary<PacketType, Action> _registrations;

        public NetworkingApplication()
        {
            _registrations = new Dictionary<PacketType, Action>();
        }

        public void Register(PacketType packetType, Action method)
        {
            _registrations[packetType] = method;
        }

        //invoke this when the packet is received
        public void OnPacketReceived(PacketType type)
        {
            if (_registrations.TryGetValue(type, out var action))
            {
                action?.Invoke();
            }
        }
    }



    public enum PacketType
    {
        Type1,Type2,Type3
    }
}

上面提到可以使用MethodInfo,它属于System.Reflection命名空间。为此,首先像这样获取对象的 Type 类型:

var 类型 = obj.GetType()

之后你可以使用var methods = type.GetMethods()。这将为您提供 MethodInfo[]。使用您最喜欢的方法搜索元素。如Linq:

var method = methods.Where(it => it.Name == __yourName__).LastOrDefault();

*其中 您的姓名 是您的方法的名称。

现在您找到了想要的方法。使用

获取 parameters
var parameters = method.getParameters();

还有参数ParameterInfo[]。 从那里您可以使用 parameter.ParameterType 属性.

获取每个参数的类型

这就是说要非常小心反射,它非常非常强大,但如果过度使用会严重降低性能。

看看它 System.Reflection 命名空间 here .

您现在可以将方法添加到集合中,例如字典:

var dictionary = new Dictionary<int,MethodInfo>();
dictionary.Add(1, method);

然后像这样检索它:

var method = dictionary[1];

要调用该函数,您可以使用 method.Invoke() 并根据需要传入参数。

编辑: 如果您想通过网络发送参数和功能。您可以创建一个新的 class 作为 DTO 数据传输对象。 class 可以有一个参数数组 (ParameterInfo[])、MethodInfo 和您想要的任何其他内容。属性。

然后您可以序列化对象(可能 json)并将其发送到另一个系统,然后反序列化它,并调用 MethodInfo obj

在使用泛型和委托约束的 C# 8.0 中,您可以执行如下操作:

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
    }
}