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

2022/09/09閱讀時間約 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
贊助創作者百元,就抽 $8,888 紅包與好禮!
一代軍師
一代軍師
程式語言、理財自我投資、加密貨幣、資訊安全 https://moushih.com/
如果要發表留言,請先登入註冊會員
享受沈浸的閱讀體驗
徜徉在不受干擾的簡約介面,瀏覽數百萬篇原創內容。
領取見面禮
只要設定追蹤作者,即可享有 48小時
Premium 閱讀權限