C#–特性详解
一、特性是什么
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();
}
}
}