0
点赞
收藏
分享

微信扫一扫

究竟是什么可以比反射还快实现动态调用?| Source Generators版

前言

最近在公众号上看到一篇文章《究竟是什么可以比反射还快实现动态调用?》,它使用的是​​Newbe.ObjectVisitor​​,基于C#表达式树访问一个普通class的所有属性和对应的值,可以拥有比直接使用反射快上10倍的性能。

就这一需求来说,我认为​​Source Generators​​应该会更快,因为访问代码在编译时而不是运行时就生成了。

事实也验证了确实如此:

究竟是什么可以比反射还快实现动态调用?| Source Generators版_表达式树

实现

这次我们使用第三方开发的​​Source Generators​​类库来实现。

1.引用Nuget包

创建示例控制台程序,引用如下Nuget包:

AOPMethodsCommon
AOPMethodsGenerator

2.设置Attribute

在需要动态调用的类上声明Attribute:

[AutoMethods(template = TemplateMethod.CustomTemplateFile, CustomTemplateFileName = "template.txt")]
public class Yueluo

3.代码模板

添加​​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​​实现,可以达到更好的性能和可读性。


举报

相关推荐

0 条评论