程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

C#中的 Attribute 與 Python/TypeScript 中的裝飾器是同個東西嗎

編輯:Python

前言

最近成功把「前端帶師」帶入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#的Attribute和Typescript的裝飾器之比較:https://blog.csdn.net/weixin_43263355/article/details/110137016

  • C#中如何實現類似Python中的裝飾器:https://www.zhihu.com/question/36211661

  • AOP面向切面編程:https://bbs.huaweicloud.com/blogs/289045


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved