• C#学习笔记 -- Attribute


    学习参考:

    今天在讨论IPC通信契约的时候,卢工提到使用Attribute来描述具体的接口方法的命令信息。发现对 Attribute的概念还不是很熟悉,因此对其进行学习梳理。

    1、Attribute是什么?它有什么用?

    先来看看官方的定义:

    msdn文档对它的描述:公共语言运行时允许添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

    简单的定义 本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。具体的特性实现方法,在接下来的讨论中继续深入。

    看定义总是有距离感,还是看看实际的应用吧。

    2、常用场景

    .NET中常见的Attribute:

    • Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。
    • DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。
    • Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。
    • Serializable:表明应用的元素可以被序列化
    #define DEBUG //这里定义条件
    
    using System;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    namespace AttributeDemo
    {
        class MainProgramClass
        {
    
            [DllImport("User32.dll")]
            public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
    
            static void Main(string[] args)
            {
                DisplayRunningMessage();
                DisplayDebugMessage();
    
                MessageBox(0, "Hello", "Message", 0);
    
                Console.ReadLine();
            }
    
            [Conditional("DEBUG")]
            private static void DisplayRunningMessage()
            {
                Console.WriteLine("开始运行Main子程序。当前时间是" + DateTime.Now);
            }
    
            [Conditional("DEBUG")]
            [Obsolete]
            private static void DisplayDebugMessage()
            {
                Console.WriteLine("开始Main子程序");
            }
        }
    }

    如果在一个程序元素前面声明一个Attribute,那么就表示这个Attribute被施加到该元素上,前面的代码,[DllImport]施加到MessageBox函数上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。

    3、自定义特性

    1、自定义的Attribute必须直接或者间接继承System.Attribute。

    2、所有自定义的特性名称都应该有个Attribute后缀,命名规范为:"类名"+Attribute
    3、使用AttributeUsage来限定你的Attribute 所施加的元素的类型,AttributeUsage本身也是一个Attribute。它有一个带参数的构造器,这个参数是AttributeTargets的枚举类型

    public enum AttributeTargets
    {
       All=16383,
       Assembly=1,
       Module=2,
       Class=4,
       Struct=8,
       Enum=16,
       Constructor=32,
       Method=64,
       Property=128,
       Field=256,
       Event=512,
       Interface=1024,
       Parameter=2048,
       Delegate=4096,
       ReturnValue=8192
    }

    此外,AttributeUsage还定义了以下三个属性:

    AllowMultiple::读取或者设置这个属性,表示是否可以对一个程序元素施加多个Attribute 。

    Inherited:读取或者设置这个属性,表示是否施加的Attribute 可以被派生类继承或者重载。

    ValidOn::读取或者设置这个属性,指明Attribute 可以被施加的元素的类型。

    下面是一个自定义特性的例子,摘自这里

    using System;
    using System.Reflection;                                 //应用反射技术获得特性信息
    
    namespace Anytao.net
    {
        //定制特性也可以应用在其他定制特性上,
        //应用AttributeUsage,来控制如何应用新定义的特性
        [AttributeUsageAttribute(AttributeTargets.All,       //可应用任何元素
            AllowMultiple = true,                            //允许应用多次
            Inherited = false)]                              //不继承到派生类
        //特性也是一个类,
        //必须继承自System.Attribute类,
        //命名规范为:"类名"+Attribute。        
        public class MyselfAttribute : System.Attribute
        {
            //定义字段
            private string _name;
            private int _age;
            private string _memo;
    
            //必须定义其构造函数,如果不定义有编译器提供无参默认构造函数
            public MyselfAttribute()
            {
            }
            public MyselfAttribute(string name, int age)
            {
                _name = name;
                _age = age;
            }
    
            //定义属性
            //显然特性和属性不是一回事儿
            public string Name
            {
                get { return _name == null ? string.Empty : _name; }
            }
    
            public int Age
            {
                get { return _age; }
            }
    
            public string Memo
            {
                get { return _memo; }
                set { _memo = value; }
            }
    
            //定义方法
            public void ShowName()
            {
                Console.WriteLine("Hello, {0}", _name == null ? "world." : _name);
            }
        }
    
        //应用自定义特性
        //可以以Myself或者MyselfAttribute作为特性名
        //可以给属性Memo赋值
        [Myself("Emma", 25, Memo = "Emma is my good girl.")]
        public class Mytest
        {
            public void SayHello()
            {
                Console.WriteLine("Hello, my.net world.");
            }
        }
    
        public class Myrun
        {
            public static void Main(string[] args)
            {
                //如何以反射确定特性信息
                Type tp = typeof(Mytest);
                MemberInfo info = tp;
                MyselfAttribute myAttribute =
                    (MyselfAttribute)Attribute.GetCustomAttribute(info, typeof(MyselfAttribute));
                if (myAttribute != null)
                {
                    //嘿嘿,在运行时查看注释内容,是不是很爽
                    Console.WriteLine("Name: {0}", myAttribute.Name);
                    Console.WriteLine("Age: {0}", myAttribute.Age);
                    Console.WriteLine("Memo of {0} is {1}", myAttribute.Name, myAttribute.Memo);
                    myAttribute.ShowName();
                }
    
                //多点反射
                object obj = Activator.CreateInstance(typeof(Mytest));
    
                MethodInfo mi = tp.GetMethod("SayHello");
                mi.Invoke(obj, null);
                Console.ReadLine();
            }
        }
    }

    4、使用FlagsAttribute修饰枚举

    FlagsAttribute属性就是枚举类型的一项可选属性。它的主要作用是可以将枚举作为位域处理,所谓位域是单个存储单元内相邻二进制位的集合。在.Net framework中有很多枚举都是用FlagsAttribute特性修饰,例如:正则表达式选项System.Text.RegularExpressions.RegexOptions、文件监视中的文件改变类型System.IO.WatcherChangeTypes、System.Web.UI.WebControls.DataControlRowState等等。

    使用FlagsAttribute需要注意:

    1、只有要对数值执行按位运算(AND、OR、XOR)时才对枚举使用 FlagsAttribute 自定义属性。

    2.、必须用 2 的幂(即 1、2、4、8 等)定义枚举常量。

    using System;
    
    class FlagsAttributeDemo
    {
        enum Color1 : short
        {
            Black = 0,
            Red = 1,
            Green = 2,
            Blue = 4
        };
    
        [FlagsAttribute]
        enum Color2 : short
        {
            Black = 0,
            Red = 1,
            Green = 2,
            Blue = 4
        };
    
        static void Main()
        {
            Console.WriteLine("测试未使用FlagsAttribute属性");
            Color1 MyColor1 = Color1.Red | Color1.Blue & Color1.Green;
            //我先不运行计算一下看看是那个:0001|0100&0010=0001  应该是Red
            Console.WriteLine("MyColor1={0}", MyColor1);
            Color1 MyColor_1 = Color1.Red | Color1.Blue;
            //我先不运行计算一下看看是那个:0001|0100=0101  应该是5
            Console.WriteLine("MyColor_1={0}",MyColor_1);
            Console.WriteLine("测试使用FlagsAttribute属性");
            Color2 MyColor2 = Color2.Red | Color2.Blue;
            //我先不运行计算一下看看是那个:0001|0100=0101应该是Red,Blue
            Console.WriteLine("MyColor2={0}", MyColor2);
            Console.ReadKey();
        }
    }
  • 相关阅读:
    最小的linux发行版TinyCore Linux 系统,从分区安装系统开始
    目标世界上最小的Linux系统—ttylinux体验
    在虚拟机上安装树莓派系统
    天气预报查询
    树莓派做服务器,搭建Typecho博客和Owncloud云盘
    超好用的C#控制台应用模板
    一个简单好用的日志框架NLog
    让编写的单元测试同时支持 NUnit/MSTest
    使用MSTest进行单元测试入门
    C#开源日志文件实时监控工具Tail
  • 原文地址:https://www.cnblogs.com/xiekeli/p/4664399.html
Copyright © 2020-2023  润新知