【问题标题】:Pre compiled lambda expression to create class that has a constructor with a parameter预编译的 lambda 表达式以创建具有带参数的构造函数的类
【发布时间】:2019-03-13 08:46:36
【问题描述】:

我目前正在使用Activator.CreateInstance 创建一个类的实例,其类型作为泛型参数传入。问题是,这非常慢。我在某处读到,我可以使用预编译的 lambda 表达式来做同样的事情,但是,由于需要将参数传递给正在创建的类的实例,因此在我的情况下我无法实现这一点。

目前我正在做以下事情

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

如何使用预编译的 lambda 表达式完成类似的操作?

【问题讨论】:

  • 真的不明白我的问题与该帖子的大部分内容有什么关系。我正在寻找可以处理参数的表达式 lambda 的具体帮助
  • Don't really see how my question is related to much of that post. 但您接受的答案与副本中显示的解决方案之一基本相同...
  • @mjwills:答案可能类似,但问题肯定不同。
  • 我们必须同意不同意@Vlad。

标签: c# lambda linq-expressions


【解决方案1】:

实际上,如果您只需要创建 一个 对象,使用Expression.Compile更慢。但是,如果您缓存创建者函数,从长远来看它会更快,因为 lambda 性能会很好。

代码如下:

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

【讨论】:

  • 是不是和stackoverflow.com/a/53959282/34092基本一样?
  • @mjwills:我没有深入研究整个代码,但是方法(创建并缓存创建者)似乎是一样的。
  • @mjwills:答案的主要目标是展示使用参数调用构造函数的语法。
  • 要通过这种方法创建不同的类型,是否需要为每种类型重复编译的 lambda 表达式?或者有一种方法可以通过以某种方式传递Type 参数来重用一个?
  • @mireazma:不,您需要重新创建创建者。除了使用(可能间接地)Activator.CreateInstance 之外,没有通用构造函数调用的可能性。
猜你喜欢
  • 1970-01-01
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多