2022-09-09|閱讀時間 ‧ 約 19 分鐘

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

介紹

特性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
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.