• C#中的 Attribute 与 Python/TypeScript 中的装饰器是同个东西吗


    前言

    最近成功把「前端带师」带入C#的坑(实际是前端带师开始从cocos转unity游戏开发了)

    某天,「前端带师」看到这段代码后问了个问题:[这个是装饰器]?

    [HttpGet]
    public Response Get() {
        return ...
    }
    

    我第一反应觉得不是,这玩意在C#中叫“特性”(英文名Attribute,下文统称为特性),在Java中叫注解,虽然写法和Python/TypeScript中的差不多,但印象中实现方式应该是不同的。

    但咱学理工科的就是要严谨,不能仅凭经验和感觉,为此,我查了一下资料,看了之前杨旭大佬推荐的《C# in nutshell》这本书,不仅确认了这个问题的答案,也对Attribute有了更多了解。

    关于AOP

    “特性”、装饰器,其实都是设计模式中的装饰器模式,同时也是AOP思想。

    AOP是Aspect Oriented Programming,即面向切面编程。

    AOP把系统分解为不同的关注点,或者称之为切面(Aspect),是一种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想

    比如现在有一个网站,有购物、社交、游戏等多种功能且对所有用户开放,现在需要限制只有高级会员才能使用其中的几个功能,我们可以在每个模块加上if判断,但这样侵入性太强,且会造成大量重复代码;换成AOP的方法就是使用装饰器,在需要高级会员的地方加上限制就行~

    具体的区别

    先来看看语法上的不同

    Python的装饰器

    先来看看Python中的装饰器,在Python中函数是一等公民,装饰器也是个函数,其内部又内嵌了另一个函数

    def outer(func):
        def inner():
            # ... 一些代码
            result = func()
            return result
        return inner
    

    使用的时候

    @outer
    def test():
        print('test')
    

    使用时语法和Java的注解一样,以@开头

    其实这是个语法糖,实际的效果等同于

    outer(test)
    

    test函数作为参数传入给装饰器,之后这段代码的执行顺序如下:

    • def outer(func):装饰器定义
    • @outer:装饰器语法糖,直接直接执行outer函数,将test函数作为参数传入
    • outer:执行装饰器语法规则,将test函数替换成inner函数
    • inner:执行inner函数代码
    • test:根据inner中的这行代码:result = func(),执行test函数代码
    • 返回

    在Python这种动态语言中,实现装饰器模式确实是比静态语言容易的,被装饰的内容作为参数传入装饰器,装饰器可以直接访问到被装饰的内容进行一些处理。

    C#的“特性”

    C#中,“特性”是一个类,继承自Attribute类,然后可以包含任意你想要的属性字段

    AttributeUsage特性修饰,可以指定该特性可以修饰哪些代码元素

    [AttributeUsage(AttributeTargets.Class |
    AttributeTargets.Constructor |
    AttributeTargets.Field |
    AttributeTargets.Method |
    AttributeTargets.Property,
    AllowMultiple = true)]
    public class DemoAttribute : Attribute {
        public DemoAttribute(string param1) {
            this.param1 = param1;
        }
        
        public string param1 { get; set; }
    }
    

    构造方法中的参数,就是使用特性时传入的参数,比如这样:

    [DemoAttribute("class")]
    public class DemoClass {
        [Demo("method")]
        public void Method1() {
        }
    }
    

    PS:特性使用时可以省略后面的"Attribute",所以DemoAttributeDemo是同个东西

    这样写了之后并不会产生什么效果

    因为特性只是单纯的装饰

    在代码运行的时候,C#编译器先实例化DemoAttribute这个类,然后再实例化DemoClass这个类,且在DemoAttribute内是无法获取到被装饰的内容的。

    为了使装饰起效果,需要搭配使用反射~

    反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

    通过下面的代码可以获取到装饰在DemoClass上的特性

    var info = typeof(DemoClass);
    var attributes = info.GetCustomAttributes(true);
    

    通过下面的代码可以获取被装饰的方法,和装饰参数啥的

    foreach (var methodInfo in typeof(DemoClass).GetMethods()) {
        var attribute = (DemoAttribute) Attribute.GetCustomAttribute(methodInfo, typeof(DemoAttribute));
        if (attribute != null)
            Console.WriteLine("方法 {0} 被装饰,装饰参数 {1}", methodInfo.Name, attribute.param1);
    }
    

    获取到这些信息后,通过反射提供的其他功能再进行一些处理,也就实现了所谓的AOP

    小结

    所以,C#的特性和Python/TypeScript中的装饰器,虽然写法用法不一样,但殊途同归,要实现的目的确实是差不多的。

    但要说是同样的东西又不严谨,所以应该同样的东西,不过都是各自语言中实现AOP的方式。

    参考资料

  • 相关阅读:
    八个方便C#开发的省时的国外工具
    从babel-polyfill的一个坑而起
    Universal Link
    微信机器人
    微信手记
    Elasticsearch手记
    小游戏引擎手记
    【数学基础】3D数学基础-左右手坐标系
    【linux基础】linux误改sudoers权限之后的恢复及配置sudoers
    【c/c++基础】struct/typedef struct的用法详解总结
  • 原文地址:https://www.cnblogs.com/deali/p/16413139.html
Copyright © 2020-2023  润新知