如何声明一个可以采用直到运行时才知道的枚举的委托?

How to declare a delegate that can take an enum which is not known until runtime?

我有一个客户端应用程序,它使用来自外部 dll 的 类(和枚举),该 dll 在运行时加载并反映。我知道我期望在 dll 中找到什么方法以及我期望它的枚举被调用什么。

我想创建一个可以在客户端应用程序中使用的委托,它是在运行时从反射方法创建的。当委托只有 "standard" 类型时,这种方法有效,但如果 dll 方法采用枚举,我如何才能使它起作用?我不能将委托中的枚举声明为 object,因为它是一个值类型,尝试 Enumint 似乎也不起作用。有没有解决的办法?非常感谢收到任何帮助!

// e.g. external code
namespace test2 
{
    public static class test2
    {
        public static int calc(int a, int b, testEnum c)
        {
            if (c == testEnum.add) return a + b;
            else return a - b;
        }

        public static int add(int a, int b)
        {
            return a + b;
        }
    }

    public enum testEnum
    {
        add, subtract
    }
}

// my client code
namespace test1
{
    public class TestClient
    {
        private static Assembly _assembly;

        public static void SetUp()
        {
            const string externalDll = ".../test2.dll";

            Assembly assembly = Assembly.LoadFrom(externalDll);
            AppDomain.CurrentDomain.Load(assembly.GetName());
            _assembly = assembly;
        }

        private delegate int _add(int a, int b);
        private _add add;

        private delegate int _calc(int a, int b, ??? c); // nothing works here
        private _calc calc;

        public void Run()
        {
            SetUp();
            add = GetExpectedFunction<_add>("add");
            int three = add(1, 2); // OK

            calc = GetExpectedFunction<_calc>("calc"); // not OK

            // intended usage
            var reflectedEnum = ReflectMe("testEnum", "add");
            int threeAgain = calc(1, 2, reflectedEnum); 
        }

        public static T GetExpectedFunction<T>(string functionName) where T : class
        {
            try
            {
                if (!typeof(T).IsSubclassOf(typeof(Delegate))) throw new ApplicationException("GetExpectedFunction must return a delegate!");
                var foundMethod = _assembly.GetType("test2.test2").GetMethod(functionName, BindingFlags.Public | BindingFlags.Static);
                return (T)(object)Delegate.CreateDelegate(typeof(T), foundMethod);  
            }
            catch (Exception e)
            {
                // "Error binding to target method!"
            }
        }
    }
}

有一个解决方案,你必须创建伪造的枚举(如果你创建准确的枚举会更好),然后你将像这样作为整数传递:

   private delegate int _add(int a, int b);
    private _add add;

    private delegate int _calc(int a, int b, FakedEnum c); // faked enum here
    private _calc calc;

    public enum FakedEnum
    {

    }

    public void Run()
    {
        SetUp();
        add = GetExpectedFunction<_add>("add");
        int three = add(1, 2); // OK

        calc = GetExpectedFunction<_calc>("calc"); // it will be ok
    var result=    calc(4, 6, (FakedEnum)0);
        // intended usage
       // var reflectedEnum = ReflectMe("testEnum", "add");
        //int threeAgain = calc(1, 2, reflectedEnum);
    }

使用动态关键字声明您的委托参数:

private delegate int _calc(int a, int b, dynamic c);

您可以将 object 类型的委托绑定到采用 enum 的方法,方法是在运行时创建一个使用 LINQ Expression 的动态方法调用,并为类型不匹配的参数:

public static T GetExpectedFunction<T>(string functionName) where T : class {
    try {
        if (!typeof(T).IsSubclassOf(typeof(Delegate))) throw new ApplicationException("GetExpectedFunction must return a delegate!");
        var foundMethod = Type.GetType("test2.test2").GetMethod(functionName, BindingFlags.Public | BindingFlags.Static);
        var inv = typeof(T).GetMethod("Invoke");
        var parameters = inv.GetParameters().Zip(foundMethod.GetParameters(), (a, b) => new {
            PassedIn = a.ParameterType
        ,   Reflected = b.ParameterType
        ,   Parameter = Expression.Parameter(a.ParameterType)
        }).ToList();
        if (parameters.All(p => p.PassedIn == p.Reflected)) {
            // Bind directly
            return (T)(object)Delegate.CreateDelegate(typeof(T), foundMethod);  
        }
        var call = Expression.Call(foundMethod, parameters.Select(
            p => p.PassedIn==p.Reflected
        ?   (Expression)p.Parameter
        :   Expression.Convert(p.Parameter, p.Reflected)
        ));
        return (T) (object) Expression.Lambda(typeof(T), call, parameters.Select(p => p.Parameter)).Compile();
    } catch (Exception e) {
        // "Error binding to target method!"
        return null;
    }
}

此实现将来自反射方法和委托方法的类型配对(参见 parameters 变量),并为来自委托的类型创建 ParameterExpression 对象。然后它检查所有参数类型是否匹配(parameters.All(...) 部分)。这是对不需要转换的情况的优化。

如果至少需要一次转换,代码会创建一个方法调用,将原始参数表达式替换为类型不匹配的转换表达式,创建请求的委托类型的 lambda,编译它,然后 returns 它给调用者。

对于您的代码,此动态方法如下所示:

int dynamic_method(int a, int b, object c) {
    return test2.test2(a, b, (testEnum)c);
}