一、概念
- 特性是一种允许我们向程序的程序集添加元数据的语言结构。它是用于保存程序结构信息的特殊类型的类。(可以通过反射来访问元数据,从而操作特性相关内容)
- 特性类似于一种声明性标签,可以用于修饰各种元素,包括类,方法,结构体,枚举,属性,变量等。
二、定义
- 由方括号,特性名和参数列表构成,特性标签会被放在需要被修饰的元素之前。
- 在一个元素上,可以使用一个或多个特性进行修饰。
- 特性可以拥有参数。
- 程序可以使用反射检查自己的元数据或者其他程序中的元数据。
三、示例
- 单个特性修饰类
[Serializable] //可序列化
public class MyClass
{
}
自定义特性类时需要主要它的三个参数
-
AttributeTargets
是标注当前特性可用于哪些对象,因为该枚举的值 在二进制中均只有一位为1, 即为2的次方整数值,所以通常通过位运算 或 ( | )来运算。 -
Inherited
表示特性是否可被 修饰类型的派生类继承。 -
AllowMultiple
表示是否可以对同一个目标进行多次修饰。 -
自定义特性类
//自定义特性类
[AttributeUsage(AttributeTargets.All)] //用于标注当前声明的类应用于哪些对象,All表示全部对象
public class MyDemoAttribute: Attribute
{
public string Description { get; set; }
}
public class OrderData
{
[MyDemo(Description = "订单 ID")]
public int OrderID { get; set; }
[MyDemo(Description = "添加时间")]
public DateTime AddTime { get; set; }
[MyDemo(Description = "计算折扣价")]
public double Compute(double q)
{
return q * 0.8;
}
}
- 向特性类构造函数传递参数
//向特性类构造函数传递参数
//特性类只有无参构造,应用时可以忽略小括号。若有有参构造函数,那么应在小括号中添上参数
[AttributeUsage(AttributeTargets.Class)]
public class DoubleRangeAttribute: Attribute
{
public double Largest { get; }
public DoubleRangeAttribute(double largest)
{
Largest = largest;
}
}
[DoubleRange(800)]
public class Test1
{
}
- 同一对象上应用多个特性实例
//同一对象上应用多个特性实例(AllowMultiple默认为false)
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
class CustomAttribute: Attribute
{
public string Version { get; set; }
}
//两种写法
[Custom(Version = "1.0.0.1"), Custom(Version = "1.0.0.2")]
[Custom(Version = "1.0.0.3")]
public class Test2
{
}
- 通过反射进行数据验证处理
// 声明特性类
[AttributeUsage(AttributeTargets.Property)]
public class MyAttribute: Attribute
{
public char StartChar { get; set; }
public int MaxLen { get; set; }
}
//被测试的类
public class Test3
{
[My(StartChar = 'G', MaxLen = 10)]
public string Name { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
func1();
}
static void func1()
{
// 实例化并初始化
Test3 test3 = new Test3 { Name = "Gzy" };
// 检查
bool b = checkTest(test3, nameof(Test3.Name));
if (b) Console.WriteLine("验证通过");
}
/// <summary>
/// 检查传入的名字在元数据中 是否合法 包括以下3点:
/// 判断名字是否存在
/// 判断名字是否以StartChar字段开头
/// 判断名字长度是否小于MaxLen字段
/// </summary>
/// <param name="t">Test3 类型对象</param>
/// <param name="name">需要判断的字符串</param>
/// <returns>是否合法</returns>
static bool checkTest(Test3 t, string name)
{
Type type = t.GetType();
//名字为name的获取属性
PropertyInfo prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (prop == null)
{
return false;
}
// 获取特性
MyAttribute att = prop.GetCustomAttribute<MyAttribute>();
//获取实例的属性值
string value = prop.GetValue(t) as string;
if (string.IsNullOrEmpty(value) //判空
|| value.StartsWith(att.StartChar.ToString()) == false //判断首字母
|| value.Length > att.MaxLen) //判断长度
{
return false;
}
return true;
}
}
- 在返回值上应用特性
[AttributeUsage(AttributeTargets.ReturnValue)]
public class CheckSumAttribute: Attribute
{
}
public class Test4
{
[return: CheckSum]
static string Speak() => "HelloWorld";
}