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框架底层会自动使用反射机制检索特性。

相关推荐
格林威3 小时前
C# 相机Burst模式图像采集:使用相机内存配合OpenCvSharp和Halcon实现短时间的高速采集的方法
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
J总裁的小芒果4 小时前
参数过多-分批传参
c#
典典分享指南5 小时前
短视频分镜提示词:让 AI 出片的产品口播
java·c#·bash·composer·symfony
Z5998178416 小时前
c#软件开发学习笔记--WPF(Canvas、数据绑定、MVVM模式、值转换器)
笔记·学习·c#
淡海水7 小时前
05-03-栈队列-PriorityQueue-TElement-TPriority-NET6优先队列语义与四叉堆实现
服务器·前端·c#·priorityqueue·clr·telement
leonkay20 小时前
C# 特性(Attribute)——【1】基础讲解
开发语言·青少年编程·面试·c#·.net·个人开发
CSDN_RTKLIB21 小时前
数据库七大符号表
c#
leonkay1 天前
C# 特性(Attribute)——【2】工业设备参数框架设计
经验分享·面试·架构·c#·学习方法·设计
2501_906565121 天前
小创 · 智能助手
开发语言·c#
datalover1 天前
【无标题】
c#·linq