iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
Software Development

玩轉C# 進階學習之旅系列 第 9

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

  • 分享至 

  • xImage
  •  

介紹

在程式中你看到上面有一個中括號[] 就是特性,它自身沒有任何功能。

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

第一個感受
特性:中括號宣告
錯覺:每一個特性都可以帶來對應的功能

實際上添加後,編譯器會在元素內部產生IL,但是我們是沒辦法直接使用的,而且在metadata會有紀錄

  • 特性會影響程式執行
  • 注釋不會影響程式執行

特性,本身是沒用的,程式執行的過程中,可以透過反射找到特性,在沒有破壞類型封裝前提下,可以加點額外的訊息和行為,任何一個可以生效的特性,都是因為有地方主動使用它的

特性的範例

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

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

Obsolete範例
Obsolete

實作一個特性

//定義:一個類別繼承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");
    }
}

參考資料

Attributes (C#)

本篇已同步發表至個人部落格
https://moushih.com/2022ithome09/


上一篇
玩轉C#之【反射 Reflection 射爆妳】
下一篇
玩轉C#之【物件導向】
系列文
玩轉C# 進階學習之旅31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言