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

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了究竟是什么可以比反射还快实现动态调用?| Source Generators版相关的知识,希望对你有一定的参考价值。

前言

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

就这一需求来说,我认为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.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“

以上是关于究竟是什么可以比反射还快实现动态调用?| Source Generators版的主要内容,如果未能解决你的问题,请参考以下文章

pypy真的能让python比c还快?

PHP FFI调用go,居然比go还快

.NET中同步异步多线程究竟是什么关系?

SpringMVC为啥是基于反射机制

Istio究竟是干嘛的?

关于虚方法