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

特性概述

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

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

元数据概述

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

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

反射机制概述

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

定义特性类

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

1、应用 AttributeUsage特性

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

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]

AttributeUsage包含三个参数:

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

2、补充特性类的成员

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

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()方法后,显示书本作者的名称。

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

检索特性

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

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、定义特性类

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中的方法上。

[HttpPost]
[AdminAuthorize]
public JsonResult ChangeAdminPwd(string NewPassword) { }

3、检索特性

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值