03.特性Attribute


1. 基本了解

1.1 簡述說明

特性(Attribute)本質上是一個類,此類需要直接或間接繼承 Attribute 類,特性為目標元素(比如類、方法、結構、枚舉、組件等)提供關聯附加信息,並在運行期以反射的方式來獲取附加信息

說明:特性類的實例里沒有驗證邏輯,只有驗證用到的規范數據(比如字符串長度)、提示信息等,而驗證邏輯需要自己寫

.Net 框架提供了兩種類型的特性:預定義特性和自定義特性

1.2 特性應用

添加額外信息

可以使用特性附加需要的信息,例如:字段的中文名,實體字段對應數據表字段的名稱

示例:Table 特性,指定表名,Route 指定路由路徑

其它功能

例如,信息的效驗,功能標識

示例:值的長度效驗,類型效驗等(Model驗證就是很好的例子)

2. 特性限定

2.1 AttributeUsage 限定

AttributeUsageAttributeAttribute,用戶指定特性使用限制,常用的有:AttributeTargetsAllowMultiple

2.2 AttributeTargets 目標限定

使用 AttributeTargets 表示指定Attribute限制用於哪類實體上,在這里,實體是指: classmethodconstructorfieldpropertyGenericParameter或者用All,表明可用於所有實體

每個target標記可以用|鏈接(組合),如AttributeTargets.Class|AttributeTargets.Method表示可用於class或者method上,以此為例

示例:無限定

[AttributeUsage(AttributeTargets.All)]
public class AllTargetsAttribute : Attribute {}

示例:限定只能標記在類上

[AttributeUsage(AttributeTargets.Class)]
public class ClassTargetAttribute : Attribute {}

示例:限定只能標記在方法上

[AttributeUsage(AttributeTargets.Method)]
public class MethodTargetAttribute : Attribute {}

[AttributeUsage(AttributeTargets.Method,AllowMultiple = true)]
public class MethodTargetAttribute : Attribute {}

示例:限定只能標記在構造函數上

[AttributeUsage(AttributeTargets.Constructor)]
public class ConstructorTargetAttribute : Attribute {}

示例:限定只能標記在字段上

[AttributeUsage(AttributeTargets.Field)]
public class FieldTargetAttribute : Attribute {}

示例:限定只能標記在泛型類型參數上

[AttributeUsage(AttributeTargets.GenericParameter)]
public class GenericParameterTargetAttribute : Attribute {}

示例:限定標記在類與方法上

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MethodAndClassTargetAttribute : Attribute {}

2.2 AllowMultiple 重復限定

使用 AllowMultiple 表示是否可以多次標記在同一目標上,不指定默認 true

[AttributeUsage(AttributeTargets.Method,AllowMultiple = true)]
public class CustomAttribute : Attribute {}

3. 自定義特性

3.1 自定義步驟

  • 聲明自定義特性,創建類
  • 構建自定義特性,寫邏輯,功能
  • 在目標程序元素上應用自定義特性
  • 通過反射訪問特性,調用特性邏輯,功能

3.2 定義特性

基本定義

一個新的自定義特性應派生(繼承)自 System.Attribute

public class CustomAttribute : Attribute
{
	...
}

帶構造函數定義,類中默認有個無參構造方法

public class CustomAttribute : Attribute
{
    public CustomAttribute()
    {
        Console.WriteLine("調用子類無參構造函數");
    }
    public CustomAttribute(string text)
    {
        Console.WriteLine("調用子類有參構造函數:"+text);
    }
}

帶屬性特性定義

public class CustomAttribute : Attribute
{
    public int index { get; set; }
}

帶字段特性定義

public class CustomAttribute : Attribute
{
    public string name;
}

3.3 標記使用

標記在類上

[CustomAttribute]
public class Studen
{
    ...
}

標記在方法上

public class Studen
{
    [CustomAttribute]
    public void Show() { }
}

標記在屬性上

public class Studen
{
    [CustomAttribute]
    public int id { get; set; }
}

標記在字段上

public class Studen
{
    [CustomAttribute]
    public int no;
}

標記在構造函數上

public class Studen
{
    [CustomAttribute]
    public Studen()
    {

    }
}

標記在方法返回參數上

public class Studen
{
    [return:CustomAttribute]	// 多個特性逗號隔開
    public void Show()
    {

    }
}

4. 綜合示例

4.1 定義特性

定義驗證特性,使用抽象(類)特性實現擴展,實現邏輯在Validate方法中

public abstract class AbstractValidateAttribute : Attribute
{
    public abstract bool Validate(object oValue);
}

// 驗證值長度
public class LengthAttribute : AbstractValidateAttribute
{
    public long Max { get; set; }
    public long Min { get; set; }
    public override bool Validate(object oValue)
    {
        return oValue != null && long.TryParse(oValue.ToString().Length.ToString(), out long lValue) 
            && lValue >= Min && lValue <= Max;
    }
}

// 驗證非空
public class NullAttribute : AbstractValidateAttribute
{
    public override bool Validate(object oValue)
    {
        return oValue != null;
    }
}

4.2 使用特性

public class Studen
{
    [NullAttribute]
    [LengthAttribute(Max =10,Min =5)]
    public string name { get; set; }
}

4.3 調用特性

缺陷:需要手動調用擴展方法,且擴展方法沒有限制,功能單一(只能用於驗證)

public static class AttributeExtend
{
    public static bool Validate<T>(this T t) where T : class
    {
        Type type = t.GetType();
        foreach (var prop in type.GetProperties())
        { 
            // 這里先判斷,是為了提高性能
            if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
            {
                object ovale = prop.GetValue(t);
                // 獲取特性的實例,上面先判斷之后,再獲取實例
                foreach (AbstractValidateAttribute attribute 
                    in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
                {
                    if (!attribute.Validate(ovale))
                    {
                        return false;
                    }
                }
            }
        }
        return true;
    }
}

4.4 測試使用

static void Main(string[] args)
{
    Studen stu = new Studen
    {
        name = "起舞在人間"
    };

    Console.WriteLine(stu.Validate());
}

5. 擴展補充

特性編譯后內容

通過反編譯工具得知,標記特性的元素,最終會在元素內部生成.custom的元素

.class public auto ansi beforefieldinit ConsoleApp2.Studen
	extends [mscorlib]System.Object
{
	// Methods
	.method public hidebysig 
		instance void Show () cil managed 
	{
		.custom instance void ConsoleApp2.CustomAttribute::.ctor() = (
			01 00 00 00
		)
		// Method begins at RVA 0x2085
		// Code size 2 (0x2)
		.maxstack 8

		IL_0000: nop
		IL_0001: ret
	} // end of method Studen::Show

	.method public hidebysig specialname rtspecialname 
		instance void .ctor () cil managed 
	{
		// Method begins at RVA 0x2088
		// Code size 8 (0x8)
		.maxstack 8

		IL_0000: ldarg.0
		IL_0001: call instance void [mscorlib]System.Object::.ctor()
		IL_0006: nop
		IL_0007: ret
	} // end of method Studen::.ctor

} // end of class ConsoleApp2.Studen

特性,內部屬性

在特性中聲明屬性,且此屬性只能用於外部訪問,內部賦值

public int text { get; private set; }

特性應使用構造函數賦值還是使用屬性

當屬性為必填時使用構造函數,選填時使用屬性賦值

// 驗證最大長度時,最大長度必填,
[AttributeUsage(AttributeTargets.Property)]
public class MaxLenAttribute : Attribute
{
    public int length { get; private set; }
    public string remark { get; set; }
    public MaxLenAttribute(int len)
    {
        this.length = len;
    }
    public bool Validate(object oValue)
    {
        return oValue.ToString().Length > this.length;
    }
}
public class User
{
    [MaxLenAttribute(5, remark = "超出長度!")]
    public string name { get; set; }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM