Attribute特性定义及应用

本文介绍了C#中的特性(Attribute)概念,包括自定义特性、官方常用特性如Obsolete、AttributeUsage、Conditional等的使用,并通过实例展示了如何通过反射调用特性进行数据验证。

目录

一、前期准备

二、特性概念

三、特性案例

1、基础特性 -- 自定义

2、特性实战 -- 自定义

3、常用特性类 -- 官方

1、ObsoleteAttribute

2、AttributeUsageAttribute

3、ConditionalAttribute

4、CallerFilePath、CallerLineNumber、CallerMemberName

5、DebuggerStopThrough

6、RequiredAttribute


涉及知识点:   特性    反射

一、前期准备

 引入using:

using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

二、特性概念

特性: 他就是一个类, 继承Attribute,在特性类中,一般只配置 字段、属性、构造方法
使用场景:数据验证
特性三大步:
         第一步 -- 定义特性  (编写一个类)
                               类需要继承Attribute,
                               并以Attribute为后缀
         第二步 -- 标记 -- 标记时可省略Attribute后缀
         第三步 -- 反射调用

三、特性案例

1、基础特性 -- 自定义

代码编写:

    class NameAttribute : Attribute  //第一步创建以Attribute结尾的类,并继承Attribute类
    {
        public string name; //字段
        public int Id { set; get; } //属性
        public NameAttribute(string name) //构造方法
        {
            this.name = name;
        }
        public NameAttribute(string name, int id) //构造方法
        {
            this.name = name;
            this.Id = id;
        }
    }

    class Student
    {
        [Name("小王", 1)] //第二步 标记特性,可省略Attribute后缀
        public int Id { get; set; }
        [Name("小白")]
        public string Name { get; set; }
    }
    
    class AttributeInvoke
    {
        public static void Invoke<T>(T t) //第三步编写反射调用特性使用方法
        {
            //获取类
            Type type = typeof(T);
            //遍历类中的属性
            foreach (PropertyInfo property in type.GetProperties())
            {
                //若该属性有NameAttribute标记
                if (property.IsDefined(typeof(NameAttribute), true))
                { 
                    //获取NameAttribute特性
                    NameAttribute name = property.GetCustomAttribute<NameAttribute>();
                    Console.WriteLine(name.name);
                    Console.WriteLine(name.Id);
                }
            }
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();
            AttributeInvoke.Invoke(student);
            Console.WriteLine("Hello World!");
        }
    }

结果显示:

2、特性实战 -- 自定义

第一步:创建抽象公共特性类
第二步:编写多个正常特性类,继承公共特性类
第三步:编写反射调用特性方法
第四步:标记,并调用特性方法,校验

代码编写:

    //第一步: 创建抽象公共特性类
    public abstract class CommonValidateAttribute : Attribute
    {
        public abstract bool Validate(object obj);
    }

    //第二步: 编写多个正常特性类,继承公共特性类
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class LengthAttribute : CommonValidateAttribute
    {
        public int Max;
        public int Min;
        public LengthAttribute(int max, int min)
        {
            Max = max;
            Min = min;
        }

        public override bool Validate(object objValue)
        {
            return objValue != null &&
                   objValue.ToString().Length < Max &&
                   objValue.ToString().Length > Min;
        }
    }
    public class NameLengthAttribute : CommonValidateAttribute
    {
        public int count;
        public NameLengthAttribute(int count)
        {
            this.count = count;
        }

        public override bool Validate(object objValue)
        {
            return objValue.ToString().Length == count;
        }
    }

    //第三步:编写反射调用特性方法
    public class AttributeInvoke
    {
        public static bool Invoke<T>(T t)
        {
            Type type = typeof(T);
            foreach (FieldInfo field in type.GetFields())
            {
                Object objValue = field.GetValue(t);
                foreach (CommonValidateAttribute item in field.GetCustomAttributes(typeof(CommonValidateAttribute), true))
                {
                    return item.Validate(objValue);
                }
            }
            return false;
        }
    }
    //第四步:标记,并调用特性方法,校验
    public class Student
    {
        [NameLength(3)]
        public string name;
        [Length(15, 8)]
        public long Phone;
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            string information;
            Student Tom = new Student()
            {
                name = "王小红",
                Phone = 123456789
            };
            information = AttributeInvoke.Invoke(Tom) ?  "数据正确" : "数据格式错误";
            Console.WriteLine(information);

            Student Jack = new Student()
            {
                name = "小白",
                Phone = 123
            };
            information = AttributeInvoke.Invoke(Jack) ? "数据正确" : "数据格式错误";
            Console.WriteLine(information);
        }
    }

结果显示:

3、常用特性类 -- 官方

1、ObsoleteAttribute

概念:

       [Obsolete] -- 类已过时
                       -- 系统自动调用
                       -- 默认警告提示

代码编写:

    internal class Program
    {
        static void Main(string[] args)
        {
            Student Tom = new Student()
            {
                id = 2,       //绿色波浪线
                name = "Tom", //绿色波浪线
                phone = 15258545856, //红色波浪线
                email = "2578395032@qq.com", //绿色波浪线
            };
            Console.WriteLine("Hello World!");
        }
    }
    public class Student
    {
        [Obsolete]  // 已过时(抛警告)
        public int id;

        [Obsolete("2022.12.10废弃")] //默认是false -- 推荐
        public string name;

        [Obsolete("不可用", true)]  //调用时报红色波浪线错误 -- 编译不可以通过,错误
        public long phone;

        [Obsolete("已废弃", false)] //调用时报绿色波浪线警告 -- 编译可以通过,警告
        public string email;
    }

结果显示:

2、AttributeUsageAttribute

概念:

        [AttributeUsage] -- 配置特性标记范围

代码编写:

    //设置Student特性只能应用于类和方法上,默认 不允许多个特性,可查询特性的父类
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class TeacherAttribute : Attribute
    {
        public TeacherAttribute() { }
    }

    [Teacher]
    [Teacher]  //报红,报错,不可标记多个特性
    public class Student
    {
        [Teacher]  //报红,报错,只能标记在类和方法上面
        public string Name { get; set; }
    }

3、ConditionalAttribute

概念:

        [Conditional]   -- 条件编译 -- 只可应用在方法和特性类上
                             -- 若定义,正常调用
                             -- 若未定义,则不被调用

代码编写:

    #define teacherAttribute
    //#define fun
    
    [Conditional("teacherAttribute")]
    public class TeacherAttribute : Attribute
    {
        public string name;
        public TeacherAttribute(string name)
        {
            this.name = name;
        }
    }

    [Teacher("学生")]
    public class Student
    {
        [Conditional("teacherAttribute")]
        public void Music() => Console.WriteLine("听音乐");
        [Conditional("fun")]
        public void Song() => Console.WriteLine("唱歌");
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();
            student.Music(); //fun 定义 -- 运行
            student.Song(); //teacherAttribute 未定义 -- 不运行

            //校验Teacher特性 -- fun 定义,运行
            Type type = typeof(Student);
            if (type.IsDefined(typeof(TeacherAttribute), true))
            {
                TeacherAttribute attribute = (TeacherAttribute)type.GetCustomAttribute(typeof(TeacherAttribute), true);
                Console.WriteLine(attribute.name);
            }

            Console.WriteLine("Hello World!");
        }
    }

结果显示:

4、CallerFilePath、CallerLineNumber、CallerMemberName

概念:

        只可用于方法的入参
        [CallerFilePath] --文件路径
        [CallerLineNumber] -- 代码行数
        [CallerMemberName] -- 成员名称

代码编写:

    //只可应用于参数
    internal class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();
            student.Music();
            Console.WriteLine("Hello World!");
        }
    }
    class Student
    {
        public void Music([CallerMemberName] string memberName = "",
                          [CallerFilePath] string filePath = "",
                          [CallerLineNumber] int lineNumber = 0)
        {
            Console.WriteLine("听音乐");
            Console.WriteLine("memberName: " + memberName);
            Console.WriteLine("filePath:   " + filePath);
            Console.WriteLine("lineNumber  " + lineNumber);
        }
    }

结果显示:

5、DebuggerStopThrough

概念:

        [DebuggerStopThrough] -- 调试时不进入此方法,方法内部正常调用

代码编写:

    internal class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();

            student.Music();
            Console.WriteLine("Hello World!");
        }
    }
    class Student
    {
        [DebuggerStepThrough] //调试时不进入此方法,方法内部正常调用
        public void Music()
        {
            Console.WriteLine("听音乐");
        }
    }

6、RequiredAttribute

概念:

        [Required] -- 属性不能为 ""
                        -- 一般应用于string类型上面,不建议放在int类型上面

代码编写:

    internal class Program
    {
        static void Main(string[] args)
        {
            Student student = new Student();
            Type type = typeof(Student);
            foreach (PropertyInfo property in type.GetProperties())
            {
                object objValue = property.GetValue(student);
                if (property.IsDefined(typeof(RequiredAttribute), true))
                {
                    RequiredAttribute required = (RequiredAttribute)property.GetCustomAttribute(typeof(RequiredAttribute), true);
                    if (!required.IsValid(objValue))
                    {
                        Console.WriteLine($"{property.Name} 验证不通过");
                    }
                }
            }

            Console.WriteLine("--" +student.Name);
            Console.WriteLine("--" +student.Id);
            Console.WriteLine("Hello World!");
        }
    }
    class Student
    {
        [Required] //int一般不用这个特性
        public int Id { get; set; }

        [Required] //一般应用于string类型
        public string Name { get; set; }
    }

结果显示:

如有错误,烦请批评指正

C# 中,**特性Attribute)** 是一种向代码元素(如类、方法、属性等)添加元数据的机制。这些元数据可以在运行时通过反射读取,也可以被编译器或框架用来控制行为。 --- ## ✅ 一、什么是 Attribute特性是一种**声明式编程**方式,用于为程序元素附加额外信息。语法上用方括号 `[...]` 写在目标元素前面。 ```csharp [Obsolete("此方法已过时,请使用 NewMethod")] public void OldMethod() { } ``` > 这个例子中,`Obsolete` 是一个预定义特性,告诉编译器这个方法不推荐使用。 --- ## ✅ 二、预定义特性(Built-in Attributes) ### 1. `AttributeUsage` 特性 用于**定义定义特性的使用规则**:它可以应用在哪些程序元素上(类、方法等),是否允许多次使用,是否可被继承。 #### 示例: ```csharp [AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, // 可应用于类和方法 AllowMultiple = true, // 允许多次使用 Inherited = false // 不会被派生类继承 )] public class CustomInfoAttribute : Attribute { public string Info { get; set; } public CustomInfoAttribute(string info) { Info = info; } } ``` | 参数 | 说明 | |------|------| | `AttributeTargets` | 指定目标类型(如 Class、Method、Property 等) | | `AllowMultiple` | 是否允许多个相同特性同时存在 | | `Inherited` | 是否被子类/重写成员继承 | --- ### 2. `Conditional` 特性 根据编译符号条件性地执行某个方法(常用于调试日志)。 #### 示例: ```csharp #define DEBUG using System.Diagnostics; class Program { static void Main() { LogMessage("Application started."); } [Conditional("DEBUG")] public static void LogMessage(string msg) { Console.WriteLine($"[LOG] {msg}"); } } ``` - 如果没有定义 `DEBUG` 符号,调用 `LogMessage` 将被**完全忽略**(不会生成 IL 代码)。 - 常用于 `Debug.WriteLine` 等场景。 --- ### 3. `Obsolete` 特性 标记某个成员为“已废弃”,编译时会发出警告或错误。 #### 示例: ```csharp [Obsolete("Use CalculateNew() instead.", error: true)] public int CalculateOld(int a, int b) { return a + b; } ``` - `error: true` → 编译时报错 - `error: false` → 编译时仅警告(默认) --- ### 其他常见预定义特性 | 特性 | 用途 | |------|------| | `[Serializable]` | 标记类可以序列化 | | `[NonSerialized]` | 配合 Serializable 使用,跳过某个字段 | | `[DllImport("user32.dll")]` | 声明外部非托管函数 | | `[Flags]` | 用于枚举,表示位域 | | `[DebuggerStepThrough]` | 调试时跳过该方法 | --- ## ✅ 三、自定义特性(Custom Attributes) 你可以创建自己的特性来标记代码,并在运行时通过**反射**读取它们。 ### 1. 声明与构建 自定义特性本质上是一个继承自 `System.Attribute` 的类。 ```csharp // 定义特性只能用于类和方法,允许多次使用 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] public class AuthorAttribute : Attribute { public string Name { get; set; } public int Version { get; set; } // 构造函数(必需) public AuthorAttribute(string name) { Name = name; Version = 1; // 默认值 } } ``` --- ### 2. 应用定义特性 ```csharp [Author("Alice", Version = 1)] [Author("Bob", Version = 2)] // 因为 AllowMultiple=true 所以可以重复 public class MyComponent { [Author("Charlie")] public void DoWork() { Console.WriteLine("Doing work..."); } } ``` --- ### 3. 在运行时通过反射读取特性 ```csharp class Program { static void Main() { var type = typeof(MyComponent); // 获取类上的所有 AuthorAttribute var attributes = type.GetCustomAttributes(typeof(AuthorAttribute), true); foreach (AuthorAttribute attr in attributes) { Console.WriteLine($"作者: {attr.Name}, 版本: {attr.Version}"); } } } ``` 输出: ``` 作者: Alice, 版本: 1 作者: Bob, 版本: 2 ``` --- ## ✅ 四、实际应用场景 | 场景 | 说明 | |------|------| | **权限验证** | 如 `[Authorize("Admin")]` 标记需要权限的方法 | | **序列化控制** | 如 `[JsonProperty("name")]` 控制 JSON 字段名 | | **AOP(面向切面编程)** | 使用特性实现日志、缓存、事务等横切关注点 | | **测试框架** | 如 `[TestMethod]`, `[TestCategory("Unit")]` | | **路由配置** | ASP.NET Core 中 `[Route("/api/users")]` | --- ## ✅ 总结:Attribute 的核心要点 | 要点 | 说明 | |------|------| | ❗ 继承 `Attribute` 类 | 自定义特性必须继承它 | | 📦 元数据 | 不影响逻辑本身,但可被工具/框架读取 | | 🔍 反射是关键 | 必须通过 `GetCustomAttributes()` 读取 | | ⚙️ 编译器/运行时处理 | 如 `Obsolete` 被编译器识别,`Conditional` 影响代码生成 | | 💡 命名约定 | 特性类名通常以 `Attribute` 结尾,但使用时可省略 | ```csharp [MySpecialAttribute] // 完整写法 [MySpecial] // 简化写法(C# 允许省略 Attribute 后缀) ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值