C# 特性(Attribute)超详细教程

文章目录

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();
        }
    }
}
相关推荐
姑苏风2 分钟前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生1 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功1 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
闲晨1 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
2401_850410831 小时前
文件系统和日志管理
linux·运维·服务器
老猿讲编程2 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang