第1章:特性
本章目标
理解特性的概念
熟悉常用预定义特性
掌握自定义特性的运用
本章内容
特性是什么
特性的定义
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
特性其实就是一个类,直接或间接继承自Attribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
#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
|
特性的语法
特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
1 2
| [attribute(positional_parameters, name_parameter = value, ...)] element
|
特性和注释的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| using System;
namespace CH01Demo { [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 2 3 4 5 6 7 8 9 10 11
| using System;
namespace CH01Demo { public class CustomAttribute : Attribute { } }
|
约定俗成用Attribute结尾,标记时就可以省略掉;可以用中括号包裹,然后标记到元素,其实就是调用构造函数;
使用特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| using System;
namespace CH01Demo { [Custom] public class Student { [Custom] public int Id { get; set; } public string Name { get; set; } [Custom] public void Study() { Console.WriteLine($"这里是{this.Name}跟着Gerry老师学习"); } } }
|
AttributeUsage特性
直接在一个元素上添加多个相同的特性,会报错特性重复,需要在特性上面添加特性标记 :[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
样就可以给同一个元素添加多个相同的特性了 .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { [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; } } }
|
多个相同的特性情况展示 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| using System;
namespace CH01Demo { [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}跟着陈老师学习"); } [Custom(0)] public string Answer([Custom]string name) { return $"This is {name}"; } } }
|
AttributeUsage特性,影响编译器运行,指定修饰的对象、能否重复修饰、修饰的特性子类是否生效,建议是明确约束用在哪些对象的 。特性应用案例
1
| [AttributeUsage(AttributeTargets.Method|AttributeTargets.Class|AttributeTargets.Property, AllowMultiple = true)]
|
特性可以指定属性和字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| using System;
namespace CH01Demo {
[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; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| using System;
namespace CH01Demo { [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}跟着陈老师学习"); } } }
|
特性可以修饰返回值和参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| using System;
namespace CH01Demo { public class Student { [return: Custom] public string Answer([Custom]string name) { return $"This is {name}"; } } }
|
特性的多重修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| using System;
namespace CH01Demo { [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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| namespace CH01Demo { public enum UserState { [Remark("正常状态")] Normal = 0, [Remark("已冻结")] Frozen = 1, [Remark("已删除")] Deleted = 2 } }
|
创建特性类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { [AttributeUsage(AttributeTargets.Field)] internal class RemarkAttribute:Attribute { public string Remark { get; set; }
public RemarkAttribute(string c) { this.Remark = c; } } }
|
枚举扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
static string GetRemark(Enum en) { Type type = en.GetType();
FieldInfo field = type.GetField(en.ToString());
if (field.IsDefined(typeof(RemarkAttribute))) { RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute)); return attribute.Remark; } else { return en.ToString(); } }
|
1 2 3 4 5 6 7 8 9 10
| static void Fun4() { UserState state = UserState.Normal;
string ab = GetRemark(state);
Console.WriteLine(ab); }
|
特性数显数据验证
基类抽象特性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { internal abstract class AbstractValidateAttribute:Attribute { public abstract bool Validate(object oValue); } }
|
子类特性实现-数字大小:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { [AttributeUsage(AttributeTargets.Property)] internal 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) { if (oValue == null) return false;
long value = long.Parse(oValue.ToString());
return value>=_Min && value<=_Max; } } }
|
子类特性实现-字符串非空:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { internal class RequiredAttribute : AbstractValidateAttribute { public override bool Validate(object oValue) { return oValue != null && !string.IsNullOrWhiteSpace(oValue.ToString()); } } }
|
子类特性实现-字符串长度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { [AttributeUsage(AttributeTargets.Property)] internal 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; } } }
|
特性校验方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public static bool Validate<T>(T t) { Type type = t.GetType();
foreach (var prop in type.GetProperties()) { if (prop.IsDefined(typeof(AbstractValidateAttribute))) { object oValue = prop.GetValue(t);
foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute))) { if (!attribute.Validate(oValue)) return false; } } } return true; }
|
教师类定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace CH01Demo { internal class Teacher { [Required] [StringLength(2,6)] public string Name { get; set; }
[Long(20,40)] public int Age { get; set; } } }
|
类调用扩展方法验证字段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static void Fun5() { Teacher t = new Teacher() { Name="陈凯", Age=35 };
if (Validate(t)) { Console.WriteLine("特性校验成功"); } else { Console.WriteLine("特性校验失败"); } }
|
本章总结
略
课后作业
略