一、特性是什麼
1、特性定義
特性(Attribute)是用於在執行時傳遞程式中各種元素(比如類、方法、結構、列舉、元件等)的行為資訊的宣告性標籤。您可以透過使用特性向程式新增宣告性資訊。一個宣告性標籤是透過放置在它所應用的元素前面的方括號([ ])來描述的。
特性(Attribute)用於新增後設資料,如編譯器指令和註釋、描述、方法、類等其他資訊。.Net 框架提供了兩種型別的特性:預定義特性和自定義特性。
2、特性的語法
特性(Attribute)的名稱和值是在方括號內規定的,放置在它所應用的元素之前。positional_parameters 規定必需的資訊,name_parameter 規定可選的資訊。
1
2
[attribute(positional_parameters, name_parameter = value, ...)]
element
3、特性和註釋有什麼區別
特性很厲害,加了特性之後,就有很厲害的功能
[Obsolete]編譯時就有提示,影響了編譯器[Obsolete(“請不要使用這個了,請使用什麼來代替”, true)]甚至導致編譯報錯
[Serializable]物件就可以序列化,影響了程式執行
using System;
namespace MyAttribute
{
/// <summary>
/// 這裡是註釋,除了讓人看懂這裡寫的是什麼,對執行沒有任何影響
/// </summary>
///[Obsolete("請不要使用這個了,請使用什麼來代替")]//對編譯都產生了影響,編譯出現警告
///[Obsolete("請不要使用這個了,請使用什麼來代替", true)]//對編譯都產生了影響,編譯報錯不透過
[Serializable]//可以序列化和反序列化
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public void Study()
{
Console.WriteLine($"這裡是{this.Name}在學習");
}
public string Answer([Custom]string name)
{
return $"This is {name}";
}
}
}
特性無處不在:EF–MVC–WCF–WebService–UnitTest–IOC–AOP–SuperSocket
二、特性宣告和使用
1、什麼是特性
特性其實就是一個類,直接或間接繼承自Attribute
#region 程式集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\mscorlib.dll
// Decompiled with ICSharpCode.Decompiler 6.1.0.5902
#endregion
using System.Reflection;
using System.Runtime.InteropServices;
namespace System
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
[ComVisible(true)]
public sealed class SerializableAttribute : Attribute
{
internal static Attribute GetCustomAttribute(RuntimeType type)
{
if ((type.Attributes & TypeAttributes.Serializable) != TypeAttributes.Serializable)
{
return null;
}
return new SerializableAttribute();
}
internal static bool IsDefined(RuntimeType type)
{
return type.IsSerializable;
}
}
}
#if false // 反編譯日誌
快取中的 9 項
#endif
2、自定義一個特性
using System;
namespace MyAttribute
{
public class CustomAttribute : Attribute
{
}
}
約定俗成用Attribute結尾,標記時就可以省略掉;可以用中括號包裹,然後標記到元素,其實就是呼叫建構函式;
using System;
namespace MyAttribute
{
[Custom]
public class Student
{
[Custom]
public int Id { get; set; }
public string Name { get; set; }
[Custom]
public void Study()
{
Console.WriteLine($"這裡是{this.Name}跟著Gerry老師學習");
}
}
}
3、AttributeUsage特性
直接在一個元素上新增多個相同的特性,會報錯特性重複,需要在特性上面新增特性標記[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
這樣就可以給同一個元素新增多個相同的特性了
using System;
namespace MyAttribute
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class CustomAttribute : Attribute
{
public CustomAttribute()
{
Console.WriteLine($"{this.GetType().Name} 無引數建構函式執行");
}
public CustomAttribute(int id)
{
Console.WriteLine($"{this.GetType().Name} int引數建構函式執行");
this._Id = id;
}
public CustomAttribute(string name)
{
Console.WriteLine($"{this.GetType().Name} string引數建構函式執行");
this._Name = name;
}
}
}
多個相同的特性情況展示
using System;
namespace MyAttribute
{
[Custom]
[Custom()]
[Custom(10)]
public class Student
{
[Custom]
public int Id { get; set; }
public string Name { get; set; }
[Custom]
public void Study()
{
Console.WriteLine($"這裡是{this.Name}跟著Gerry老師學習");
}
[Custom(0)]
public string Answer([Custom]string name)
{
return $"This is {name}";
}
}
}
AttributeUsage特性,影響編譯器執行,指定修飾的物件、能否重複修飾、修飾的特性子類是否生效,建議是明確約束用在哪些物件的
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class|AttributeTargets.Property, AllowMultiple = true)]
4、特性可以指定屬性和欄位
using System;
namespace MyAttribute
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class CustomAttribute : Attribute
{
public CustomAttribute()
{
Console.WriteLine($"{this.GetType().Name} 無引數建構函式執行");
}
public CustomAttribute(int id)
{
Console.WriteLine($"{this.GetType().Name} int引數建構函式執行");
this._Id = id;
}
public CustomAttribute(string name)
{
Console.WriteLine($"{this.GetType().Name} string引數建構函式執行");
this._Name = name;
}
private int _Id = 0;
private string _Name = null;
public string Remark;
public string Description { get; set; }
}
}
using System;
namespace MyAttribute
{
[Custom(Remark = "123")]
[Custom(Remark = "123", Description = "456")]
[Custom(0, Remark = "123")]
[Custom(0, Remark = "123", Description = "456")]
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public void Study()
{
Console.WriteLine($"這裡是{this.Name}跟著Gerry老師學習");
}
}
}
5、特性還可以修飾返回值和引數
using System;
namespace MyAttribute
{
public class Student
{
[return: Custom]
public string Answer([Custom]string name)
{
return $"This is {name}";
}
}
}
6、多重修飾既可以中括號隔開,也可以一箇中括號裡面逗號隔開
using System;
namespace MyAttribute
{
[Custom]
[Custom()]
[Custom(Remark = "123")]
[Custom(Remark = "123", Description = "456")]
[Custom(0)]
[Custom(0, Remark = "123")]
[Custom(0, Remark = "123", Description = "456")]
public class Student
{
[return: Custom, Custom,Custom(), Custom(0, Remark = "123", Description = "456")]
public string Answer(string name)
{
return $"This is {name}";
}
}
}
四、特性應用案例
1、特性實現列舉展示描述資訊
(1)建立列舉類
namespace MyAttribute.EnumExtend
{
/// <summary>
/// 使用者狀態
/// </summary>
public enum UserState
{
/// <summary>
/// 正常狀態
/// </summary>
[Remark("正常狀態")]
Normal = 0,
/// <summary>
/// 已凍結
/// </summary>
[Remark("已凍結")]
Frozen = 1,
/// <summary>
/// 已刪除
/// </summary>
[Remark("已刪除")]
Deleted = 2
}
}
(2)建立特性類
using System;
namespace MyAttribute.EnumExtend
{
/// <summary>
/// Remark特性
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class RemarkAttribute : Attribute
{
public string Remark { get; private set; }
public RemarkAttribute(string remark)
{
this.Remark = remark;
}
}
}
(3)列舉擴充套件方法
using System;
using System.Reflection;
namespace MyAttribute.EnumExtend
{
public static class AttributeExtend
{
public static string GetRemark(this Enum value)
{
Type type = value.GetType();
var field = type.GetField(value.ToString());
if (field.IsDefined(typeof(RemarkAttribute), true))
{
RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true);
return attribute.Remark;
}
else
{
return value.ToString();
}
}
}
}
2、特性實現資料驗證
(1)基類抽象特性
using System;
namespace MyAttribute.ValidateExtend
{
public abstract class AbstractValidateAttribute : Attribute
{
public abstract bool Validate(object oValue);
}
}
(2)子類特性實現–數字長度
using System;
namespace MyAttribute.ValidateExtend
{
[AttributeUsage(AttributeTargets.Property)]
public class LongAttribute : AbstractValidateAttribute
{
private long _Min = 0;
private long _Max = 4;
public LongAttribute(long min, long max)
{
this._Min = min;
this._Max = max;
}
public override bool Validate(object oValue)
{
return oValue != null
&& long.TryParse(oValue.ToString(), out long lValue)
&& lValue >= this._Min
&& lValue <= this._Max;
}
}
}
(3)子類特性實現–可空
namespace MyAttribute.ValidateExtend
{
public class RequiredAttribute : AbstractValidateAttribute
{
public override bool Validate(object oValue)
{
return oValue != null
&& !string.IsNullOrWhiteSpace(oValue.ToString());
}
}
}
(4)子類特性實現–字串長度
using System;
namespace MyAttribute.ValidateExtend
{
[AttributeUsage(AttributeTargets.Property)]
public class StringLengthAttribute : AbstractValidateAttribute
{
private int _Min = 0;
private int _Max = 3;
public StringLengthAttribute(int min, int max)
{
this._Min = min;
this._Max = max;
}
public override bool Validate(object oValue)
{
return oValue != null
&& oValue.ToString().Length >= this._Min
&& oValue.ToString().Length <= this._Max;
}
}
}
(5)泛型擴充套件方法
using System;
namespace MyAttribute.ValidateExtend
{
public static class AttributeExtend
{
public static bool Validate<T>(this T t)
{
Type type = t.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
{
object oValue = prop.GetValue(t);
foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
{
if (!attribute.Validate(oValue))
return false;
}
}
}
return true;
}
}
}
(6)常規類欄位定義
using System;
namespace MyAttribute.ValidateExtend
{
public static class AttributeExtend
{
public static bool Validate<T>(this T t)
{
Type type = t.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
{
object oValue = prop.GetValue(t);
foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
{
if (!attribute.Validate(oValue))
return false;
}
}
}
return tue;
}
}
}
(7)類呼叫擴充套件方法驗證欄位
using MyAttribute.EnumExtend;
using MyAttribute.ValidateExtend;
using System;
namespace MyAttribute
{
/// <summary>
/// main方法呼叫
/// </summary>
class Program
{
static void Main(string[] args)
{
try
{
#region 特性實現資料驗證,並且可擴充套件
{
//透過特性去提供額外行為
//資料驗證--到處都需要驗證
StudentVip student = new StudentVip()
{
Id = 123,
Name = "無為",
QQ = 729220650,
Salary = 1010000
};
if (student.Validate())
{
Console.WriteLine("特性校驗成功");
}
//1 可以校驗多個屬性
//2 支援多重校驗
//3 支援規則的隨意擴充套件
}
#endregion
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}
}
}