一、特性是什么
1、特性定义
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
2、特性的语法
特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
1 2
[attribute(positional_parameters, name_parameter = value, ...)] element
3、特性和注释有什么区别
特性很厉害,加了特性之后,就有很厉害的功能
[Obsolete]编译时就有提示,影响了编译器[Obsolete(“请不要使用这个了,请使用什么来代替”, true)]甚至导致编译报错
[Serializable]对象就可以序列化,影响了程序运行
using System; namespace MyAttribute { /// <summary> /// 这里是注释,除了让人看懂这里写的是什么,对运行没有任何影响 /// </summary> ///[Obsolete("请不要使用这个了,请使用什么来代替")]//对编译都产生了影响,编译出现警告 ///[Obsolete("请不要使用这个了,请使用什么来代替", true)]//对编译都产生了影响,编译报错不通过 [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、什么是特性
特性其实就是一个类,直接或间接继承自Attribute
#region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\mscorlib.dll // Decompiled with ICSharpCode.Decompiler 6.1.0.5902 #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
2、自定义一个特性
using System; namespace MyAttribute { public class CustomAttribute : Attribute { } }
约定俗成用Attribute结尾,标记时就可以省略掉;可以用中括号包裹,然后标记到元素,其实就是调用构造函数;
using System; namespace MyAttribute { [Custom] public class Student { [Custom] public int Id { get; set; } public string Name { get; set; } [Custom] public void Study() { Console.WriteLine($"这里是{this.Name}跟着Gerry老师学习"); } } }
3、AttributeUsage特性
直接在一个元素上添加多个相同的特性,会报错特性重复,需要在特性上面添加特性标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
这样就可以给同一个元素添加多个相同的特性了
using System; namespace MyAttribute { [AttributeUsage(AttributeTargets.All, AllowMultiple = 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; } } }
多个相同的特性情况展示
using System; namespace MyAttribute { [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}跟着Gerry老师学习"); } [Custom(0)] public string Answer([Custom]string name) { return $"This is {name}"; } } }
AttributeUsage特性,影响编译器运行,指定修饰的对象、能否重复修饰、修饰的特性子类是否生效,建议是明确约束用在哪些对象的
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class|AttributeTargets.Property, AllowMultiple = true)]
4、特性可以指定属性和字段
using System; namespace MyAttribute { [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; } } }
using System; namespace MyAttribute { [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}跟着Gerry老师学习"); } } }
5、特性还可以修饰返回值和参数
using System; namespace MyAttribute { public class Student { [return: Custom] public string Answer([Custom]string name) { return $"This is {name}"; } } }
6、多重修饰既可以中括号隔开,也可以一个中括号里面逗号隔开
using System; namespace MyAttribute { [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、特性实现枚举展示描述信息
(1)创建枚举类
namespace MyAttribute.EnumExtend { /// <summary> /// 用户状态 /// </summary> public enum UserState { /// <summary> /// 正常状态 /// </summary> [Remark("正常状态")] Normal = 0, /// <summary> /// 已冻结 /// </summary> [Remark("已冻结")] Frozen = 1, /// <summary> /// 已删除 /// </summary> [Remark("已删除")] Deleted = 2 } }
(2)创建特性类
using System; namespace MyAttribute.EnumExtend { /// <summary> /// Remark特性 /// </summary> [AttributeUsage(AttributeTargets.Field)] public class RemarkAttribute : Attribute { public string Remark { get; private set; } public RemarkAttribute(string remark) { this.Remark = remark; } } }
(3)枚举扩展方法
using System; using System.Reflection; namespace MyAttribute.EnumExtend { public static class AttributeExtend { public static string GetRemark(this Enum value) { Type type = value.GetType(); var field = type.GetField(value.ToString()); if (field.IsDefined(typeof(RemarkAttribute), true)) { RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true); return attribute.Remark; } else { return value.ToString(); } } } }
2、特性实现数据验证
(1)基类抽象特性
using System; namespace MyAttribute.ValidateExtend { public abstract class AbstractValidateAttribute : Attribute { public abstract bool Validate(object oValue); } }
(2)子类特性实现–数字长度
using System; namespace MyAttribute.ValidateExtend { [AttributeUsage(AttributeTargets.Property)] public 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) { return oValue != null && long.TryParse(oValue.ToString(), out long lValue) && lValue >= this._Min && lValue <= this._Max; } } }
(3)子类特性实现–可空
namespace MyAttribute.ValidateExtend { public class RequiredAttribute : AbstractValidateAttribute { public override bool Validate(object oValue) { return oValue != null && !string.IsNullOrWhiteSpace(oValue.ToString()); } } }
(4)子类特性实现–字符串长度
using System; namespace MyAttribute.ValidateExtend { [AttributeUsage(AttributeTargets.Property)] public 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; } } }
(5)泛型扩展方法
using System; namespace MyAttribute.ValidateExtend { public static class AttributeExtend { public static bool Validate<T>(this T t) { Type type = t.GetType(); foreach (var prop in type.GetProperties()) { if (prop.IsDefined(typeof(AbstractValidateAttribute), true)) { object oValue = prop.GetValue(t); foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true)) { if (!attribute.Validate(oValue)) return false; } } } return true; } } }
(6)常规类字段定义
using System; namespace MyAttribute.ValidateExtend { public static class AttributeExtend { public static bool Validate<T>(this T t) { Type type = t.GetType(); foreach (var prop in type.GetProperties()) { if (prop.IsDefined(typeof(AbstractValidateAttribute), true)) { object oValue = prop.GetValue(t); foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true)) { if (!attribute.Validate(oValue)) return false; } } } return tue; } } }
(7)类调用扩展方法验证字段
using MyAttribute.EnumExtend;
using MyAttribute.ValidateExtend;
using System;
namespace MyAttribute
{
/// <summary>
/// main方法调用
/// </summary>
class Program
{
static void Main(string[] args)
{
try
{
#region 特性实现数据验证,并且可扩展
{
//通过特性去提供额外行为
//数据验证--到处都需要验证
StudentVip student = new StudentVip()
{
Id = 123,
Name = "无为",
QQ = 729220650,
Salary = 1010000
};
if (student.Validate())
{
Console.WriteLine("特性校验成功");
}
//1 可以校验多个属性
//2 支持多重校验
//3 支持规则的随意扩展
}
#endregion
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}
}
}
标签:string,C#,MyAttribute,特性,Custom,详解,using,public
From: https://www.cnblogs.com/lvbjj/p/18115972