2011-11-20 99 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); 
} 

就像一個魅力。 :)

相關問題