3

C#中的 Attribute 与 Python/TypeScript 中的装饰器是同个东西吗 - 程序设计实验室

 1 year ago
source link: https://www.cnblogs.com/deali/p/16413139.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近成功把「前端带师」带入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的方式。

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK