为了账号安全,请及时绑定邮箱和手机立即绑定

预编译的 lambda 表达式以创建具有带参数的构造函数的类

预编译的 lambda 表达式以创建具有带参数的构造函数的类

C#
DIEA 2022-10-23 15:32:16
我目前正在使用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 表达式完成类似的操作?
查看完整描述

1 回答

?
蛊毒传说

TA贡献1895条经验 获得超3个赞

实际上,如果您只需要创建一个对象,使用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


查看完整回答
反对 回复 2022-10-23
  • 1 回答
  • 0 关注
  • 79 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信