如何声明一个可以采用直到运行时才知道的枚举的委托?
How to declare a delegate that can take an enum which is not known until runtime?
我有一个客户端应用程序,它使用来自外部 dll 的 类(和枚举),该 dll 在运行时加载并反映。我知道我期望在 dll 中找到什么方法以及我期望它的枚举被调用什么。
我想创建一个可以在客户端应用程序中使用的委托,它是在运行时从反射方法创建的。当委托只有 "standard" 类型时,这种方法有效,但如果 dll 方法采用枚举,我如何才能使它起作用?我不能将委托中的枚举声明为 object
,因为它是一个值类型,尝试 Enum
或 int
似乎也不起作用。有没有解决的办法?非常感谢收到任何帮助!
// 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);
}
我有一个客户端应用程序,它使用来自外部 dll 的 类(和枚举),该 dll 在运行时加载并反映。我知道我期望在 dll 中找到什么方法以及我期望它的枚举被调用什么。
我想创建一个可以在客户端应用程序中使用的委托,它是在运行时从反射方法创建的。当委托只有 "standard" 类型时,这种方法有效,但如果 dll 方法采用枚举,我如何才能使它起作用?我不能将委托中的枚举声明为 object
,因为它是一个值类型,尝试 Enum
或 int
似乎也不起作用。有没有解决的办法?非常感谢收到任何帮助!
// 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);
}