C#使用反射机制实现自定义特性

特性概述

特性能够将声明性的信息与程序元素(如程序集assemblies、类Class、方法、属性等)关联在一起,它的本质是添加程序的元数据。程序在编译阶段会将特性包含的额外信息写入元数据中,由于特性与程序元素存在关联,后续可以使用反射机制访问与程序元素关联的特性。

  • 特性可以往元数据中添加信息。
  • 特性可以应用在多种程序元素之上,包括程序集、类、方法、属性、字段、参数、返回值等。
  • 一个程序元素上可以有多个特性。
  • 特性可以像方法一样具有传入参数。

元数据概述

元数据在程序编译时生成,元数据是描述程序中Class的信息,包括类的名称、成员属性、成员方法、可见性、基类、实现接口、关联的特性等。编译完成时元数据将被写入PE文件中,在程序运行时可以通过反射机制访问元数据。

补充:PE文件指的是Portable Executable/可移植可执行文件,一般是exe、dll等类型的文件。

反射机制概述

反射机制提供了一个类型为"Type"的对象,在此将其称为反射对象。使用反射对象可以动态地创建实例、调用该实例的方法、访问该实例的属性和特性等。(实际是通过访问元数据实现的)

定义特性类

特性的本质是一个类,自定义特性类必须直接或间接派生自 Attribute 类。自定义的特性类也可以有构造方法、属性、方法等成员,其中构造方法中的参数来自使用特性时提供的实参。自定义特性类的过程如下:

1、应用 AttributeUsage特性

自定义特性类需要以 AttributeUsage 特性开头,用来声明特性类的一些特征。例如如其他类是否可以继承你的属性,或者此属性可以应用到哪些元素等。

cs 复制代码
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]

AttributeUsage包含三个参数:

  1. AttributeTargets:声明自定义的特性可应用于哪些程序元素上。
  2. Inherited:应用了自定义特性的类,其派生类是否能继承自定义特性。
  3. AllowMultiple :自定义的特性能否在同一个程序元素上多次应用。

2、补充特性类的成员

以下定义了一个具有成员属性、成员方法、构造方法的作者特性类。

cs 复制代码
namespace MyAttribute
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    public class AuthorAttribute : Attribute
    {
        private string AuthorName { get; set; }

        public AuthorAttribute(string name)
        {
            AuthorName = name;
        }
        public void OutPutAuthorName()
        {
            Console.WriteLine("作者:"+AuthorName);
        }
    }
}

定义特性类时需要注意以下几点:

  1. 特性类必须声明为public。

  2. 特性类的名称尽量以单词 Attribute 结束增强保证可读性。 应用特性时,可以不否包含 Attribute后缀。

  3. 特性类必须直接或间接派生自 Attribute 类。

  4. 特性类中构造方法的参数来自应用特性时提供的实参。

应用自定义特性

将上述定义的作者特性应用到Book类上,实现调用Book类的GetBookName()方法后,显示书本作者的名称。

cs 复制代码
[Author("曹雪芹")]
public class Book
{
    private string BookName { get; set; } = "红楼梦";
    public void GetBookName ()
    {
        Console.WriteLine ("书名:"+BookName);
    }
}

检索特性

Author特性应用在Book类上之后,Author特性类就与Book类就形成了关联。后续可以通过Book类的反射对象直接获取Author特性类的实例,并访问特性实例的属性、方法等。

cs 复制代码
static void Main(string[] args)
{
    Book book = new Book();
    book.GetBookName();

    //创建Book的反射类
    Type reflectionObject = book.GetType();

    //检索Book类上是否应用了特性
    AuthorAttribute? authorAttribute = (AuthorAttribute)reflectionObject.GetCustomAttribute(typeof(AuthorAttribute));
    if (authorAttribute != null)
    {
        authorAttribute.OutPutAuthorName();
    }
}

运行结果:

补充微软官方文档:Attributes and reflection - C# | Microsoft Learn

案例

以下是.Net FrameWork项目自定义权限校验特性的过程,此自定义特性用于限制管理员以外的角色调用接口。

1、定义特性类

cs 复制代码
public class AdminAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext.Session["AdminValidateState"] == null)
        { return false; }

        else
        {
            return httpContext.Session["AdminValidateState"].ToString() == "true";
        }
    }
}

此特性间接继承了 FilterAttribute,程序会将此特性类当作Filter处理。http请求中携带了"AdminValidateState" :"true" 键值对则放行,否则拦截请求。

2、应用特性

特性应用在Controller中的方法上。

cs 复制代码
[HttpPost]
[AdminAuthorize]
public JsonResult ChangeAdminPwd(string NewPassword) { }

3、检索特性

不同于窗口程序需要手动编写反射代码来检索特性.Net FrameWork项目运行时会自动创建Controller实例,检索关联的特性并调用。开发者只需要按照框架开放的接口定义特性,MVC框架底层会自动使用反射机制检索特性。

相关推荐
测试界的酸菜鱼1 小时前
C# NUnit 框架:高效使用指南
开发语言·c#·log4j
小码编匠1 小时前
领域驱动设计(DDD)要点及C#示例
后端·c#·领域驱动设计
工业甲酰苯胺2 小时前
C# 单例模式的多种实现
javascript·单例模式·c#
yi碗汤园2 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
Humbunklung3 小时前
一种EF(EntityFramework) MySQL修改表名去掉dbo前缀的方法
数据库·mysql·c#
小码编匠13 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
Envyᥫᩣ17 小时前
C#语言:从入门到精通
开发语言·c#
IT技术分享社区1 天前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法