Использование Reflection Invoke статический универсальный метод, передавая Lamba в качестве параметра - PullRequest
1 голос
/ 21 ноября 2011

Можно ли написать следующий код с помощью Reflection?

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

Где o :

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. */);

Ответы [ 3 ]

5 голосов
/ 22 ноября 2011

Да, можно делать то, что вы предлагаете, с помощью размышлений, однако это совершенно не нужно. Было бы проще определить статический метод самостоятельно так:

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);
1 голос
/ 23 ноября 2011

Наконец-то! :)

Сложность заключалась в том, что во время выполнения я не знаю типы, поэтому generics не является опцией (даже для метода private helper).

Сценарий должен быть в состоянии сделать это:

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);
}

Работает как Шарм. :)

1 голос
/ 21 ноября 2011

Если я правильно понял вопрос, то должно подойти следующее:

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)
    {
    }
}
...