您的位置:首页 >聚焦 >

当前热议!C#中的 Attribute 与 Python/TypeScript 中的装饰器是同个东西吗

2022-06-27 05:30:43    来源:程序员客栈
前言

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

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

[HttpGet]publicResponseGet(){return...}


【资料图】

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

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

关于AOP

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

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

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

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

具体的区别

先来看看语法上的不同

Python的装饰器

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

defouter(func):definner():#...一些代码result=func()returnresultreturninner

使用的时候

@outerdeftest():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)]publicclassDemoAttribute:Attribute{publicDemoAttribute(stringparam1){this.param1=param1;}publicstringparam1{get;set;}}

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

[DemoAttribute("class")]publicclassDemoClass{[Demo("method")]publicvoidMethod1(){}}

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

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

因为特性只是单纯的装饰

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

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

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

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

varinfo=typeof(DemoClass);varattributes=info.GetCustomAttributes(true);

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

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

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

小结

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

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

参考资料C#的Attribute和Typescript的装饰器之比较:https://blog.csdn.net/weixin_43263355/article/details/110137016C#中如何实现类似Python中的装饰器:https://www.zhihu.com/question/36211661AOP面向切面编程:https://bbs.huaweicloud.com/blogs/289045

关键词: 高级会员 殊途同归 参考资料

相关阅读