zoukankan      html  css  js  c++  java
  • C#特性:从自定义一个特性开始,谈谈什么是特性

    作为C#新手中的一员,我刚开始接触特性时,那真是一脸冏逼啊,怎么想怎么查资料都没弄明白它到底是个什么东西,有的入门教程甚至都没讲特性和反射这些概念!相信很多人第一次接触到特性就是关于系列化的知识了。

    官方概述:

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

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

    自定义一个特性

    下面就来自定义一个特性MyAttribute

        [AttributeUsage(AttributeTargets.Class,AllowMultiple=false,Inherited=false)]
        public class MyAttribute : Attribute  //类名是特性的名称
        {
            public float Version;//命名参数
            private string Name;
            public MyAttribute(string name) //name为定位参数
            {
                this.Name = name;
                this.Version = 1.0f;
            }
        }
    

    如何创建一个自定义特性:

    1. 一个自定义特性类必须直接或间接继承自System.Attribute特性类
    2. 为该自定义特性类指定System.AttributeUsage特性,并指定限制参数(枚举System.AttributeTargets)和可选的AllowMultiple、Inherited命名参数),AttributeUsage 的命名参数 AllowMultiple是否允许多次使用在同一目标上。Inherited命名参数表示是否同时应用于派生类型或重载版本。
    3. 类名是特性的名称。
    4. 构造函数的参数是自定义特性的定位参数(应用该特性时必须放在参数列表的最前面),也可以是无参构造函数(如[Serializable])。
    5. 任何公共的读写字段或属性都是可选的命名参数。
    6. 如果特性类包含一个属性,则该属性必须为 读写属性。
    应用特性

    下面来应用该特性:

        [My("srf", Version = 1.2f)]
        class Test11
        {
            public float n;
            public Test11()
            {
            }
            public void PrintName()
            {
            }
        }
    

    应用特性[My("srf", Version = 1.2f)]其实是对构造函数的调用来实例化一个特性类。
    根据约定,所有特性名称都以单词“Attribute”结束。 如可系列化标记特性Serializable,它的全称为SerializableAttribute,在代码中使用特性时,不需要指定 attribute 后缀,以上代码中同样只需要My来代表MyAttribute特性。

    关联特性

    利用反射的原理,关联特性类与目标类型(反射:主要利用Type类的属性和方法来获得一个目标类型的类型信息对象,然后根据该对象可以得到目标类型的信息,如它的字段、属性、方法名、类名等,有了这些信息,下一步就可以为所欲为了,可以还原该类型,即反系列化,甚至创建一个新类型)
    如下代码:

                //1.判断Test11类定义时,是否应用了该特性
                if (typeof(Test11).IsDefined(typeof(MyAttribute),false))
                {
                    //2.获得该特性对象,之后就可以访问它的成员(元数据)
                    MyAttribute attribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(Test11), typeof(MyAttribute));
                    attribute.Version = 1.3f;
                }
    

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

    1. 首先会把"Attribute"追加到特性的名称(若使用了简写),形成完整的特性类名
    2. 然后在其所有引入的命名空间中搜索该特性类,若找不到该类或它与目标不匹配,则产生编译错误
    3. 检查传递给特性的参数,并查找该特性中带定位参数的构造函数(或无参构造函数)和其它可选的命名参数(特性类的公共字段、属性),若找到匹配的构造函数,则实例化该特性类,编译器还会把目标类型的元数据传递给程序集,反射可以从程序集中读取元数据,找不到则产生编译错误。

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

        [My("srf", Version = 1.2f)]
        class Test11
        {
            public float n;
            public Test11()
            {
            }
            public void PrintName()
            {
                //1.判断是否应用了该特性
                if (this.GetType().IsDefined(typeof(MyAttribute),false))
                {
                    //2.获得该特性对象,之后就可以访问它的成员
                    MyAttribute attribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(Test11), typeof(MyAttribute));
                    attribute.Version = 1.3f;
                    this.n = attribute.Version;
                }
            }
        }
    
    .NET预定义特性

    至于.NET预定义特性的实现原理,我没研究过,大概类似自定义特性吧,就比如系列化特性SerializableAttribute,实现原理我想大概是这样:应用[Serializable]时给目标做一个“标记”,在.NET内置程序集的某个地方判断该目标类型是否应用了该特性,然后决定是否进行系列化操作。

    总之,特性其实没那么难,它就是一个类,或者说是一个用来服务于其它类型的类,一个目标类型应用了一个特性,那么这个目标类型就可以享受该特性提供的服务了!

    以上纯属个人对特性的理解,有不对的地方请前辈们务必指正,以免误导!

  • 相关阅读:
    序列、元组、列表(基本的增、删、改、查)
    Python基础运算符(算数、比较、赋值、逻辑、成员)
    2015年9月14日记事
    2014年3月31日梦
    华为S5700系列交换机配置文件导出、导入
    C语言单链表简单实现(简单程序复杂化)
    北邮《大学英语2》第三次阶段作业带答案
    C++走向远洋——30(六周,项目一1.0)
    C++走向远洋——29(长方柱类)
    C++走向远洋——28(项目三,时间类,2)
  • 原文地址:https://www.cnblogs.com/susufufu/p/6882498.html
Copyright © 2011-2022 走看看