Предварительно скомпилированное лямбда-выражение для создания класса с конструктором с параметром - PullRequest
1 голос
/ 13 марта 2019

В настоящее время я использую Activator.CreateInstance для создания экземпляра класса, тип которого передается в качестве универсального параметра. Проблема в том, что это очень медленно. Я где-то читал, что могу сделать то же самое с помощью предварительно скомпилированного лямбда-выражения, однако у меня возникают проблемы с реализацией этого в моем случае из-за того, что параметр необходимо передать экземпляру создаваемого класса.

В настоящее время я делаю следующее

public class Class1
{
    private int TestVariable;

    public Class1(int testVariable)
    {
        TestVariable = testVariable;
    }

    public void TestMethod()
    {
        Console.WriteLine($"Test Variable was {TestVariable}");
    }
}

public object Test<T>(params object[] parameters)
{
    var instance = (T) Activator.CreateInstance(typeof(T), BindingFlags.Instance, null, new object[] {9999}, null);

    var testMethod = typeof(T).GetMethod("TestMethod", BindingFlags.Instance);

    return testMethod.Invoke(instance, parameters)
}

Как можно сделать что-то похожее на это, используя предварительно скомпилированное лямбда-выражение?

1 Ответ

0 голосов
/ 13 марта 2019

На самом деле, использование Expression.Compile будет медленнее , если вам нужно создать только один объект. Однако, если вы кешируете функцию создателя, в конечном итоге она будет быстрее, так как лямбда-производительность будет хорошей.

Вот код:

static Func<TArg, T> CreateCreator<TArg, T>()
{
    var constructor = typeof(T).GetConstructor(new Type[] { typeof(TArg) });
    var parameter = Expression.Parameter(typeof(TArg), "p");
    var creatorExpression = Expression.Lambda<Func<TArg, T>>(
        Expression.New(constructor, new Expression[] { parameter }), parameter);
    return creatorExpression.Compile();
}
Func<TArg, T> creator = CreateCreator<TArg, T>();

Имея это, вы можете использовать creator(arg) для создания новых объектов T.


Небольшой эталон из трех способов:

class Program
{
    static void Main(string[] args)
    {
        // warm up
        Test1<Class1>(0);
        Test2<int, Class1>(0);

        const int numiter = 10000;
        var sw1 = Stopwatch.StartNew();
        for (int i = 0; i < numiter; i++)
            Test1<Class1>(i);
        sw1.Stop();
        Console.WriteLine($"With Activator.CreateInstance: " +
            $"{(double)sw1.ElapsedTicks / numiter} ticks per object");

        var sw2 = Stopwatch.StartNew();
        for (int i = 0; i < numiter; i++)
            Test2<int, Class1>(i);
        sw2.Stop();

        Console.WriteLine($"With Expression.Compile: " +
            $"{(double)sw2.ElapsedTicks / numiter} ticks per object");

        var sw3 = Stopwatch.StartNew();
        var creator = CreateCreator<int, Class1>();
        for (int i = 0; i < numiter; i++)
            creator(i);
        sw3.Stop();

        Console.WriteLine($"With cached Expression.Compile: " +
            $"{(double)sw3.ElapsedTicks / numiter} ticks per object");
    }

    static public object Test1<T>(params object[] parameters)
    {
        var instance = (T)Activator.CreateInstance(
            typeof(T), BindingFlags.Instance | BindingFlags.Public, null, parameters, null);
        return instance;
    }

    static Func<TArg, T> CreateCreator<TArg, T>()
    {
        var constructor = typeof(T).GetConstructor(new Type[] { typeof(TArg) });
        var parameter = Expression.Parameter(typeof(TArg), "p");
        var creatorExpression = Expression.Lambda<Func<TArg, T>>(
            Expression.New(constructor, new Expression[] { parameter }), parameter);
        return creatorExpression.Compile();
    }

    static public object Test2<TArg, T>(TArg arg)
    {
        var creator = CreateCreator<TArg, T>();
        return creator(arg);
    }
}

дает следующий результат на моем компьютере (режим выпуска / вне Visual Studio):

With Activator.CreateInstance: 3.0739 ticks per object
With Expression.Compile: 494.0388 ticks per object
With cached Expression.Compile: 0.1097 ticks per object
...