特性
三、特性Attribute
1.特性attribute和注释有什么区别
特性可以影响编译器和程序运行,注释不影响。
2.声明和使用attribute,AttributeUsage
编译器也提供了很多特性 ,Obsolete 用来提醒代码过时,[Obsolete(“请不要使用这个,请使用**代替”,true)] ,影响程序的编译,当为false的时候允许正常使用,但是会产生警告,为true禁止使用。[Serializable],可以序列化和反序列化,可以影响程序的运行。还有MVC filter ORM table key display WCF等等。
现在,我们自定义一个特性
[AttributeUsage(AttributeTargets.All,Inherited =true)]
class CustomAttribute :Attribute
{
public string Description { get; set; }
public string Remark;
public CustomAttribute()
{
}
public CustomAttribute(int id)
{
}
public CustomAttribute(string name)
{
}
public void Show()
{
}
}
AttributeUsage修饰特性的特性
[AttributeUsage(AttributeTargets.All,Inherited =true)],AttributeTargets限定使用的特性范围,AttributeTarget.Class|AttributeTarget.Method修饰类或方法,AttributeTargets.All可以修饰所有。AllowMultiple 默认是false,是否允许允许重复修饰AllowMultiple =true,允许重复修饰 ,Inherited =true可以被继承。
//[CustomAttribute]//一般来说,以attribute结尾可以省略掉。CustomAttribute
//[Custom]无参
//[Custom()]//无参
//[Custom(12)]//带参
//[Custom(13),Custom(123,Description ="123")]//带参,参数可以直接传过来,字段属性需要带名字
[Custom(14,Description ="1234",Remark ="234")]//方法不可以 很像补充类
public class Student
{
[Custom]
public int Id { get; set; }
public string Name { get; set; }
[Custom]
public void Study()
{
Console.WriteLine($"{this.Name}");
}
[Custom()]//方法加特性
[return:Custom()]//返回值带特性,用的少
public string Answer([Custom]string name)//参数加特性
{
return name;
}
}
错觉:每一种特性都可以带来对应的功能,实际上特性添加后,编译会在元素内部产生IL,而且在metadata里面会有记录,但是我们是没有办法使用的。
//反射使用特性
public static void Show(Student student)
{
// student.Study();
// string result = student.Answer("EVE");
Type type = typeof(Student);//student.GetType(); 访问student上的特性
//类的
if (type.IsDefined(typeof(CustomAttribute), true))//判断是否有这个特性
{
Attribute oAttrubute = type.GetCustomAttribute(typeof(CustomAttribute), true);
CustomAttribute attribute = (CustomAttribute)oAttrubute;
Console.WriteLine($"{attribute.Description} {attribute.Remark}");
attribute.Show();
}
//属性
PropertyInfo property = type.GetProperty("Id");
if (property.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description} {attribute.Remark}");
attribute.Show();
}
//方法
MethodInfo methodInfo = type.GetMethod("Answer");
if (methodInfo.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description} {attribute.Remark}");
attribute.Show();
}
//参数
ParameterInfo parameter = methodInfo.GetParameters()[0];
if (parameter.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description} {attribute.Remark}");
attribute.Show();
}
//方法返回值
ParameterInfo returnInfo = methodInfo.ReturnParameter;
if (returnInfo.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.Description} {attribute.Remark}");
attribute.Show();
}
student.Study();
string result = student.Answer("Eleven");
}
特性,本身是没有用的,程序运行的过程中,我们能找到特性,而且也能用反射应用一下,不用反射,特性没有用,任何一个可以生效的特性,都是因为又地方主动使用了的。更像是一个补充类。
//使用
Student student = new Student();
student.Id = 1;
student.Name = "LIN";
// student.Study();
// string result = student.Answer("EVE");
Manager.Show(student);
3.运行中获取attribute:额外信息,额外操作
没有破坏类型封装的前提下,可以加点额外的信息和行为
特性在IL在编译时都要确定,所以特性不能是动态的,不能是变量
实例:
1.枚举加一个描述
2.实体的是属性也可以Display
3.别名 映射
增加信息
public enum UserState
{
/// <summary>
/// 正常
/// </summary>
[Remark("正常")]
Normal=0,
/// <summary>
/// 冻结
/// </summary>
[Remark("冻结")]
Frozon = 1,
/// <summary>
/// 删除
/// </summary>
[Remark("删除")]
Deleted = 2,
}
public class RemarkAttribute:Attribute
{
private string _Remark = null;
public RemarkAttribute(string remark)
{
this._Remark = remark;
}
public string GetRemark()
{
return this._Remark;
}
}
public static class RemarkExtension
{
public static string GetRemark(this Enum value) //扩展方法
{//静态类中的静态方法,第一个参数类型前加一个this,这里只要是枚举值就可以传进来,调用就不需要在定义类,直接可以使用
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
if (fieldInfo.IsDefined(typeof(RemarkAttribute), true))
{
RemarkAttribute attribute = (RemarkAttribute)fieldInfo.GetCustomAttribute(typeof(RemarkAttribute), true);
return attribute.GetRemark();
}
else
{
return value.ToString();
}
}
{
UserState userState = UserState.Normal;
if (userState == UserState.Normal)
Console.WriteLine("正常");
else if (userState == UserState.Frozon)
Console.WriteLine("冻结");
else
Console.WriteLine("删除");
UserState userState1 = UserState.Deleted;
Console.WriteLine(RemarkExtension.GetRemark(userState1));
Console.WriteLine(UserState.Frozon.GetRemark());//扩展方法
}
扩展方法的使用
静态类中的静态方法,第一个参数类型前加一个this,这里只要是枚举值就可以传进来,调用就不需要在定义类,直接可以使用
public static class RemarkExtension
{
public static string GetRemark(this Enum value) //扩展方法
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
if (fieldInfo.IsDefined(typeof(RemarkAttribute), true))
{
RemarkAttribute attribute = (RemarkAttribute)fieldInfo.GetCustomAttribute(typeof(RemarkAttribute), true);
return attribute.GetRemark();
}
else
{
return value.ToString();
}
}
增加行为
public static class ValidateExtension
{
public static bool Validate(this object obj)
{
Type type = obj.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(LongAttribute), true))
{
LongAttribute longAttribute= (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute),true);
if (!longAttribute.Validate(prop.GetValue(obj)))
{
return false;
}
}
else if (prop.IsDefined(typeof(LengAttribute), true))
{
LengAttribute lengAttribute = (LengAttribute)prop.GetCustomAttribute(typeof(LengAttribute), true);
if (!lengAttribute.Validate(prop.GetValue(obj)))
{
return false;
}
}
}
return true;
}
}
public class LongAttribute : Attribute
{
private long _Min = 0;
private long _Max = 0;
public LongAttribute(long min, long max)
{
this._Min = min;
this._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 LengAttribute : Attribute
{
private int _Min = 0;
private int _Max = 0;
public LengAttribute(int min, int max)
{
this._Min = min;
this._Max = max;
}
public bool Validate(object value)
{
if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if (int.TryParse(value.ToString(), out int lResult))
{
if (lResult >= this._Min && lResult <= this._Max)
{
return true;
}
}
}
return false;
}
}
public class Student
{
[Custom]
public int Id { get; set; }
public string Name { get; set; }
//写一个行为
public long QQ { get; set; }//10001~99999
private long _QQ2 = 0;//不建议这些写,业务逻辑写到属性中,给属性增加了太多的事
public long QQ2
{
get
{
return this._QQ2;
}
set
{
if (value > 10001 && value < 999999)
{
_QQ2 = value;
}
else
{
throw new Exception();
}
}
}//10001~99999
[Long(10001,99999)]
public long QQ3 { get; set; }
[Custom]
public void Study()
{
Console.WriteLine($"{this.Name}");
}
}
Student student = new Student();
student.Id = 2;
student.Name = "KING";
student.QQ3 = 12345;
Manager.Show(student);
student.QQ3 = 9;
Manager.Show(student);
student.Name = "KIN";
Manager.Show(student);
public static void Show(Student student)
{
//检查
Console.WriteLine(student.Validate());
student.Study();
string result = student.Answer("Eleven");
}
用抽象方法还可以更简单一步
public static class ValidateExtension
{
public static bool Validate(this object obj)
{
Type type = obj.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
{
object[] attributeArray = prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true);//一个属性可能会有多个特性所以加一下循环
foreach (AbstractValidateAttribute attribute in attributeArray)
{
if (!attribute.Validate(prop.GetValue(obj)))
{
return false;
}
}
}
}
return true;
}
}
public abstract class AbstractValidateAttribute : Attribute
{
public abstract bool Validate(object value);
}
public class LongAttribute : AbstractValidateAttribute
{
private long _Min = 0;
private long _Max = 0;
public LongAttribute(long min, long max)
{
this._Min = min;
this._Max = max;
}
public override 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 LengAttribute : AbstractValidateAttribute
{
private int _Min = 0;
private int _Max = 0;
public LengAttribute(int min, int max)
{
this._Min = min;
this._Max = max;
}
public override bool Validate(object value)
{
if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if (int.TryParse(value.ToString(), out int lResult))
{
if (lResult >= this._Min && lResult <= this._Max)
{
return true;
}
}
}
return false;
}
}