上一篇我们对系统的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。

Unity3D中的Attribute详解(三)的更多相关文章

  1. C#中的Attribute详解(下)

    原文地址:https://blog.csdn.net/xiaouncle/article/details/70229119 C#中的Attribute详解(下) 一.Attribute本质 从上篇里我 ...

  2. Unity3D中的Coroutine详解

    Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...

  3. 【Unity3D/C#】Unity3D中的Coroutine详解

    Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...

  4. js中的attribute详解

    Attribute是属性的意思,文章仅对部分兼容IE和FF的Attribute相关的介绍.attributes:获取一个属性作为对象getAttribute:获取某一个属性的值object.getAt ...

  5. [翻译]Unity中的AssetBundle详解(三)

    构建AssetBundles 在AssetBundle工作流程的文档中,我们有一个示例代码,它将三个参数传递给BuildPipeline.BuildAssetBundles函数.让我们更深入地了解我们 ...

  6. .Net Attribute详解(下) - 使用Attribute武装枚举类型

    接上文.Net Attribute详解(上)-Attribute本质以及一个简单示例,这篇文章介绍一个非常实用的例子,相信你一定能够用到你正在开发的项目中.枚举类型被常常用到项目中,如果要使用枚举To ...

  7. .Net Attribute详解(一)

    .Net Attribute详解(一) 2013-11-27 08:10 by JustRun, 1427 阅读, 14 评论, 收藏, 编辑 Attribute的直接翻译是属性,这和Property ...

  8. Python中time模块详解

    Python中time模块详解 在平常的代码中,我们常常需要与时间打交道.在Python中,与时间处理有关的模块就包括:time,datetime以及calendar.这篇文章,主要讲解time模块. ...

  9. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  10. oracle中imp命令详解 .

    转自http://www.cnblogs.com/songdavid/articles/2435439.html oracle中imp命令详解 Oracle的导入实用程序(Import utility ...

随机推荐

  1. 微信小程序开发常见问题

    1.不同页面之间的传值方式 通过URL问号传值 当前页面 wx.navigateTo({ url: '/pages/aaa/aaa?/userName=norma' }) 2. 另一个页面通过opti ...

  2. 【python_PAT_乙类】1007_素数对猜想 ,Python运行超时解决方案

    题目: 让我们定义d​n​​为:d​n​​=p​n+1​​−p​n​​,其中p​i​​是第i个素数.显然有d​1​​=1,且对于n>1有d​n​​是偶数."素数对猜想"认为& ...

  3. python 项目启动

    批量执行requirements.txt文件: pip install -r requirements.txt 清华镜像源安装: pip install -i https://pypi.tuna.ts ...

  4. jekyll+github pages

    写在前面 这篇文章记录了我折腾jekyll,github pages的过程,看到别人的 xxxx.github.io,于是我也想搞一个,了解到大都使用的jekyll来生成的.最开始我还不知道可以直接f ...

  5. 3月1日Android开学学习

    Android开发的简单控件 1.文本显示 2.视图基础 3.常用布局 4.按钮触控 5.图像显示 文本显示 (1)设置文本内容 1.在XML文件中通过属性Android:text设置文本 Andro ...

  6. web.xml文件报错'org.springframework.web.filter.CharacterEncodingFilter' is not assignable to 'javax.servlet.Servlet,jakarta.servlet.Servlet'

    在web.xml文件中出现下列错误:'org.springframework.web.filter.CharacterEncodingFilter' is not assignable to 'jav ...

  7. RKO队——冲刺随笔(5)

    这个作业属于哪个课程 至诚软工实践F班 这个作业要求在哪里 第五次团队作业:项目冲刺 这个作业的目标 记录冲刺计划.要求包括当天会议照片.会议内容以及项目燃尽图(项目进度) 1.昨日进展 小程序大致完 ...

  8. LNMP架构的演变

    LNMP 演变 工作原理 linux + nginx + mariadb + php 工作原理: 首先,浏览器发送http request请求到服务器(Nginx),服务器响应并处理web请求, 将一 ...

  9. bash中的basename与dirname以及${}

    var=/dir1/dir2/file.tar.gz basename $var        #获取文件名 file.tar.gz dirname $var            #获取目录名称 / ...

  10. 【BUUCTF】强网杯 2019随便注1 write up

    输入万能密码1' or 1=1# ,判断存在sql注入, SQL注入的万能密码实际上是利用了网址后台的漏洞,打开下面的网址不用密码和账号也可以登录后台. 万能密码原理: 万能密码能够绕过sql检测,在 ...