主要内容概要
- 特性attribute,和注释有什么区别
- 声明和使用attribute,AttributeUsage
- 运行中获取attribute:额外信息 额外操作
- Remark封装、attribute验证
特性及其语法
MVC-EF-WCF-IOC-AOP-SuperSocket 无处不在在。
特性很厉害,加了特性,可以影响编译器编译,还可以增加额外功能。
[Obsolete(“请不要使用这个了,请使用什么来代替”)]//系统
[Serializable]//可以序列化和反序列化
特性到底是什么
特性其实就是一个类,需要直接/间接继承Attribute父类
声明特性的时候,约定俗称是以Attribute 结尾,标记时就可以省略
下面自己写一个特性类:
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class CutomAttribute : Attribute
{
public CutomAttribute()
{
Console.WriteLine("无参数构造函数");
}
public CutomAttribute(int i)
{
Console.WriteLine("Int类型构造函数");
}
public CutomAttribute(int i, string j)
{
Console.WriteLine("两个参数构造函数");
}
public string Remark { get; set; }
public string Description;
public void Show()
{
Console.WriteLine("通过反射调用特性中的方法");
}
}
public class CutomAttributeChild : CutomAttribute
{
public CutomAttributeChild() : base(345)
{
}
}
在外面就可以对特性Cutom进行应用:
// [Obsolete("请不要使用这个了,请使用什么来代替")]//系统
//[Serializable]//可以序列化和反序列化
[Cutom(234, "飞哥")]
[Cutom(123)]
[Cutom(567, Remark = ".Net学习", Description = ".Net正在学习")]
[CutomAttributeChild]
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public void Study()
{
Console.WriteLine($"这里是{this.Name}跟着Eleven老师学习");
}
//[return:Cutom]
public string Answer([Cutom]string name)//修饰参数 返回值
{
return $"This is {name}";
}
}
在标记的时候以中括号包裹,可以标记在元素
[AttributeUsage(AttributeTargets.All,AllowMultiple =true)]
AttributeTargets.Class设置标记的元素 指定修饰对象,建议明确的指定标记的元素
特性的原理与作用
Obsolete,Serializable是系统提供的,我们实现不了,这种是特例。
特性在代码运行的时候,究竟起什么作用?例如像框架中会有很多特性,是怎么工作的?
通过反编译工具可以看到:标记了特性的元素,都会在元素内部生成.custom
,但是这个c#元素内部不能调用。
至此,是不是感觉特性没啥?
那么各种框架集里面是如何使用特性? 反射
反射确实可以调用特性,但是需要一个第三方的InvokeCenter,在这里主动去检测并且使用特性,才能提供功能。
- 首先,看一下在vipstudent类中的特性的使用情况:
[Cutom(123, Remark = ".Net学习", Description = "正在学习特性")]
public class StudentVip : Student
{
[Cutom(123, Remark = ".Vip", Description = "高级会员")]
public string VipGroup { get; set; }
[Cutom(123, Remark = ".Vip", Description = "高级会员")]
public void Homework()
{
//.cutom
Console.WriteLine("Homework");
}
[SalaryAttribute(1000000)]
public long Salary { get; set; }
}
然后,再看一下具体的特性中是怎么写的?这里主要参考CutomAttribute 这个特性的内容。特性中有方法,字段,还有属性。
最后,研究一下,是如何发挥使用特性的?怎么用反射来调用特性中的内容。
public static void ManagerSudent<T>(T student) where T : Student
{
Console.WriteLine(student.Id);
Console.WriteLine(student.Name);
student.Answer("YZM");
Type type = student.GetType();
if (type.IsDefined(typeof(CutomAttribute), true)) // 先判断
{
object[] aAttributeArry = type.GetCustomAttributes(typeof(CutomAttribute), true);
foreach (CutomAttribute attribute in aAttributeArry)
{
attribute.Show();
}
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(CutomAttribute), true)) // 先判断
{
object[] aAttributeArryprop = prop.GetCustomAttributes(typeof(CutomAttribute), true);
foreach (CutomAttribute attribute in aAttributeArryprop)
{
attribute.Show();
}
}
}
foreach (var method in type.GetMethods())
{
if (method.IsDefined(typeof(CutomAttribute), true)) // 先判断
{
object[] aAttributeArryMethod = method.GetCustomAttributes(typeof(CutomAttribute), true);
foreach (CutomAttribute attribute in aAttributeArryMethod)
{
attribute.Show();
}
}
}
}
}
特性是在编译时确认,因此不能赋一个变量!
所以 MVC filter是不能注入的,在core里面才提供了注入了filter的方式
特性的应用
第一个例子:通过特性提供额外信息。
首先举一个remark的实例,在绑定到界面上的时候,经常用到,例如用户的状态。
这里有一个特性,叫做RemarkAttribute,如下:
[AttributeUsage(AttributeTargets.Field)]
public class RemarkAttribute : Attribute
{
public string Remark { get; private set; }
public RemarkAttribute(string remark)
{
this.Remark = remark;
}
}
然后,在用户状态中可以这么用特性:
public enum UserState
{
/// <summary>
/// 正常状态
/// </summary>
[RemarkAttribute("正常状态")]
Normal = 0,
/// <summary>
/// 已冻结
/// </summary>
[RemarkAttribute("已冻结")]
Frozen = 1,
/// <summary>
/// 已删除
/// </summary>
[RemarkAttribute("已删除")]
Deleted = 2
}
为了获取特性还需要封装一个方法来获取:
public static class AttributeExtend
{
public static string GetRemark(this Enum value)
{
Type type = value.GetType();//这个type是UserState
var field = type.GetField(value.ToString());//从UserState这个类中获取字段,更具传进来的名字获取,一般是唯一的,例如这里是Deleted
if (field.IsDefined(typeof(RemarkAttribute), true))//从字段上判断是有特性 true 支持继承
{
RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true);//获取特性RemarkAttribute
return attribute.Remark;
}
else
{
return value.ToString();
}
}
}
下面就来通过特性获取额外信息
//特性获取额外的信息
string remark1 = AttributeExtend.GetRemark(UserState.Normal);
string remark2 = AttributeExtend.GetRemark(UserState.Frozen);
string remark3 = UserState.Deleted.GetRemark(); //扩展方法
总结:这个例子当中,其实特性的作用是提供了一些信息,一些额外的数据。,应用的特点是:
- 数据展示——不想展示属性名字,而是一个中文描述;
- 想指定那个是主键,哪个是自增;
- 别名——数据库里面叫A 程序叫B 怎么映射起来
第二个例子:通过特性提供额外行为。
例如QQ号码有个范围,可以通过特性来做验证。
在上面我们已经写了vipStudent这个类,里面有个field叫做QQ,有个属性叫Salary,如下:
[LongAttribute(_Max = 10, _Min = 5)]
public long QQ;
[SalaryAttribute(800000)] // 公司的预算
public long Salary { get; set; }
这里有两个特性LongAttribute和SalaryAttribute ,他们分别继承自AbstratValidateAttribute,这个特性里面有个抽象方法叫做Validate,让前两个特性去实现验证,如下:
public abstract class AbstratValidateAttribute : Attribute
{
public abstract bool Validate(object oValue);
}
[AttributeUsage(AttributeTargets.Field)]
public class LongAttribute : AbstratValidateAttribute
{
public int _Max { get; set; }
public int _Min { get; set; }
public override bool Validate(object oValue)
{
return oValue != null && oValue.ToString().Length >= 5 && oValue.ToString().Length <= 10;
}
}
public class SalaryAttribute : AbstratValidateAttribute
{
public long _Salary { get; set; } // 公司的预算 800000
public SalaryAttribute(long salary)
{
_Salary = salary;
}
public override bool Validate(object oValue)
{
return oValue != null && long.TryParse(oValue.ToString(), out long lValue) && lValue < _Salary;
}
}
还需要写个泛型的扩展方法,来调用特性的这个验证方法,如下:
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(AbstratValidateAttribute), true)) // 这里先判断 是为了提高性能
{
object ovale = prop.GetValue(t);
foreach (AbstratValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstratValidateAttribute), true)) // 获取特性的实例 上面先判断之后,再获取实例
{
if (!attribute.Validate(ovale))
{
return false;
}
}
}
}
foreach (var field in type.GetFields())
{
if (field.IsDefined(typeof(AbstratValidateAttribute), true)) // 这里先判断 是为了提高性能
{
object ovale = field.GetValue(t);
foreach (AbstratValidateAttribute attribute in field.GetCustomAttributes(typeof(AbstratValidateAttribute), true)) // 获取特性的实例 上面先判断之后,再获取实例
{
if (!attribute.Validate(ovale))
{
return false;
}
}
}
}
return true;
}
这样通过AbstratValidateAttribute可以获取到所有的特性,然后分别去获取属性和字段上的所有特性,依次去执行验证的方法,这就是抽象类的一个应用,分别是实现不同的validate方法。可以通过调用特性student.Validate()
来验证。