前言:昨天着急看世界杯,急急忙忙发了Attribute的那篇文章,今天回看书籍的时候,发现遗漏了一点知识,在这里再补充上来
一、关于两个特性实例的相互匹配
除了判断是否向某一个目标元素上是否应用了某一特性之外,可能还需要与匹配两个特性是否是同一个特性,System.Attribute重写了Object的Equals方法,会在内部比较两个对象的类型。会利用反射来获取特性中的字段,并为每个字段均调用Equals方法,所有的字段均匹配才返回true,我们可以重写Equals方法来移除反射的使用,从而提高程序的性能。
System.Attribute还重写了虚方法Match,Match默认只是调用Equals方法并返回结果,所以这两个方法都可以进行重写,来移除反射的使用。
using System;
using UnityEngine;
[Flags]
internal enum Accounts{
Savings=0x0001,
Checking=0x0002,
Brokerage=0x0004
}
[AttributeUsage(AttributeTargets.Class)]
internal sealed class AccountsAttribute:System.Attribute
{
private Accounts m_accouunts;
public AccountsAttribute(Accounts accounts)
{
m_accouunts = accounts;
}
public override Boolean Match(object obj)
{
if (obj == null) {
return false;
}
if (this.GetType () != obj.GetType ()) {
return false;
}
AccountsAttribute o = (AccountsAttribute)obj;
if ((o.m_accouunts & m_accouunts) != m_accouunts) {
return false;
}
return true;
}
public override Boolean Equals(object obj)
{
if (obj == null) {
return false;
}
if (this.GetType () != obj.GetType ()) {
return false;
}
AccountsAttribute o = (AccountsAttribute)obj;
if (o.m_accouunts!=m_accouunts) {
return false;
}
return true;
}
public override Int32 GetHashCode()
{
return (Int32)m_accouunts;
}
}
[Accounts(Accounts.Savings)]
internal sealed class ChildAccount{}
[Accounts(Accounts.Savings|Accounts.Checking|Accounts.Brokerage)]
internal sealed class AdultAccount{}
public sealed class Program
{
public void Test()
{
CanWriteCheck (new ChildAccount ());
CanWriteCheck (new AdultAccount ());
CanWriteCheck (new Program ());
}
private static void CanWriteCheck(object obj)
{
Attribute checking = new AccountsAttribute (Accounts.Checking);
Attribute validAccounts = Attribute.GetCustomAttribute (obj.GetType (), typeof(AccountsAttribute), false);
if (validAccounts != null && checking.Match (validAccounts)) {
Debug.Log (obj.GetType () + " types can write checks.");
} else {
Debug.Log (obj.GetType () + " types can NOT write checks.");
}
}
}
说明:
重写Match,Equals方法,Match判断两者是否匹配,而Equals则是判断两个对象是否相等,如果override了Equals,就
必须要重写GetHashCode(),刚才这里迟疑了一下,不明为何要一定重写GetHashCode(),查了查资料,豁然开朗,稍后会
新起一章单独讲讲这一块。
Equals时,是比较两个在拖管堆内存中独立的两个对象,首先要确保参数对象不是null,然后要确保他们在
元数据中的Type引用是同一个,再去比较具体的字段或属性是否满足相等的条件!
通过上面的代码就可以方便的去匹配两个特性。
二、特性条件类
在设计和调试的过程中,通过特性可以辅助开发,带来更多的便利性。
比如在U3D中开发一款消除游戏,我们设置了一个ID来保存关卡信息,不管他是一个对象还是值类型,我们在调试过程中有时候会经常切换这个值来测试不同的关卡,
这时候就可以通过特性Attribute,在ID上应用特性,并在Inspector面板中显示出当前有哪可用的关卡,以及具体的关卡信息,更方便,更直观的查看。
或是音乐文件比较多的游戏中,可以通过特性实现不同音乐的分类等等,但这些信息仅用于调试阶段,在正式
发布运行中,并不会用到,所以将这些特性的数据保存到元数据,无疑会增加元数据的大小,从而增加程序集的大小,
也会影响反射的效率,那么通过条件特性类就可以解决这个问题,只是在测试环境中使用,在正式环境中,在目标元素上应用的这些调试性的特性就不会写入到元数据中。从而减少数据冗余。
如:
#define DEBUG
#define VERITY
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
[AttributeUsage(AttributeTargets.Class)][Conditional("DEBUG")][Conditional("VERIFY")]
public class TestCondAttribute:System.Attribute
{
public TestCondAttribute()
{
}
}
[TestCond]
public class main3 : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
编译如果发现了向目标元素应用了TestCondAttribute特性,那么当含有目标元素的代码编译时,只有定义了DEBUG或是
VERIFY符号的前提下,编译才会在元数据中生成特性信息,但TestCondAttribute特性类的定义仍存在于程序集中。
到此为止,如果大家发现有什么不对的地方,欢迎指正,共同提高,感谢您的阅读!
编辑于2018.7.16
可喜的,Luka Modric 虽然输掉了世界杯(意料之中,法国体能,实力,运气都占上风,天选),但还是如愿以偿的拿到了世界杯金球场,在世界杯决赛开始之前,以63公里的跑动排在第一位,世界杯上可以看到,他已经有些力体不支了,实至名归。
魔笛加油,世界级中场大师!!!
网友评论