玩轉C#之【特性(attribute)】

閱讀時間約 1 分鐘

介紹

特性attribute,和注釋有什麼區別

第一個感受
特性:中括號宣告
錯覺:每一個特性都可以帶來對應的功能
實際上添加後,編譯器會在元素內部產生IL,但是我們是沒辦法直接使用的,而且在metadata會有紀錄
  • 特性會影響程式執行
  • 注釋不會影響程式執行
特性,本身是沒用的,程式執行的過程中,可以透過反射找到特性,在沒有破壞類型封裝前提下,可以加點額外的訊息和行為,任何一個可以生效的特性,都是因為有地方主動使用它的

特性的範例

//Api升級時,會在舊版本上面加上的特性,當編譯時會出現警告畫面
[Obsolete("請不要使用這個了,請使用什麼來代替",true)]//影響編譯器運行
//當Obsolete => true 編譯會直接報錯誤 false 編譯報警告

[Serializable]//可以序列化和反序列化 可以影響程式的運行
//MVC => filter ORM => table key display

實作一個特性

//定義:一個類別繼承Attribute 就是特性
//一般以Attribute結尾,宣告時可以省略掉
public class CustomAttribute:Attribute
{

}
[Custom]
public class Student
{
public int Id{get;set;}
public string Name{get;set;}
public void Study()
{
Console.WriteLine($"這裡是{this.Name}跟者老師學習");
}

public string Answer(string name)
{
return $"This is {name}";
}
}

宣告和使用attribute,AttributeUsage

AttributeTargets=>指定可以被哪個類型修飾
Inherited =true =>可不可以被繼承,默認是true
AllowMultiple =>多重修飾,默認是false,通常不推薦使用
[AttributeUsage(AttributeTargets.All,AllowMultiple = true)]=>讓宣告可以多重修飾某個元素
public class CustomAttribute:Attribute
{
public CustomAttribute()
{}

public CustomAttribute(int id)
{}

public string Description{get;set;}

public string Remark = null;

public void Show()
{
Console.WriteLine($"This is{nameof(CustomAttribute)}");
}
}
[Custom]   完全一樣的,表示都是使用無參數的建構子
[Custom()] 完全一樣的,表示都是使用無參數的建構子
[Custom(123)]帶參數的建構子
[Custom(123),Custom(123, Description ="123")] 多重修飾
[Custom(123, Description ="123",Remark ="2345")] //方法不行
public class Student
{
public int Id{get;set;}
public string Name{get;set;}
public void Study()
{
Console.WriteLine($"這裡是{this.Name}跟者老師學習");
}

[Custom] //方法加上特性
[return:Custom()] //給方法返回值加上特性
public string Answer([Custom] string name)//給參數也加上特性
{
return $"This is {name}";
}
}
使用反射找特性
public class Manager
{
public static void Show(Student student)
{
Type type =typeof(Student);//student.GetType();
if(type.IsDefined(typeof(CustomAttribute),true))//檢查有沒有 性能高
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute),true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}

PropertyInfo propertyinfo = type.GetProperty("id");
if(propertyinfo.IsDefined(typeof(CustomAttribute),true))
{
CustomAttribute attribute = (CustomAttribute)propertyinfo.GetCustomAttribute(typeof(CustomAttribute),true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}

MethodInfo method = type.GetMethod("Answer");
if(method.IsDefined(typeof(CustomAttribute),true))
{
CustomAttribute attribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute),true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}

ParameterInfo parameter = method.GetParameters()[0]
if(parameter.IsDefined(typeof(CustomAttribute),true))
{
CustomAttribute attribute = (CustomAttribute)parameter.GetCustomAttribute(typeof(CustomAttribute),true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}

ParameterInfo Returnparameter = method.ReturnParameter;
if(Returnparameter.IsDefined(typeof(CustomAttribute),true))
{
CustomAttribute attribute = (CustomAttribute)Returnparameter.GetCustomAttribute(typeof(CustomAttribute),true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}


student.Study();
string result = student.Answer("Apple");
}
}

應用範例,增加訊息

public enum UserState
{
//正常
Normal = 0,
//凍結
Frozen = 1,
//刪除
Deleted =2
}
一般使用的情況
UserState userState = UserState.Normal;
if( userState == UserState.Normal)
{
Console.WriteLine("正常狀態");
}else if(userState == UserState.Frozen)
{
Console.WriteLine("凍結");
}
使用Attribute
public class RemarkAttribute:Attribute
{
public RemarkAttribute(string remark)
{
this._Remark = remark;
}
private string _Remark = null;
public string GetRemark()
{
return this._Remark;
}
}
//在列舉上加上一個描述,實體類的屬性也可以 Display=>已經有的
//別名 映射
public enum UserState
{
//正常
[Remark("正常")]
Normal = 0,
//凍結
[Remark("凍結")]
Frozen = 1,
//刪除
[Remark("刪除")]
Deleted =2
}
查找
public static class RemarkExtension
{
public static string GetRemark(this Enum value)
{
Type type = value.GetType();
FieldInfo field = type.GetField(value.ToString());
if(field.IsDefined(typeof(RemarkAttribute),true))
{
RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute),true);
return attribute.GetRemark();
}else
{
return value.ToString();
}
}
}
UserState userState = UserState.Normal;
Console.WriteLine(userState.GetRemark());

應用範例,增加行為

public static class ValidateExtension
{
public static bool Validate(this object oObject)
{
Type type = oObject.GetType();
foreach(var prop in type.GetProperties())
{
if(prop.IsDefined(typeof(LongAttribute),true))
{
LongAttribute attribute = prop.GetCustomAttribute(typeof(LongAttribute),true);
if(!attribute.Validate(prop.GetValue(oObject)))
{
return false;
}
}
}
return true;
}
}
public class LongAttribute:Attribute
{
private long _Min =0;
private long _Max = 0;
public LongAttribute(long min,long max)
{
_Min = min;
_Max=max;
}

public bool Validate(object value)
{
if(value != null && string.IsNullOrWhiteSpace(value.ToString()))
{
if(long.TryParse(value.ToString(),out long lResult))
{
if(lResult > this._Min&& lResult <this._Max)
{
return true;
}
}
}
return false;

}
}
public class Student
{
public int Id{get;set;}
//可以做長度檢查的特性
public string Name{get;set;}
//範圍10001~999999999999
[LongAttribute(10001,999999999999)]
public long QQ{get;set;}
public void Study()
{
Console.WriteLine($"這裡是{this.Name}跟者老師學習");
}

[Custom] //方法加上特性
[return:Custom()] //給方法返回值加上特性
public string Answer([Custom] string name)//給參數也加上特性
{
return $"This is {name}";
}
}
public class Manager
{
public static void Show(Student student)
{
Type type =typeof(Student);//student.GetType();
if(type.IsDefined(typeof(CustomAttribute),true))//檢查有沒有 性能高
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute),true);
Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
attribute.Show();
}

//數值檢查 範圍10001~999999999999

//一般寫法
if(student.QQ > 10001 & sudent.QQ <999999999999)
{

}else
{

}
//使用Attribute
student.Validate();

student.Study();
string result = student.Answer("Apple");
}
}

參考資料

本篇已同步發表至個人部落格
https://moushih.com/2022ithome09/
鐵人賽文章
https://ithelp.ithome.com.tw/articles/10288837
為什麼會看到廣告
avatar-img
8會員
39內容數
我是這個部落格的作者,喜歡分享有關投資 💰、軟體開發 💻、占卜 🔮 和虛擬貨幣 🚀 的知識和經驗。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
一代軍師 的其他內容
介紹 反射:System.Reflection .Net框架提供的Library,可以讀取並使用metadata Dll-IL-Metadata-反射 透過VS編譯器 編譯成dll/exe 點擊exe的時候,他有一個依賴的環境叫做CLR IL:可以透過ILSpy(反編譯工具) 📷 一般情況
介紹 在C#2.0時代以前,是沒有泛型的,所以當我們遇到需求是方法內做相同的事情,但因為輸入或輸出的型別不一樣,我們就必須重複寫出類似的程式 以下的例子是=>不同的輸入型別 但卻做相同的事情 =>印出輸入資料 範例: 輸入參數:int或string或DateTime 功能:印出輸入的"數值"
介紹 分層架構模式,是將一個軟體系統進行分層,每個軟體系統都去要通過層來隔離不同的關注點,其中最為經典的就是三層架構以及領域驅動設計提出的四層架構。 📷 三層式架構 下面會介紹每一層專門要處理的事情 最常是用的分層方式 至於每層模組的命名方式,每間公司都不太ㄧ樣 📷 參考資料 鐵人賽文章
介紹 類似Windows排程的一個套件,不過他有Dashboard可以看 可以用在商業用途 使用情境 簡單來說如果你需要定時的執行某一段程式就可以使用這個套件來幫你完成。 優點 Simple 開發簡易、安裝簡單、方便部署 Persistent 工作任務可存放於多種儲存裝置 任務執行方式 版本 📷
Swagger是SmartBear Software的API開發人員套件工具,它是OpenAPI規範的基礎規範。 簡單來說就是 API 文件產生器。
在上一篇文章介紹過API之後,大家應該會很好奇寫好了一隻API應該怎麼測試巴?
介紹 反射:System.Reflection .Net框架提供的Library,可以讀取並使用metadata Dll-IL-Metadata-反射 透過VS編譯器 編譯成dll/exe 點擊exe的時候,他有一個依賴的環境叫做CLR IL:可以透過ILSpy(反編譯工具) 📷 一般情況
介紹 在C#2.0時代以前,是沒有泛型的,所以當我們遇到需求是方法內做相同的事情,但因為輸入或輸出的型別不一樣,我們就必須重複寫出類似的程式 以下的例子是=>不同的輸入型別 但卻做相同的事情 =>印出輸入資料 範例: 輸入參數:int或string或DateTime 功能:印出輸入的"數值"
介紹 分層架構模式,是將一個軟體系統進行分層,每個軟體系統都去要通過層來隔離不同的關注點,其中最為經典的就是三層架構以及領域驅動設計提出的四層架構。 📷 三層式架構 下面會介紹每一層專門要處理的事情 最常是用的分層方式 至於每層模組的命名方式,每間公司都不太ㄧ樣 📷 參考資料 鐵人賽文章
介紹 類似Windows排程的一個套件,不過他有Dashboard可以看 可以用在商業用途 使用情境 簡單來說如果你需要定時的執行某一段程式就可以使用這個套件來幫你完成。 優點 Simple 開發簡易、安裝簡單、方便部署 Persistent 工作任務可存放於多種儲存裝置 任務執行方式 版本 📷
Swagger是SmartBear Software的API開發人員套件工具,它是OpenAPI規範的基礎規範。 簡單來說就是 API 文件產生器。
在上一篇文章介紹過API之後,大家應該會很好奇寫好了一隻API應該怎麼測試巴?
你可能也想看
Google News 追蹤
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
※ class類別 什麼是class? class是創造consturctor function時的語法糖,本質上與使用function創造物件(object)的行為沒有不同。 class的作用: 用來定義、描述要創造的物件(object)具有那些屬性、行為的一個表達式。就像是「車子的設計圖
Thumbnail
內容涵蓋資料型別、型別轉換、自訂型別、元組型別、集合型別和字典型別等主題。文章首先詳述內建型別如bool、byte、char等的定義和使用,接著討論型別轉換,包括隱含轉換和明確轉換。之後文章介紹自訂型別的建立,以及元組、集合、陣列和字典型別的操作與例子。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
Thumbnail
C#是一種開源、跨平台、面向對象的編程語言,具有類型安全、泛型、模式匹配等特性。廣泛應用於桌面和Web應用程序、遊戲開發、移動應用、雲計算等領域。全球數十萬家公司像微軟、Unity Technologies、Stack Overflow等使用C#支持其業務。C#還提供豐富的進階學習資源和主題。
  在職涯諮詢現場與相關的討論經驗中,我們經常運用性格、興趣、能力三者之交叉分析,來定位適合個人發展的職涯進路,而除此之外,有一個很特別的是我們還經常使用「天賦」一詞,來解釋某種難以量化的個人優勢。
Thumbnail
關於程式語言的學習,只要掌握住幾個基本特性要熟悉幾種程式語言也不困難,這三個基本特性就是…
Thumbnail
在使用類別創建實例時,輸入的屬性的都要定義好資料型態,例如dog_1 = Dog("Buddy", 3),有沒有輸入一段字串讓他自己判斷的方法阿? 有的就是使用classmethod: classmethod 是一種裝飾器,它用於定義類別方法。類別方法與實例方法不同,它們被綁定到類別而不是實例。
上一篇文章提到有些介面不應被繼承,但物件導向的子類別只能繼承父類別的介面,因而產生一些不合適的介面實作。trait/typeclass則沒有這種繼承機制,這似乎使需要繼承的特性無法直接使用。然而函數式導向比起繼承,更適合使用組合,根本不需要使用繼承疊加特性。 資料類型的定義往往跟怎麼建構模型相
Thumbnail
本文將介紹自定函式及應用,利用程式範例解釋為什麼要用到自定函式 自定函式好處當然就是,讓你的程式碼看起來比較簡潔,在重複使用到的程式碼區塊,可以包裝成函式,讓你重複使用它。
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
※ class類別 什麼是class? class是創造consturctor function時的語法糖,本質上與使用function創造物件(object)的行為沒有不同。 class的作用: 用來定義、描述要創造的物件(object)具有那些屬性、行為的一個表達式。就像是「車子的設計圖
Thumbnail
內容涵蓋資料型別、型別轉換、自訂型別、元組型別、集合型別和字典型別等主題。文章首先詳述內建型別如bool、byte、char等的定義和使用,接著討論型別轉換,包括隱含轉換和明確轉換。之後文章介紹自訂型別的建立,以及元組、集合、陣列和字典型別的操作與例子。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
Thumbnail
C#是一種開源、跨平台、面向對象的編程語言,具有類型安全、泛型、模式匹配等特性。廣泛應用於桌面和Web應用程序、遊戲開發、移動應用、雲計算等領域。全球數十萬家公司像微軟、Unity Technologies、Stack Overflow等使用C#支持其業務。C#還提供豐富的進階學習資源和主題。
  在職涯諮詢現場與相關的討論經驗中,我們經常運用性格、興趣、能力三者之交叉分析,來定位適合個人發展的職涯進路,而除此之外,有一個很特別的是我們還經常使用「天賦」一詞,來解釋某種難以量化的個人優勢。
Thumbnail
關於程式語言的學習,只要掌握住幾個基本特性要熟悉幾種程式語言也不困難,這三個基本特性就是…
Thumbnail
在使用類別創建實例時,輸入的屬性的都要定義好資料型態,例如dog_1 = Dog("Buddy", 3),有沒有輸入一段字串讓他自己判斷的方法阿? 有的就是使用classmethod: classmethod 是一種裝飾器,它用於定義類別方法。類別方法與實例方法不同,它們被綁定到類別而不是實例。
上一篇文章提到有些介面不應被繼承,但物件導向的子類別只能繼承父類別的介面,因而產生一些不合適的介面實作。trait/typeclass則沒有這種繼承機制,這似乎使需要繼承的特性無法直接使用。然而函數式導向比起繼承,更適合使用組合,根本不需要使用繼承疊加特性。 資料類型的定義往往跟怎麼建構模型相
Thumbnail
本文將介紹自定函式及應用,利用程式範例解釋為什麼要用到自定函式 自定函式好處當然就是,讓你的程式碼看起來比較簡潔,在重複使用到的程式碼區塊,可以包裝成函式,讓你重複使用它。