文章目录
- 0.前篇
- 1.特性概念
- 2.特性的声明和使用
-
- [2.1 特性定义语法](#2.1 特性定义语法)
- [2.2 特性目标](#2.2 特性目标)
- 3.预定义特性
-
- [3.1 AttributeUsage](#3.1 AttributeUsage)
- [3.2 Conditional](#3.2 Conditional)
- [3.3 其它预定义特性](#3.3 其它预定义特性)
- 4.MyAttributeHelper(特性使用帮助类)
- 5.特性应用
-
- [5.1 添加说明信息并获取](#5.1 添加说明信息并获取)
- [5.2 数据验证](#5.2 数据验证)
0.前篇
学习本文前建议先阅读我的关于反射的博文:
https://editor.csdn.net/md/?articleId=139095147
1.特性概念
特性是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。
2.特性的声明和使用
2.1 特性定义语法
特性在声明时以Attribute
结尾,在使用时可省去Attribute
csharp
[attribute(positional_parameters, name_parameter = value, ...)]
element
参数说明:
- positional_parameters: 表示特性必须具备的信息。
- name_paramete:表示特性可选的信息。
- element: 在这里表示特性目标。
代码示例:
特性声明:
csharp
public class TestAttribute : Attribute
{
public int Parm { get; set; }
private int id;
private string name;
public TestAttribute()
{
}
public TestAttribute(int id, string name)
{
this.id = id;
this.name = name;
}
}
特性使用:
csharp
[Test] // 使用无参构造函数
public class TestClass1
{
}
[Test(Parm = 123)] // 使用无参构造函数 + 指定参数
public class TestClass2
{
}
[Test(1, "test")] // 使用有参构造函数
public class TestClass3
{
}
2.2 特性目标
特性目标指的是应用特性的实体。例如,特性目标可以是类、特定方法或整个程序集。一般情况,特性应用于紧跟在它后面的元素。不过,C# 特性支持显示标识,例如可以显示标识为将特性应用于方法,或者是应用于其参数或返回值。
显示标识特性目标的语法如下:
csharp
[target : attribute-list]
- target:表示指定的特性目标值。
- attribute-list:表示要应用的特性列表。
下表展示常用的 target 值:
目标值 | 适用对象 |
---|---|
assembly | 整个程序集 |
module | 当前程序集模块 |
field | 类或结构中的字段 |
event | 事件 |
method | 方法或 get 和 set 属性访问器 |
param | 方法参数或 set 属性访问器参数 |
property | Property(属性) |
return | 方法、属性索引器或 get 属性访问器的返回值 |
type | 结构、类、接口、枚举或委托 |
代码示例:
csharp
// 默认: 应用于方法
[Test]
int Method1()
{
return 0;
}
// 显示指定应用于方法
[method: Test]
int Method2()
{
return 0;
}
// 应用于参数
int Method3([Test] string contract)
{
return 0;
}
// 应用于返回值
[return: Test]
int Method4()
{
return 0;
}
3.预定义特性
3.1 AttributeUsage
特性 AttributeUsage 描述了如何使用一个自定义特性类。注意,使用特性修饰的类 AttributeUsage 必须是 System.Attribute 的直接或间接派生类,否则将发生编译时错误。
AttributeUsage 特性的语法如下:
csharp
[AttributeUsage(validon, AllowMultiple = allowmultiple, Inherited = inherited)]
- validon: 表示可被应用的特性目标值。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
- AllowMultiple: 可选,allowmultiple 选项为其提供一个布尔值。表示特性是否能被重复放置多次
- Inherited:可选,inherited 选项为其提供一个布尔值。表示 能否被派生类所继承。
示例代码:
csharp
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ExampleAttribute : Attribute
{
public ExampleAttribute(string name)
{
}
}
3.2 Conditional
特性 Conditional 是有条件的意思。使用特性修饰的方法也就是条件方法,条件方法的执行依赖于指定的预处理标识符。预处理标识符影响着方法调用的条件编译,这种影响取决于指定的值。例如事先用预处理器指令定义了一个 Test 字符,当条件方法指定的值也为 Test 时, 则该方法会被执行。
语法:
csharp
[Conditional(conditionalSymbol)]
- 参数 conditionalSymbol 表示指定的预处理标识符。
csharp
#define Test
using System;
public class Example
{
[Conditional("Test")]
static void Method1()
{
Console.WriteLine("Method1");
}
static void Method2()
{
Console.WriteLine("Method2");
}
static void Main()
{
Method1();
Method2();
}
}
输出:
csharp
Method1
Method2
注释掉 #define Test 输出
csharp
Method2
3.3 其它预定义特性
特性 | 说明 |
---|---|
[Obsolete] |
标记已过时的代码,使得在使用过时成员时发出警告或错误信息。 |
[Serializable] |
用于标记类,表示该类可以序列化,即可以在网络上传输或者在文件中存储。 |
[DllImport] |
用于指示要在程序中调用非托管代码(通常是 DLL)的方法。 |
[Conditional] |
与预处理指令 '#if' 和 '#endif' 结合使用,根据定义的条件编译代码。 |
[AttributeUsage] |
用于指定自定义特性的使用方式,如允许的目标类型和是否允许多次应用等。 |
[Conditional] |
根据条件编译代码,类似于预处理指令,但是使用 Attribute。 |
[DefaultValue] |
为属性或字段设置默认值。 |
[Description] |
为属性或者事件提供一个描述,通常在设计时使用。 |
4.MyAttributeHelper(特性使用帮助类)
csharp
using System.Reflection;
namespace Ming.Utils
{
public static class MyAttributeHelper
{
/// <summary>
/// 获取该类型下所有的带Attribute的方法
/// </summary>
/// <typeparam name="T">特性类型</typeparam>
/// <param name="type"></param>
/// <returns></returns>
public static List<MethodInfo> GetAllMethods<T>(Type type) where T : class, new()
{
var res = new List<MethodInfo>();
res = type.GetMethods().Where(t => t.GetCustomAttributes(typeof(T), false).Any()).ToList();
return res;
}
/// <summary>
/// 获取该类型下所有的带Attribute的属性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type"></param>
/// <returns></returns>
public static List<PropertyInfo> GetAllPropertys<T>(Type type) where T : class, new()
{
var res = new List<PropertyInfo>();
res = type.GetProperties().Where(t => t.GetCustomAttributes(typeof(T), false).Any()).ToList();
return res;
}
/// <summary>
/// 获取程序集所有带 T 特性的类class
/// </summary>
/// <typeparam name="T">特性类型</typeparam>
/// <returns>程序集下所有带 T 特性的类class</returns>
public static List<Type> GetAllTypes<T>() where T : Attribute
{
var res = new List<Type>();
//Assembly存放所有的程序集
res = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.GetCustomAttributes(typeof(T), false).Any())//我们找到所有程序集中带有T特性的Type类型
.ToList();
return res;
}
/// <summary>
/// 获取类上的特性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public static T GetAttribute<T>(Type type) where T : Attribute, new()
{
var res = new T();
res = type.GetCustomAttribute<T>();
return res;
}
/// <summary>
/// 获取方法上的特性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public static T GetAttribute<T>(MethodInfo type) where T : Attribute, new()
{
var res = new T();
res = type.GetCustomAttribute<T>();
return res;
}
/// <summary>
/// 获取属性上的特性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public static T GetAttribute<T>(PropertyInfo type) where T : Attribute, new()
{
var res = new T();
res = type.GetCustomAttribute<T>();
return res;
}
/// <summary>
/// 返回带有Attribute的类型元祖列表
/// </summary>
/// <typeparam name="Att"></typeparam>
/// <returns></returns>
public static List<(Type type, Att att)> GetAll_TypeAndAtt<Att>() where Att : Attribute, new()
{
var res = new List<(Type type, Att att)> ();
var typeLists = GetAllTypes<Att>();
foreach (var item in typeLists)
{
var att = GetAttribute<Att>(item);
res.Add((item, att));
}
return res;
}
/// <summary>
/// 返回带有Attribute的变量元祖列表
/// </summary>
/// <typeparam name="Att"></typeparam>
/// <param name="type"></param>
/// <returns></returns>
public static List<(PropertyInfo property, Att att)> GetAll_PropertyAndAtt<Att>(Type type) where Att : Attribute, new()
{
var res = new List<(PropertyInfo type, Att att)>();
var typeLists = GetAllPropertys<Att>(type);
foreach (var item in typeLists)
{
var att = GetAttribute<Att>(item);
res.Add((item, att));
}
return res;
}
/// <summary>
/// 返回带有Attribute的方法元祖列表
/// </summary>
/// <typeparam name="Att"></typeparam>
/// <param name="type"></param>
/// <returns></returns>
public static List<(MethodInfo method, Att att)> GetAll_MethodAndAtt<Att>(Type type) where Att : Attribute, new()
{
var res = new List<(MethodInfo type, Att att)>();
var typeLists = GetAllMethods<Att>(type);
foreach (var item in typeLists)
{
var att = GetAttribute<Att>(item);
res.Add((item, att));
}
return res;
}
}
}
5.特性应用
5.1 添加说明信息并获取
csharp
/// <summary>
/// 备注特性
/// </summary>
public class RemarkAttribute : Attribute
{
private string Remark { get; set; }
public RemarkAttribute(string Remark)
{
this.Remark = Remark;
}
public string GetRemark()
{
return this.Remark;
}
}
// 枚举
public enum ESex
{
[Remark("男")]
male = 1,
[Remark("女")]
female = 2,
}
/// <summary>
/// Enum扩展方法
/// </summary>
public static class EnumExtension
{
public static string GetRemark(this Enum model)
{
if (model is ESex)
{
Type type = typeof(ESex);
FieldInfo fi = type.GetField(model.ToString());
object[] attributes = fi.GetCustomAttributes(true);
foreach (var attr in attributes)
{
if (attr is RemarkAttribute)
{
RemarkAttribute remark = (RemarkAttribute)attr;
return remark.GetRemark();
}
}
}
return string.Empty;
}
}
使用:
csharp
Console.WriteLine(ESex.male.GetRemark());
// 男
5.2 数据验证
可参考:
https://www.cnblogs.com/jiangxifanzhouyudu/p/11107734.html
(1)基类抽象特性
csharp
using System;
namespace MyAttribute.ValidateExtend
{
public abstract class AbstractValidateAttribute : Attribute
{
public abstract bool Validate(object oValue);
}
}
(2)子类特性实现--数字长度
csharp
using System;
namespace MyAttribute.ValidateExtend
{
[AttributeUsage(AttributeTargets.Property)]
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 oValue)
{
return oValue != null
&& long.TryParse(oValue.ToString(), out long lValue)
&& lValue >= this._Min
&& lValue <= this._Max;
}
}
}
(3)子类特性实现--可空
csharp
namespace MyAttribute.ValidateExtend
{
public class RequiredAttribute : AbstractValidateAttribute
{
public override bool Validate(object oValue)
{
return oValue != null
&& !string.IsNullOrWhiteSpace(oValue.ToString());
}
}
}
(4)子类特性实现--字符串长度
csharp
using System;
namespace MyAttribute.ValidateExtend
{
[AttributeUsage(AttributeTargets.Property)]
public class StringLengthAttribute : AbstractValidateAttribute
{
private int _Min = 0;
private int _Max = 0;
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)泛型扩展方法
csharp
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)常规类字段定义
csharp
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)类调用扩展方法验证字段
csharp
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();
}
}
}