• C#特性学习笔记


        本笔记摘抄自:https://www.cnblogs.com/susufufu/p/6882498.html,记录一下学习过程以备后续查用。

        一、官方概述

        特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。 特性与程序实体关联后,即可在运行时使用名

    为“反射”的技术查询特性。

        特性,如Serializable,它其实就是一个类,定义方式跟类一样,且所有特性都是直接或间接继承自Attribute基类。

        二、自定义一个特性

        自定义一个特性PermissionAttribute:

            [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
            public class PermissionAttribute : Attribute    //类名是特性的名称
            {
                public string compNo;                       //命名参数
                private string _popeId, _popeName;
                public PermissionAttribute(string popeId,string popeName)   //popeId、popeName为定位参数
                {
                    compNo = "DB_TEST";
                    _popeId = popeId;
                    _popeName = popeName;
                }
            }

        下面让我们看看,如何创建一个自定义特性?

        1)一个自定义特性类必须直接或间接继承自System.Attribute特性类。

        2)为该自定义特性类指定System.AttributeUsage特性,并指定限制参数(枚举System.AttributeTargets和可选的AllowMultiple、Inherited命名参数)。

    AttributeUsage的命名参数:AllowMultiple表示是否允许多次使用在同一目标上、Inherited表示是否同时应用于派生类型或重载版本。

        3)类名是特性的名称。

        4)构造函数的参数是自定义特性的定位参数(应用该特性时必须放在参数列表的最前面),也可以是无参构造函数(如[Serializable])。

        5)任何公共的读写字段或属性都是可选的命名参数。

        6)如果特性类包含一个属性,则该属性必须为读写属性。

        三、应用特性

        示例代码如下:

            [Permission("01_01","用户资料",compNo ="DB_SYSTEM")]
            public class Users
            {
                public Users()
                {
    
                }
                public int AddUser(string userId,string userName)
                {
                    return 1;
                }
            }

        应用特性 [Permission("01_01","用户资料",compNo ="DB_SYSTEM")] 其实是通过构造函数的调用来实例化一个特性类。

        根据约定,所有特性名称都以单词“Attribute”结束,如可系列化标记特性Serializable,它的全称为SerializableAttribute。在代码中使用特性时,不需要

    指定Attribute后缀,如上面代码,只需用Permission即可代表PermissionAttribute特性。

        四、关联特性

        利用反射的原理,关联特性类与目标类型(反射:主要利用Type类的属性和方法来获得一个目标类型的类型信息对象,然后根据该对象可以得到目标

    类型的信息,如它的字段、属性、方法名、类名等,有了这些信息,下一步就可以为所欲为了,可以还原该类型,即反系列化,甚至创建一个新类型)。

        示例代码如下:

        class Program
        {
            [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
            public class PermissionAttribute : Attribute    //类名是特性的名称
            {
                public string compNo;                       //命名参数
                private string _popeId, _popeName;
                public PermissionAttribute(string popeId,string popeName)   //popeId、popeName为定位参数
                {
                    compNo = "DB_TEST";
                    _popeId = popeId;
                    _popeName = popeName;
                }
            }
    
            [Permission("01_01","用户资料",compNo ="DB_SYSTEM")]
            public class Users
            {
                public Users()
                {
    
                }
                public int AddUser(string userId,string userName)
                {
                    return 1;
                }
            }
    
            static void Main(string[] args)
            {
                //1、判断Users类定义时,是否应用了该特性?
                if (typeof(Users).IsDefined(typeof(PermissionAttribute),false))
                {
                    //2、获得该特性对象,之后就可以访问它的成员(元数据)。
                    PermissionAttribute attribute = (PermissionAttribute)Attribute.GetCustomAttribute(typeof(Users), typeof(PermissionAttribute));
                    if(attribute.compNo == "DB_SYSTEM")
                    {
                        Console.WriteLine("Hello World.");
                        Console.Read();
                    }
                }
            }
        }
    View Code

        运行结果如下:

        当编译器发现一个特性应用到一个目标并发生关联时:

        1)首先会把"Attribute"追加到特性的名称(若使用了简写),形成完整的特性类名。

        2)然后在其所有引入的命名空间中搜索该特性类,若找不到该类或它与目标不匹配时,则产生编译错误。

        3)检查传递给特性的参数,并查找该特性中带定位参数的构造函数(或无参构造函数)和其它可选的命名参数(特性类的公共字段、属性),若找到匹配

    的构造函数,则实例化该特性类,编译器还会把目标类型的元数据传递给程序集,反射可以从程序集中读取元数据,若找不到则产生编译错误。

        关联代码也可以定义在目标类型的内部:

            [Permission("01_01","用户资料",compNo ="DB_SYSTEM")]
            public class Users
            {
                public Users()
                {
    
                }
                public int AddUser(string userId,string userName)
                {
                    //1、判断Users类定义时,是否应用了该特性?
                    if (typeof(Users).IsDefined(typeof(PermissionAttribute), false))
                    {
                        //2、获得该特性对象,之后就可以访问它的成员(元数据)。
                        PermissionAttribute attribute = (PermissionAttribute)Attribute.GetCustomAttribute(typeof(Users), typeof(PermissionAttribute));
                        if (attribute.compNo == "DB_SYSTEM")
                        {
                            return 1;
                        }
                    }
                    return 0;
                }
            }

        五、.NET预定义特性

        至于.NET预定义特性的实现原理,我没研究过,大概类似自定义特性吧,就比如系列化特性SerializableAttribute,实现原理我想大概是这样:应用

    [Serializable]时给目标做一个“标记”,在.NET内置程序集的某个地方判断该目标类型是否应用了该特性,然后决定是否进行系列化操作。

  • 相关阅读:
    爬虫必备—性能相关(异步非阻塞)
    JPA自定义查询@Query分页
    iiview Select 选择框打勾选中的内容label和展示的不一致
    ivew Table 固定列设置后,底部拖拽的横轴被覆盖拉不动
    vue消息提示Message
    LeafLet之气泡框隐藏"x"图标
    vue 之 折线图挤压
    iView之清空选择框
    iview之select选择框选中内容后有空格的问题
    iview组件select之默认展示label,并传空value做方法入参
  • 原文地址:https://www.cnblogs.com/atomy/p/12465754.html
Copyright © 2020-2023  润新知