目录
什么是特性
Serializable
DllImport
Obsolete
Conditional
自定义Attribute
特性定义:特性其实是一个类,是直接/间接继承自Attribute,约定俗成是以Attribut结尾,在标记的时候以[ ]包裹,尾部的Attribut可以省略掉;
特性的作用:可以增加额外信息,增加额外功能,但让特性生效需要使用反射;
什么是特性
Serializable
首先,当一个类需要支持序列化时,我们总能看到类名上方有如下的结构:
[Serializable]
class SampleClass
{
// ...
}
[Serializable]
就是用来标识当前类是否可序列化的。
DllImport
用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。
[DllImport("User32.dll")]
public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
这段代码中 [DllImport]表明了MessageBox是User32.DLL中的函数,这样我们就可以像内部方法一样调用这个函数。
Obsolete
当我们的程序在迭代过程中出现了一些打算弃用的方法,如果直接删除的话,势必会影响到版本的兼容性。因此需要添加一个标识,让开发者注意到这个方法已经弃用,在未来的版本中可能会被删除。这就需要用到[Obsolete]
特性:
[Obsolete("这是需要提示的内容",true)]
public class Test
{
}
当我们的程序在迭代过程中出现了一些打算弃用的方法,如果直接删除的话,势必会影响到版本的兼容性。因此需要添加一个标识,让开发者注意到这个方法已经弃用,在未来的版本中可能会被删除。这就需要用到[Obsolete]
特性:
这样在调用被[Obsolete(string,bool)]
标记的方法,当bool类型为true时,编译器就会进行提示并报错:
Conditional
在开发环境下,我们经常需要编写一些调试方法,而在打包时又需要去除这些代码。挨个删除显然是不现实的,此时就需要用到[Conditional]
特性。它需要在参数中传递一个字符串,编译器会根据这个字符串寻找同名的宏。如果这个宏定义了,该特性修饰的方法就会启用,反之则禁用。
#define IsShowMessage
// ...
[Conditional("IsShowMessage")]
public static void Test2()
{
Console.WriteLine("调试信息。。。。");
}
// ...
因此在调试过程中,先用[Conditional]
特性标记调试方法,调试完毕将#define IsShowMessage注释即可一键禁用标记过的调试方法。
通过上面的几个示例,我们再来理解特性的定义:
- 将应用了特性的程序结构叫做目标
- 设计用来获取和使用元数据的程序(对象浏览器)叫做特性的消费者
- NET预定了很多特性,我们也可以声明自定义特性
重要的一点就是Attribute就是一个类,Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。
我们对编写的类、方法、属性,方法的参数,方法的返回值等通过特性进行标记,编译器就会根据特性产生元数据,再将这些元数据放入程序集中。消费者就可以获取到这些元数据,并进行使用。
自定义Attribute
首先,特性是一个类。在自定义特性时,我们自定义的特性类名中必须包含Attribute,同时继承(直接继承或者间接继承)Attribute类。然后需要在该类上通过AttributeUsage特性指明自定义特性的应用范围。
/// <summary>
/// 自定义特性
/// AllowMultiple =true:标记在特性上的特性,其实是对特性的一种约束;
/// Inherited =true:约束当前特性是否可以继承
/// AttributeTargets.All:当前特性可以标记在所有的元素上
/// AttributeUsage:在定义特性的时候,对特性的一种约束
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class CustomAttribute : Attribute
{
public CustomAttribute()
{
}
public CustomAttribute(int id)
{
}
public CustomAttribute(string name)
{
}
public CustomAttribute(int id,string name)
{
this.Id = id;
this.Name = name;
}
public string Description;
public int Id { get; set; }
public string Name { get; set; }
public int Do()
{
return 1;
}
}
}
利用反射获取Attribute信息并执行方法
public static void ReflectionArrtibute<T>(T t) where T : Class
{
Type type = t.GetType();//通过参数来获取Type类型
if (type.IsDefined(typeof(CustomAttribute),true)) //反射调用特性,记得一定要先判断,然后再获取
{
foreach (CustomAttribute attribute in type.GetCustomAttributes()) //把所有标记的特性都实例化了
{
Console.WriteLine($"attribute.Id={attribute.Id}");
Console.WriteLine($"attribute.Name={attribute.Name}");
attribute.Do();
}
///循环获取所有的属性上标记的特性,调用特性中的元素
foreach (PropertyInfo prop in type.GetProperties())
{
if (prop.IsDefined(typeof(CustomAttribute), true))
{
foreach (CustomAttribute attribute in prop.GetCustomAttributes()) //把所有标记的特性都实例化了
{
Console.WriteLine($"attribute.Id={attribute.Id}");
Console.WriteLine($"attribute.Name={attribute.Name}");
attribute.Do();
}
}
}
///循环获取所有的字段上标记的特性,调用特性中的元素
foreach (FieldInfo field in type.GetFields())
{
if (field.IsDefined(typeof(CustomAttribute), true))
{
foreach (CustomAttribute attribute in field.GetCustomAttributes()) //把所有标记的特性都实例化了
{
Console.WriteLine($"attribute.Id={attribute.Id}");
Console.WriteLine($"attribute.Name={attribute.Name}");
attribute.Do();
}
}
}
///循环获取所有方法上标记的特性,调用特性中的元素
foreach (MethodInfo method in type.GetMethods())
{
if (method.IsDefined(typeof(CustomAttribute), true))
{
foreach (CustomAttribute attribute in method.GetCustomAttributes()) //把所有标记的特性都实例化了
{
Console.WriteLine($"attribute.Id={attribute.Id}");
Console.WriteLine($"attribute.Name={attribute.Name}");
attribute.Do();
}
}
}
}
将枚举值的中文扩展信息添加到Attribute
//新增ExplainAttribute
[AttributeUsage( AttributeTargets.All,AllowMultiple =true,Inherited =true)]
public class ExplainAttribute :Attribute
{
public string Explain { get; private set; }
public ExplainAttribute(string explain)
{
this.Explain = explain;
}
}
//创建枚举
public enum GameState
{
[ExplainAttribute("开始")]
Start,
[ExplainAttribute("暂停")]
Stop,
[ExplainAttribute("继续")]
GoOn
}
//获取Attribute信息
public static class ExplainExtension
{
public static string GetExplain(this Enum @enum)
{
Type type = @enum.GetType();
FieldInfo field = type.GetField(@enum.ToString());
if (field != null)
{
if (field.IsDefined(typeof(ExplainAttribute), true))
{
ExplainAttribute attribute = field.GetCustomAttribute<ExplainAttribute>();
return attribute.Explain;
}
}
return @enum.ToString();
}
}
使用Attribute验证输入信息
//定义用于验证的Attribute
[AttributeUsage(AttributeTargets.Property)]
public class MobileNumAtrribute : AbstractValidateAttribute
{
public int Count { get; private set; }
public MobileNumAtrribute(int count)
{
if (count<=0)
{
throw new Exception("手机号长度不可能为负数");
}
Count = count;
}
public override bool Validate(object mobileNum)
{
return mobileNum != null && mobileNum.ToString().Length == Count;
}
}
//定义需要验证的类
public class Student
{
public string Description;
public string QQ { get; set; }
[MobileNumAtrribute(11)]
public long MobileNum { get; set; }
}
//验证
public static bool Validate<T>(T t)
{
Type type = t.GetType();
foreach (PropertyInfo prop in type.GetProperties())
{
/ 验证手机号长度
if (prop.IsDefined(typeof(MobileNumAtrribute), true))
{
object oValue = prop.GetValue(t);
MobileNumAtrribute atrribute = prop.GetCustomAttribute<MobileNumAtrribute>(true);
if (!atrribute.Validate(oValue))
{
return false;
}
}
return true;
}