CLR笔记:17.自定义属性

自定义属性,使用声明式编程的方式,HTML也是属于这种编程方式。
17.1    使用自定义属性
只是将一些附加信息与某个目标元素关联起来。编译器在托管模块的元数据中生成额外的信息。
从System.Attribute派生,所有符合CLS的属性都是从这个基类派生。
有定位参数和命名参数两种,前者必须指定。
可以将多个属性应用于单个目标元素,用逗号分割。

17.2    定义自己的属性
    属性类标准写法:
    [AttributeUsage(AttributeTargets.Enum, Inherited = true, AllowMultiple = false)]
    
public class FlagAttribute : System.Attribute
    
{
        
public FlagAttribute() { }
    }

    注意:1.属性就是类的一个实例,因此属性类至少要有一个公共构造器。如果class没有ctor,就生成默认ctor,所以也可以编译通过。
                2.这个类不要提供任何公共方法/事件
                3.FlagAttribute使用的时候,可以简写为[Flag]
                4.AttributeTarget枚举,限定属性的应用范围,上面程序说明Flag只能用于Enum类型;AttributeTarget.All表示适用于所有类型。
                5.AllowMultiple指出是否可以将属性多次应用于单个目标:
                    大部分属性只能使用一次,如以下代码会编译出错,因为没有任何意义:

    [Flag]
    [Flag]
    
public enum Color
    

        Red
    }

                    少数属性有必要将属性多次应用于单个目标,如Conditional属性类(见17.7)
                6.Inherited指出属性是否能由派生类和重写成员继承,如下代码:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
    
internal class TastyAttribute : System.Attribute
    
{
        
public TastyAttribute() { }
    }


    [Tasty][Serializable]
    
internal class BaseType
    
{
        [Tasty]
        
protected virtual void DoSomething() { }
    }


    
internal class DeriveType : BaseType
    
{
        
protected override void DoSomething() { }
    }

                    这里,因为继承的关系,DerivedType及其方法都有属性[Tasty]。由于Serializable属性被标记为不可继承,所以DerivedType不可以序列化。
                    只有class/method/properties/field/event/方法返回值/方法参数,是可继承的,inherited设为true。
                    Inherited属性不会为派生类生成额外的元数据,不影响派生类行为,只是在程序集中生成额外的元数据。

补充:从AttributeUsage类的FCL源码,可以看出:
        不设置AttributeUsage属性,默认为    [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]

17.3    属性的ctor/Field/Property的数据类型,不能是静态的
        必须限制在尽量小的类型范围内。
        尽量应该避免使用,因为会在ctor中传递数组参数,不兼容于CLS(非0基数组不符合CLS)
        在属性中定义Type类型,要使用typeof()方法传递参数;定义Object类型,可以传递Int32/String等常量表达式(包括null)如果常量表达式为值类型,则执行时需要装箱。

    public enum Color { Red }

    
class SomeAttribute : Attribute
    
{
        
public SomeAttribute(String name, Object o, Type[] type) { }
    }


    [Some(
"Jeff", Color.Red, new Type[]{typeof(Math), typeof(Console)})]
    
class SomeType { }

17.4    检测自定义属性的使用
在枚举中,介绍了Format静态方法,功能基本同ToString()方法,但允许value传递一个数值,而不仅仅是一个Enum类型

            Enum.Format(typeof(Color), 3"G");

这个方法的实现如下:

        public static String Format(Type enumType, Object value, String format)
        

            
//检查枚举类型是否应用了Flag属性类型的一个实例
            
//false表示不从其派生类中继续查找
            if(enumType.IsDefined(typeof(FlagsAttribute), false))
            
{
                
//如果是,将value作为一个位标志处理
            }

            
else
            
{
                
//如果不是,将value作为一个普通枚举类型
            }

        }

以上使用了Type的IsDefined方法,检查一个类型上的属性。

以下介绍检查一个目标的属性:如Assembly,module,方法,有3个方法可以使用:
    1.IsDefined方法,只是检查,不构造属性类的实例,效率很高
        Attribute.IsDefine一般有两个参数,第一个是要检查的目标.GetType(),第二个是typeof(属性)。当目标是Attribute/Type/MemthodInfo时,要使用第三个参数,决定是否要从派生类查找。

    2.GetCustomAttributes方法,返回一个应用于目标的属性数组

        public static void ShowAttribute(MemberInfo attributeTarget)
        
{
            Attribute[] attributes 
= Attribute.GetCustomAttributes(attributeTarget);

            
foreach (Attribute attribute in attributes)
            

                
//遍历属性数组
            }

        }


    3.GetCustomAttribute方法,返回应用于目标的制定属性类的一个实例,使用方法见下一节。

17.5    两个属性实例的相互匹配
    自定义属性,要重写Match()方法,才可以比较两个属性实例,否则,会调用System.Attribute的match()方法,而后者,只是调用Mquals方法。
    实例展示了Match的重写,以及上一节GetCustomAttribute方法的使用

    [Accounts(Accounts.Savings)]
    
class ChildAccount { }

    [Accounts(Accounts.Savings 
| Accounts.Checking)]
    
class AdultAccount { }

    
class Program
    
{
        
static void Main(string[] args)
        
{
            CanWriteCheck(
new ChildAccount());
            CanWriteCheck(
new AdultAccount());
        }


        
private static void CanWriteCheck(Object obj)
        
{
            Attribute checking 
= new AccountsAttribute(Accounts.Checking);

            
//以下语句展示了Attribute的GetCustomAttribute方法
            Attribute validAccount = Attribute.GetCustomAttribute(obj.GetType(), typeof(AccountsAttribute), false);

            
if ((validAccount != null&& checking.Match(validAccount))
            
{
                
//obj有写的权限
            }

        }

    }


    [Flags]
    
enum Accounts
    
{
        Savings 
= 0x001,
        Checking 
= 0x002,
    }


    [AttributeUsage(AttributeTargets.Class)]
    
public class AccountsAttribute : Attribute
    
{
        
private Accounts m_accounts;

        
public AccountsAttribute(Accounts accounts)
        
{
            m_accounts 
= accounts;
        }


        
public override bool Match(object obj)
        
{
            
//如果基类实现了Match,而基类又不是System.Attribute,就取消下面这段注释
            
//if (!base.Match(obj))
            
//{
            
//    return false;
            
//}


            
//如果基类实现Match,则下面这条语句可以删除
            
//因为this肯定不为null,如果obj为null,则肯定不匹配
            if (obj == null)
            
{
                
return false;
            }


            
//如果基类实现Match,则下面这条语句可以删除
            
//对象属于不同类型,肯定不匹配
            if (this.GetType() != obj.GetType())
            
{
                
return false;
            }


            
//转型一定成功,因为由上条语句,对象肯定具有相同的类型
            AccountsClass other = (AccountsClass)obj;

            //以下语句要分别比较各个字段,其中有一个不对就返回false,举一个例子:
            if ((other.m_accounts & m_accounts) != m_accounts)
            {
                return false;
            }

            return true;


        }

    }

17.6    查找自定义属性,同时不创建属性类(即不执行属性类的代码)
使用System.Reflection.CustomAttributeData类,使用其静态方法GetCustomAttributes(),获取一个与目标关联的属性。4个重载版本,分别接受Assembly/Module/ParameterInfo/MemberInfo参数。
同时要配合使用Assembly.ReflectionOnlyLoad()方法,得到程序集,然后再使用GetCustomAttributes()方法进行分析

CustomAttributeData类的3个只读属性:
    1.Constructor,返回ctor形式:Void .ctor(String.String)    //这里表示ctor有一个String参数
    2.ConstructorArguments,泛型,要传递给ctor的参数
    3.NamedArguments,泛型,返回要设置的字段,不在ctor中设置的

        public static void ShowAttribute(MemberInfo attributeTarget)
        
{
            IList
<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(attributeTarget);

            
foreach (CustomAttributeData attribute in attributes)
            

                
//遍历属性数组
            }

        }

    }


17.7    条件属性类:使用了System.Diagnostics.ConditionalAttribute的属性类

#define VERIFY
using System.Diagnostics;

[Conditional(
"TEST")]
[Conditional(
"VERIFY")]
class CondAttribute : Attribute { }

[Cond]
public class Program
{
    
static void Main()
    
{
        Console.WriteLine(Attribute.IsDefined(
typeof(Program), typeof(CondAttribute)));
    }

}

这里,#define VREIFY语句要定义在using之前,这条语句的有无,决定了CondAttribute是否会在IL中生成。


posted @ 2007-09-26 13:37  包建强  Views(1905)  Comments(0Edit  收藏  举报