2011-11-20 120 views
1

是否可以通过Reflection写下面的代码?使用反射调用传递Lamba作为参数的静态泛型方法

var fake = A.Fake<Foo>(
      o => o.WithArgumentsForConstructor(new[] { "Hello" })); 

Ø是:

Action<IFakeOptionsBuilder<T>> 

WithArgumentsForConstructor是:

IFakeOptionsBuilder<T> WithArgumentsForConstructor(IEnumerable<object> argumentsForConstructor); 

Foo类是:

class Foo 
{ 
    public Foo(string s) 
    { 
    } 
} 

我所做的是:

object fake = typeof(A) 
    .GetMethod("Fake", new Type[] { }) 
    .MakeGenericMethod(new[] { this.targetType }) 
    .Invoke(null, /* Here I need to pass the lambda. */); 
+0

“通过反射写下面的代码” - 你是指用Reflection.Emit还是什么? lambda通常首先编译的是一个表达式,您可能希望实际创建一个表达式,然后生成所需的IL代码。 – Lucero

+0

嗨,不是用Reflection.Emit。可以使用Emit,但我想用System.Reflection命名空间中定义的类型来完成它。 –

+0

那么,反射(不发射)不会产生任何代码,但正如我所说的表达式是你最亲密的朋友在这里。 – Lucero

回答

5

是,有可能通过反思来完成你的建议,然而这是不必要的。这将是简单的自己定义一个静态方法是这样的:

public static class MyClass 
{ 
    public static T CreateFakeWithArgumentsForConstructor<T>(object[] argumentsForConstructor) 
    { 
     return A.Fake<T>(x => x.WithArgumentsForConstructor(argumentsForConstructor)); 
    } 
} 

现在只需使用反射调用这个函数:

var method = typeof(MyClass).GetMethod("CreateFakeWithArgumentsForConstructor", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(new[] { theType }); 
method.Invoke(null, argumentsForConstructor); 
+0

太棒了!谢谢。:) –

1

如果我的问题理解正确的话,下面的应该是罚款:

Action<IFakeOptionsBuilder<Foo>> fakeOptionsBuilderAction = 
    o => o.WithArgumentsForConstructor(new[] { "", "" }); 

// You need the BindingFlags as Fake() is a static method: 
object fake = typeof(A) 
    .GetMethod("Fake", BindingFlags.Public | BindingFlags.Static) 
    .MakeGenericMethod(new[] { typeof(Foo) }) 
    .Invoke(null, new object[] { fakeOptionsBuilderAction }); 

随着Foo违抗为:

class Foo 
{ 
    public Foo(string one, string two) 
    { 
    } 
} 
+0

我在运行时不知道Foo。 :( –

+0

啊,好的 - 所以你不知道它具有什么构造函数... –

+0

我在运行时有ConstructorInfo和MethodInfo,我只需要通过反射传递lamdba表达式 –

1

终于来了! :)

困难的部分是,在运行时,我不知道类型,所以泛型不是一个选项(甚至没有私人助手方法)。

该方案是能够做到这一点:

var fake = new Fake<Foo>(o => o.WithArgumentsForConstructor("Hello")); 

这里是我得到了解决:

private IEnumerable<object> argumentsForConstructor; 

public object Invoke(IEnumerable<object> parameters) 
{ 
    this.argumentsForConstructor = parameters; 

    Type actionType = typeof(Action<>).MakeGenericType(
     typeof(IFakeOptionsBuilder<>).MakeGenericType(this.targetType)); 

    MethodInfo actionMethod = this.GetType() 
     .GetMethod("SetArgumentsForConstructor", BindingFlags.Instance | BindingFlags.NonPublic) 
     .MakeGenericMethod(new[] { this.targetType }); 

    Delegate action = Delegate.CreateDelegate(actionType, this, actionMethod); 

    Type fake = typeof(Fake<>).MakeGenericType(this.targetType); 
    ConstructorInfo ctor = (from ci in fake.GetConstructors(BindingFlags.Instance | BindingFlags.Public) 
          from pi in ci.GetParameters() 
          where pi.ParameterType == actionType 
          select ci).First(); 

    return ctor.Invoke(new[] { action }); 
} 

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by Reflection. It describes the method that is passed in the Action<IFakeOptionsBuilder<T>> overload of the Fake<T> constructor.")] 
private void SetArgumentsForConstructor<T>(IFakeOptionsBuilder<T> o) 
{ 
    if (typeof(T).IsInterface) 
    { 
     return; 
    } 

    o.WithArgumentsForConstructor(this.argumentsForConstructor); 
} 

就像一个魅力。 :)

相关问题