<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在 .NET 中創(chuàng)建對象的幾種方式的對比

          共 6128字,需瀏覽 13分鐘

           ·

          2022-03-16 10:09

          在 .net 中,創(chuàng)建一個(gè)對象最簡單的方法是直接使用 new (), 在實(shí)際的項(xiàng)目中,我們可能還會用到反射的方法來創(chuàng)建對象,如果你看過?Microsoft.Extensions.DependencyInjection?的源碼,你會發(fā)現(xiàn),為了保證在不同場景中的兼容性和性能,內(nèi)部使用了多種反射機(jī)制。在本文中,我對比了常見的幾種反射的方法,介紹了它們分別應(yīng)該如何使用,每種的簡易度和靈活度,然后做了基準(zhǔn)測試,一起看看這之間的性能差距。

          我按照使用的簡易度和靈活度,做了下邊的排序,可能還有一些其他的反射方式,比如 Source Generators,本文中只針對以下幾種進(jìn)行測試。

          ?直接調(diào)用 ConstructorInfo 對象的Invoke()方法?使用 Activator.CreateInstance()?使用 Microsoft.Extensions.DependencyInjection?黑科技 Natasha?使用表達(dá)式 Expression?使用 Reflection.Emit 創(chuàng)建動態(tài)方法

          使用標(biāo)準(zhǔn)反射的 Invoke 方法

          Type typeToCreate = typeof(Employee);ConstructorInfo ctor = typeToCreate.GetConstructor(System.Type.EmptyTypes);Employee employee = ctor.Invoke(null) as Employee;

          第一步是通過 typeof() 獲取對象的類型,你也可以通過 GetType 的方式,然后調(diào)用 GetConstructor 方法,傳入 System.Type.EmptyTypes 參數(shù),實(shí)際上它是一個(gè)空數(shù)組 (new Type[0]), 返回 ConstructorInfo對象, 然后調(diào)用 Invoke 方法,會返回一個(gè) Employee 對象。

          這是使用反射的最簡單和最靈活的方法之一,因?yàn)榭梢允褂妙愃频姆椒▉碚{(diào)用對象的方法、接口和屬性等,但是這個(gè)也是最慢的反射方法之一。

          使用 Activator.CreateInstance

          如果你需要?jiǎng)?chuàng)建對象的話,在.NET Framework 和 .NET Core 中正好有一個(gè)專門為此設(shè)計(jì)的靜態(tài)類,System.Activator, 使用方法非常的簡單,還可以使用泛型,而且你還可以傳入其他的參數(shù)。

          Employee employee = Activator.CreateInstance();

          使用 Microsoft.Extensions.DependencyInjection

          接下來就是在.NET Core 中很熟悉的 IOC 容器,Microsoft.Extensions.DependencyInjection,把類型注冊到容器中后,然后我們使用 IServiceProvider 來獲取對象,這里我使用了 Transient 的生命周期,保證每次都會創(chuàng)建一個(gè)新的對象

          IServiceCollection services = new ServiceCollection();
          services.AddTransient();
          IServiceProvider provider = services.BuildServiceProvider();
          Employee employee = provider.GetService();

          Natasha

          Natasha 是基于 Roslyn 開發(fā)的動態(tài)程序集構(gòu)建庫,直觀和流暢的 Fluent API 設(shè)計(jì),通過 roslyn 的強(qiáng)大賦能, 可以在程序運(yùn)行時(shí)創(chuàng)建代碼,包括 程序集、類、結(jié)構(gòu)體、枚舉、接口、方法等, 用來增加新的功能和模塊,這里我們用 NInstance 來創(chuàng)建對象。

          // Natasha 初始化NatashaInitializer.Initialize();
          Employee employee = Natasha.CSharp.NInstance.Creator().Invoke();

          使用表達(dá)式 Expression

          表達(dá)式 Expression 其實(shí)也已經(jīng)存在很長時(shí)間了,在 System.Linq.Expressions 命名空間下, 并且是各種其他功能 (LINQ) 和庫(EF Core) 不可或缺的一部分,在許多方面,它類似于反射,因?yàn)樗鼈冊试S在運(yùn)行時(shí)操作代碼。

          NewExpression constructorExpression = Expression.New(typeof(Employee));Expression> lambdaExpression = Expression.Lambda>(constructorExpression);Func func = lambdaExpression.Compile();Employee employee = func();

          表達(dá)式提供了一種用于聲明式代碼的高級語言,前兩行創(chuàng)建了的表達(dá)式, 等價(jià)于 () => new Employee(),然后調(diào)用 Compile 方法得到一個(gè) Func<> 的委托,最后調(diào)用這個(gè) Func 返回一個(gè)Employee對象

          使用 Emit

          Emit 主要在 System.Reflection.Emit 命名空間下,這些方法允許我們在程序中直接創(chuàng)建 IL (中間代碼) 代碼,IL 代碼是指編譯器在編譯程序時(shí)輸出的 "偽匯編代碼", 也就是編譯后的dll,當(dāng)程序運(yùn)行的時(shí)候,.NET CLR 中的 JIT編譯器 將這些 IL 指令轉(zhuǎn)換為真正的匯編代碼。

          接下來,需要在運(yùn)行時(shí)創(chuàng)建一個(gè)新的方法,很簡單,沒有參數(shù),只是創(chuàng)建一個(gè)Employee對象然后直接返回

          Employee DynamicMethod(){    return new Employee();}

          這里主要使用到了 System.Reflection.Emit.DynamicMethod 動態(tài)創(chuàng)建方法

           DynamicMethod dynamic = new("DynamicMethod", typeof(Employee), null, typeof(ReflectionBenchmarks).Module, false);

          創(chuàng)建了一個(gè) DynamicMethod 對象,然后指定了方法名,返回值,方法的參數(shù)和所在的模塊,最后一個(gè)參數(shù) false 表示不跳過 JIT 可見性檢查。

          我們現(xiàn)在有了方法簽名,但是還沒有方法體,還需要填充方法體,這里需要C#代碼轉(zhuǎn)換成 IL代碼,實(shí)際上它是這樣的

          IL_0000: newobj instance void Employee::.ctor()IL_0005: ret

          你可以訪問這個(gè)站點(diǎn),它可以很方便的把C#轉(zhuǎn)換成IL代碼,https://sharplab.io/[1]

          然后使用 ILGenerator 來操作IL代碼, 然后創(chuàng)建一個(gè) Func<> 的委托, 最后執(zhí)行該委托返回一個(gè) Employee 對象

          ConstructorInfor ctor = typeToCreate.GetConstructor(System.Type.EmptyTypes);
          ILGenerator il = createHeadersMethod.GetILGenerator();il.Emit(OpCodes.Newobj, Ctor);il.Emit(OpCodes.Ret);
          Func emitActivator = dynamic.CreateDelegate(typeof(Func)) as Func;Employee employee = emitActivator();

          基準(zhǔn)測試

          上面我介紹了幾種創(chuàng)建對象的方式,現(xiàn)在我開始使用 BenchmarkDotNet 進(jìn)行基準(zhǔn)測試,我也把 new Employee() 直接創(chuàng)建的方式加到測試列表中,并用它作為 "基線",來并比較其他的每種方法,同時(shí)我把一些方法的預(yù)熱操作,放到了構(gòu)造函數(shù)中一次執(zhí)行,最終的代碼如下

          using BenchmarkDotNet.Attributes;using Microsoft.Extensions.DependencyInjection;using System;using System.Linq.Expressions;using System.Reflection;using System.Reflection.Emit;
          namespace ReflectionBenchConsoleApp{ public class Employee { }
          public class ReflectionBenchmarks { private readonly ConstructorInfo _ctor; private readonly IServiceProvider _provider; private readonly Func _expressionActivator; private readonly Func _emitActivator; private readonly Func _natashaActivator;

          public ReflectionBenchmarks() { _ctor = typeof(Employee).GetConstructor(Type.EmptyTypes);
          _provider = new ServiceCollection().AddTransient().BuildServiceProvider();
          NatashaInitializer.Initialize(); _natashaActivator = Natasha.CSharp.NInstance.Creator();

          _expressionActivator = Expression.Lambda>(Expression.New(typeof(Employee))).Compile();
          DynamicMethod dynamic = new("DynamicMethod", typeof(Employee), null, typeof(ReflectionBenchmarks).Module, false); ILGenerator il = dynamic.GetILGenerator(); il.Emit(OpCodes.Newobj, typeof(Employee).GetConstructor(System.Type.EmptyTypes)); il.Emit(OpCodes.Ret); _emitActivator = dynamic.CreateDelegate(typeof(Func)) as Func;
          }
          [Benchmark(Baseline = true)] public Employee UseNew() => new Employee();
          [Benchmark] public Employee UseReflection() => _ctor.Invoke(null) as Employee;
          [Benchmark] public Employee UseActivator() => Activator.CreateInstance();
          [Benchmark] public Employee UseDependencyInjection() => _provider.GetRequiredService();

          [Benchmark] public Employee UseNatasha() => _natashaActivator();

          [Benchmark] public Employee UseExpression() => _expressionActivator();

          [Benchmark] public Employee UseEmit() => _emitActivator();

          } }

          接下來,還修改 Program.cs,注意這里需要在 Release 模式下運(yùn)行測試

          using BenchmarkDotNet.Running; 
          namespace ReflectionBenchConsoleApp{ public class Program { public static void Main(string[] args) { var sumary = BenchmarkRunner.Run(); } }
          }

          測試結(jié)果

          這里的環(huán)境是 .NET 6 preview5, 使用標(biāo)準(zhǔn)反射的 Invoke() 方法雖然簡單,但它是最慢的一種,使用 Activator.CreateInstance() 和 Microsoft.Extensions.DependencyInjection() 的時(shí)間差不多,時(shí)間是直接 new 創(chuàng)建的16倍,使用表達(dá)式 Expression 表現(xiàn)最優(yōu)秀,Natasha 真是黑科技,比用Emit 還快了一點(diǎn),使用Emit 是直接 new 創(chuàng)建的時(shí)間的1.8倍。你應(yīng)該發(fā)現(xiàn)了各種方式之間的差距,但是需要注意的是這里是 ns 納秒,一納秒是一秒的十億分之一。


          這里簡單對比了幾種創(chuàng)建對象的方法,測試的結(jié)果也可能不是特別準(zhǔn)確,有興趣的還可以在 .net framework 上面進(jìn)行測試,希望對您有用!


          相關(guān)鏈接?

          https://andrewlock.net/benchmarking-4-reflection-methods-for-calling-a-constructor-in-dotnet/

          https://github.com/dotnetcore/Natasha


          瀏覽 57
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级片黄色电影免费 | 毛片在线观看网站 | 精品无码久久久久久久久 | 丁香五月婷婷网 | 欧美成手机在线 |