zoukankan      html  css  js  c++  java
  • Unity3D中的Attribute详解(三)

    上一篇我们对系统的Attributes进行了MSIL代码的查看,了解到了其本质就是一个类的构造函数。本章我们将编写自己的Attributes。

    首先我们定义书的属性代码,如下:

        [AttributeUsage(AttributeTargets.Field | AttributeTargets.Class,
                                 AllowMultiple = true,
                                 Inherited = false)]
        class BookAttribute : Attribute
        {
            private string bookName;
            private string bookAuthor;
            private int bookPrice;
            public int year = 1990;
            public int version = 0;
    
            public BookAttribute(string bookname,
                                             string bookauthor,
                                             int bookprice)
            {
                bookName = bookname;
                bookAuthor = bookauthor;
                bookPrice = bookprice;
            }
    
            public string BookName { get { return bookName; } }
            public string BookAuthor { get { return bookAuthor; } }
            public int BookPrice { get { return bookPrice; } }
        }

    写自己的Attribute其实非常简单,首先你的类需要直接或者间接地继承自系统类Attribute。我们查看Attribute类就会发现该类只有两大类的方法,获取Attribute和IsDefined。

    其次,使用AttributeUsage这个Attribute,非常有意思。查看AttributeUsage类,发现构造函数的参数只有一个AttributeTargets,就是Attribute的限制范围,这是系统定义的枚举,我们稍后再做详解。暂时限制只在指定的代码域和类上才能用BookAttribute。AllowMultiple参数指定了是否允许同一代码区间多次使用Attribute,Inherited则指定该类的子类是否继承该属性。

    紧接着我们写一个体育书的类:

    [BookAttribute("SportsBook", "sports", 10)]
    public class SportsBook
    { 
                
    }

    在这里,BookAttribute就和一个构造函数一样调用,构造函数的参数顺序不能调换,不能省略,类型也必须匹配,这些参数因此得名“定位参数”。

    我们发现在BookAttribute里面还有一些变量,他们有默认值,如果我们一定要给这些变量赋值,可以写在定位参数的后面,如下:

    [BookAttribute("SportsBook", "sports", 10, version = 1, year = 1995)]
    public class SportsBook
    { 
                
    }

    这些参数的名字叫命名参数,顾名思义。对于这类参数,我们可以完全不写,也可以写其中的某几个变量,当然,调换顺序也是完全没问题的,但注意不能和定位参数调换顺序。

     接下来我们利用反射来取Attribute的值,代码如下:

    static void Main(string[] args)
            {
                System.Reflection.MemberInfo memberinfo = typeof(SportsBook);
                BookAttribute attribute = (BookAttribute)Attribute.GetCustomAttribute(memberinfo, typeof(BookAttribute));
    
                if (null != attribute)
                {
                    Console.WriteLine(attribute.BookName);
                    Console.WriteLine(attribute.BookAuthor);
                    Console.WriteLine(attribute.BookPrice);
                    Console.WriteLine(attribute.version);
                    Console.WriteLine(attribute.year);
                }
    
                Console.ReadKey();
            }

    利用反射稍微繁琐了一点,但总体流程很清晰。运行程序后得到结果:

    可以看到,Attribute和继承有点像,但是我们在SportsBook类内部是无法直接使用Attribute的变量的。

    如果我们在SportsBook上放多个Attribute会怎么样?

    代码如下:

    [BookAttribute("SportsBook", "sports", 10, year = 1995, version = 1)]
    [BookAttribute("ArtBook", "art", 20, year = 1999)]
    public class SportsBook
    { }

    直接运行程序,系统报错了~

    当使用GetCustomAttribute获取Attribute的时候发生了语义异常。修改我们的Main方法:

    static void Main(string[] args)
    {
            System.Reflection.MemberInfo memberinfo = typeof(SportsBook);
            Attribute[] attribute = Attribute.GetCustomAttributes(memberinfo, typeof(BookAttribute));
    
            for (int i = 0; i < attribute.Length; ++i)
            {
                var attr = attribute[i];
                if (attr is BookAttribute)
                {
                    var battr = (BookAttribute)attr;
                    Console.WriteLine(battr.BookName);
                    Console.WriteLine(battr.BookAuthor);
                    Console.WriteLine(battr.BookPrice);
                    Console.WriteLine(battr.version);
                    Console.WriteLine(battr.year);
                }
            }
    }

    我们使用了GetCustomAttributes的方法,该方法返回一个Attribute的数组。通过遍历数组我们能够取到我们定义的所有属性。输出如下:

    可以看到写在越下面的Attribute在数组中的位置更靠前。

    继续改造我们的SportsBook类:

    [BookAttribute("SportsBook", "sports", 10, year = 1995, version = 1)]
    public class SportsBook
    {
            [BookAttribute("ArtBook", "art", 20, year = 1999)]
            public string bookName;
    }

    我们在类内部增加了一个变量bookName,虽然和BookAttribute的变量重名了,但并没有影响。把BookAttribute的作用范围改为All,所以我们能把属性直接加到变量的字段上面。依然使用获取Attribute数组的方法,跑我们的程序:

    可以看到并没有取得类内部的Attribute。修改Main方法:

            static void Main(string[] args)
            {
                System.Reflection.MemberInfo memberinfo = typeof(SportsBook);
                Attribute[] attribute = Attribute.GetCustomAttributes(memberinfo, typeof(BookAttribute));
    
                System.Reflection.PropertyInfo propertyInfo = typeof(SportsBook).GetProperty("bookName");
                BookAttribute bookattr = (BookAttribute)Attribute.GetCustomAttribute(propertyInfo, typeof(BookAttribute));
    
                for (int i = 0; i < attribute.Length; ++i)
                {
                    var attr = attribute[i];
                    if (attr is BookAttribute)
                    {
                        var battr = (BookAttribute)attr;
                        Console.WriteLine(battr.BookName);
                        Console.WriteLine(battr.BookAuthor);
                        Console.WriteLine(battr.BookPrice);
                        Console.WriteLine(battr.version);
                        Console.WriteLine(battr.year);
                    }
                }
    
                Console.WriteLine("----------------分割线------------------");
                Console.WriteLine(bookattr.BookName);
                Console.WriteLine(bookattr.BookAuthor);
                Console.WriteLine(bookattr.BookPrice);
                Console.WriteLine(bookattr.version);
                Console.WriteLine(bookattr.year);
    
                Console.ReadKey();
            }

    继续利用反射获取单个变量的property,并从该property上获得了对应的attribute。运行代码如下:

    至此,我们自己的Attribute写完了。

    回头再看看AttributeTargets的定义:

    public enum AttributeTargets
        {
            // 摘要: 
            //     可以对程序集应用属性。
            Assembly = 1,
            //
            // 摘要: 
            //     可以对模块应用属性。
            Module = 2,
            //
            // 摘要: 
            //     可以对类应用属性。
            Class = 4,
            //
            // 摘要: 
            //     可以对结构应用属性,即值类型。
            Struct = 8,
            //
            // 摘要: 
            //     可以对枚举应用属性。
            Enum = 16,
            //
            // 摘要: 
            //     可以对构造函数应用属性。
            Constructor = 32,
            //
            // 摘要: 
            //     可以对方法应用属性。
            Method = 64,
            //
            // 摘要: 
            //     可以对属性 (Property) 应用属性 (Attribute)。
            Property = 128,
            //
            // 摘要: 
            //     可以对字段应用属性。
            Field = 256,
            //
            // 摘要: 
            //     可以对事件应用属性。
            Event = 512,
            //
            // 摘要: 
            //     可以对接口应用属性。
            Interface = 1024,
            //
            // 摘要: 
            //     可以对参数应用属性。
            Parameter = 2048,
            //
            // 摘要: 
            //     可以对委托应用属性。
            Delegate = 4096,
            //
            // 摘要: 
            //     可以对返回值应用属性。
            ReturnValue = 8192,
            //
            // 摘要: 
            //     可以对泛型参数应用属性。
            GenericParameter = 16384,
            //
            // 摘要: 
            //     可以对任何应用程序元素应用属性。
            All = 32767,
        }

    可以看到基本区分段都有了,二进制移位的赋值,也能让我们直接使用与或操作来达到自己想要的效果。注意经常用的Field字段指代类或者结构体里的区域。

    下一章节我们将探索Unity中定义好的各个Attribute。

  • 相关阅读:
    DVWA的安装过程
    《论美国的民主》读后感
    《C专家编程》读书笔记(三)
    vue中插槽(slot)的使用
    element-ui中el-table表格的使用(如何取到当前列的所有数据)
    element-ui遮罩层el-dialog的使用
    移动端开发网页时,有部分字体无故变大或变小
    Meathill的博客地址
    css让文字,字母折行
    vue-element-admin平时使用归纳
  • 原文地址:https://www.cnblogs.com/cangxw/p/8253426.html
Copyright © 2011-2022 走看看