如何创建使用反射类型参数的闭包?定位.net 3.5
没有反思我会
void Main()
{
int i = 0;
Action<Foo> doSomething = (foo) => i += foo.GetNumber();
var myFoo = new Foo();
myFoo.UseFoo(doSomething);
Console.WriteLine(i);
}
class Foo
{
public int GetNumber() { return 4; }
public void UseFoo(Action<Foo> doSomething)
{
doSomething(this);
}
}
当Foo
是通过其他装配体反射获得的类型时,我该如何设置doSomething
?
void Main()
{
Type fooType = GetType("Foo");
int i = 0;
object doSomething = // ???;
var myFoo = Activator.CreateInstance(fooType);
fooType.GetMethod("UseFoo").Invoke(myFoo, new object[] { doSomething });
Console.WriteLine(i);
}
答案 0 :(得分:2)
我使用DocXcz和Nikola Anusev的答案来达到这个
static void Main()
{
var fooType = typeof(Foo); // get type via any method
int i = 0;
Action<object> doSomething = (foo) => i += (int)foo.GetType().GetMethod("GetNumber").Invoke(foo, null);
var typedDoSomething = (typeof(Program)).GetMethod("DelegateHelper").MakeGenericMethod(fooType).Invoke(null, new object[] { doSomething });
var myFoo = Activator.CreateInstance(fooType);
fooType.GetMethod("UseFoo").Invoke(myFoo, new object[] { typedDoSomething });
Console.WriteLine(i);
}
public static Action<T> DelegateHelper<T>(Action<object> generic)
{
return x => generic(x);
}
基本上我需要使用通用辅助方法来将Action<object>
转换为在运行时确定的Action<T>
答案 1 :(得分:1)
如果在编译时没有静态链接包含Foo类的程序集,那么你就不能在代码中使用Foo。
你必须在闭包中使用反射:
Action<object> doSomething =
(foo) => i+= (int)foo.GetType().GetMethod("GetNumber").Invoke(foo, null);
这假设Foo的方法UseFoo是这样的,只适用于.NET 4.
public void UseFoo(Action<Foo> action)
{
action(this);
}
当然,您应该将UseFoo的整个调用包含在try..catch块中,因为无法保证反射中的任何内容。
更新:这仅适用于.NET 4,其中Action可以作为UseFoo方法假定的Action传递。
答案 2 :(得分:1)
我确信在.NET 3.5上完全不能完全你想要的东西。
只有来自 close 的方法不会使用lambdas,而是使用普通的旧方法。基本上,您可以在类似于以下的类中定义您的操作:
public class FooActionMethods<TFoo>
{
public static void DoSomething(TFoo foo)
{
int i = 0;
int number = (int)typeof(TFoo).GetMethod("GetNumber").Invoke(foo, null);
Console.WriteLine(i + number);
}
}
然后,您可以调用,例如DoSomething
方法:
Type fooType = // somehow, we get the type from other assembly
object fooInstance = Activator.CreateInstance(fooType);
Type fooActionType = typeof(Action<>).MakeGenericType(fooType);
Type fooActionMethodsType = typeof(FooActionMethods<>).MakeGenericType(fooType);
Delegate action = Delegate.CreateDelegate(fooActionType, fooActionMethodsType, "DoSomething");
fooType.GetMethod("UseFoo").Invoke(fooInstance, new object[] { action });
由于整个FooActionMethod
类是通用的,我们可以使用反射(MakeGenericType
)并使用确切类型FooActionMethod
创建已关闭的Foo
。由于我们在编译时没有关于TFoo
的信息,因此只能通过反射与其实例进行交互。要简化这些交互,请查看处理某些库的SO question,这些库可以简化反射工作。
除此之外,我认为你无能为力。感谢您提出的好问题 - 制作出色的拼图! :)
正如旁注,我也试图通过使用表达式树动态创建lambda表达式来解决这个问题。我无法找到一种方法来包含表达式树的闭包。否则,该方法也可以正常工作。
答案 3 :(得分:0)
C#中的闭包是由编译器生成的。如果您想为反射类型生成相同的代码,则必须手动生成代码。
答案 4 :(得分:0)
如果foo实现了合适的接口,您可以在此接口上使用Action
。
如果没有,您需要在委托中使用反射(更简单)或使用表达式API在运行时编译委托(更多工作,最大性能)。
当C#编译器为i
创建闭包时,你需要继续这样做:
Action<int> incrementI = inc => i += inc;
现在,您可以在已编译的委托中使用增量i
来增加i
。