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

前言

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

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

事实也验证了确实如此:

2ab9a43b49583a63b0d2cd0b77eceb5f.png

实现

这次我们使用第三方开发的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.ObjectVisitorSource 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“

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值