究竟是什么可以比反射还快实现动态调用?| Source Generators版
前言
最近在公眾號上看到一篇文章《究竟是什么可以比反射還快實現動態調用?》,它使用的是Newbe.ObjectVisitor,基于C#表達式樹訪問一個普通class的所有屬性和對應的值,可以擁有比直接使用反射快上10倍的性能。
就這一需求來說,我認為Source Generators應該會更快,因為訪問代碼在編譯時而不是運行時就生成了。
事實也驗證了確實如此:
實現
這次我們使用第三方開發的Source Generators類庫來實現。
1.引用Nuget包
創建示例控制臺程序,引用如下Nuget包:
AOPMethodsCommon AOPMethodsGenerator2.設置Attribute
在需要動態調用的類上聲明Attribute:
[AutoMethods(template?=?TemplateMethod.CustomTemplateFile,?CustomTemplateFileName?=?"template.txt")] public?class?Yueluo3.代碼模板
添加template.txt,用在Source Generators生成動態調用代碼的模板.
內容如下:
using?System; using?System.Collections.Generic; using?System.CodeDom.Compiler; using?System.Runtime.CompilerServices; namespace?{{NamespaceName}}?{public?static?class?{{ClassName}}Extentions{public?static?string?ValueStringProperty(this?{{ClassName}}?obj,?string?val){{{~?for?mi?in?Properties?~}}{{~?if(?mi.ReturnType??==?"string"?)?~}}if(string.Compare("{{mi.Name}}",val,StringComparison.CurrentCultureIgnoreCase)==0)?{return?obj.{{mi.Name}};}{{~?end?~}}{{~?end?~}}throw?new?ArgumentException("cannot?find?"+?val);}public?static?int?ValueIntProperty(this?{{ClassName}}?obj,?string?val){{{~?for?mi?in?Properties?~}}{{~?if(?mi.ReturnType??==?"int"?)?~}}if(string.Compare("{{mi.Name}}",val,StringComparison.CurrentCultureIgnoreCase)==0)?{return?obj.{{mi.Name}};}{{~?end?~}}{{~?end?~}}throw?new?ArgumentException("cannot?find?"+?val);}public?static?object?ValueProperty(this?{{ClassName}}?obj,?string?val){{{~?for?mi?in?Properties?~}}if(string.Compare("{{mi.Name}}",val,StringComparison.CurrentCultureIgnoreCase)==0)?{return?obj.{{mi.Name}};}{{~?end?~}}throw?new?ArgumentException("cannot?find?"+?val);}} }模板使用了scriban進行解析,具體語法詳見:https://github.com/scriban/scriban/blob/master/doc/language.md
以ValueStringProperty方法舉例來說:
public?static?string?ValueStringProperty(this?{{ClassName}}?obj,?string?val){{{~?for?mi?in?Properties?~}}{{~?if(?mi.ReturnType??==?"string"?)?~}}if(string.Compare("{{mi.Name}}",val,StringComparison.CurrentCultureIgnoreCase)==0)?{return?obj.{{mi.Name}};}{{~?end?~}}{{~?end?~}}throw?new?ArgumentException("cannot?find?"+?val); }遍歷類的所有屬性(Properties),判斷當前屬性返回類型(mi.ReturnType)是string,則返回對應屬性名的值。
4.使用
下面是Benchmark測試代碼,分別使用了Newbe.ObjectVisitor和Source Generators:
[Benchmark] public?string?GetterString()=>?ValueGetter<Yueluo,?string,?string>.GetGetter(_nameProperty).Invoke(_yueluo);[Benchmark] public?int?GetterInt()=>?ValueGetter<Yueluo,?int,?int>.GetGetter(_ageProperty).Invoke(_yueluo);[Benchmark] public?string?GetterString2() {?return?_yueluo.ValueStringProperty("Name");? }[Benchmark] public?int?GetterInt2() {return??_yueluo.ValueIntProperty("Age"); }可以看到,Source Generators生成的代碼可讀性更高。
結論
對于編譯時可生成的功能,盡量使用Source Generators實現,可以達到更好的性能和可讀性。
如果你覺得這篇文章對你有所啟發,請關注我的個人公眾號”My IO“
總結
以上是生活随笔為你收集整理的究竟是什么可以比反射还快实现动态调用?| Source Generators版的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 客户要求ASP.NET Core API
- 下一篇: 如何使用 Linq 获取每个分组中的第一