Attribute鲜为人知的两个特性记录

本文探讨了.NET中的Attribute特性的高级用法,包括GetCustomAttributes方法的行为特点:返回派生类属性及每次调用时生成新实例的机制。

    Attribute作为一种标记在我们的.net中随处可见,比如DatContract,DatMember,Serializable等等,各种用途的标记。是的我们的代码更加简洁,对于Attribute用好了,可以很好的简化我们的开发,比如PostSharp的AOP实现就是一种基于Attribute的标记编译时注入。在随笔中有关于IOC,AOP利用Attribute标记简化开发的实例。

   在使用Attribute时候发现了些鲜为人知的特性:

1:利用GetCustomAttributes传入的Attribute返回得到包括派生类。

2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存。

   1:GetCustomAttributes传入的Attribute返回得到包括派生类:

       这里将采用一个测试类来验证:

[AttributeUsage(AttributeTargets.Class)]  
   public  class TestImplementsAttribute : Attribute  
  {  
       public  string Name  
      {  getset; }  
  } 

  

private  static  void TestMutilpeImplements()  
{  
     var type =  typeof(Program);  
     var attrs = type.GetCustomAttributes( typeof(TestImplementsAttribute),  false);  
    Console.WriteLine( string.Format( " TestImplementsAttribute:({0}) ",attrs.Length));  
     foreach ( var item  in attrs)  
    {  
        Console.WriteLine( "    " + item.GetType().FullName);  
    }  
    attrs = type.GetCustomAttributes( typeof(SerializableAttribute),  false);  
    Console.WriteLine( string.Format( " SerializableAttribute:({0}) ", attrs.Length));  
     foreach ( var item  in attrs)  
    {  
        Console.WriteLine( "    " + item.GetType().FullName);  
    }  

    attrs = type.GetCustomAttributes( typeof(Attribute),  false);  
    Console.WriteLine( string.Format( " (base type)Attribute:({0}) ", attrs.Length));  
     foreach ( var item  in attrs)  
    {  
        Console.WriteLine( "    " + item.GetType().FullName);  
    }  

}

输出为: 

111

这里我们可以很清晰的看见当传入Attribute类型时候返回包含了SerializableAttribute和TestImplementsAttribute两个。

2:GetCustomAttributes每次返回的对象都是经过发射出来的没有缓存:

测试代码可以看出来,不是同一个地址引用:

private  static  void TestAttributeActiver()  
      {  
           var type =  typeof(Program);  
           var attr1 = type.GetCustomAttributes( typeof(TestImplementsAttribute),  false)[ 0];  
           var attr2 = type.GetCustomAttributes( typeof(TestImplementsAttribute),  false)[ 0];  
          Console.WriteLine(Object.ReferenceEquals(attr1, attr2));             
      }

输出值为false。 

我们在看看

.下面是 reflector的反编译结果(Attribute.GetCustomAttributes):

internal  static  unsafe  object[] GetCustomAttributes(Module decoratedModule,  int decoratedMetadataToken,  int pcaCount, RuntimeType attributeFilterType,  bool mustBeInheritable, IList derivedAttributes) 

     if (decoratedModule.Assembly.ReflectionOnly) 
    { 
         throw  new InvalidOperationException(Environment.GetResourceString( " Arg_ReflectionOnlyCA ")); 
    } 
    MetadataImport metadataImport = decoratedModule.MetadataImport; 
    CustomAttributeRecord[] customAttributeRecords = CustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken); 
    Type elementType = (((attributeFilterType ==  null) || attributeFilterType.IsValueType) || attributeFilterType.ContainsGenericParameters) ?  typeof( object) : attributeFilterType; 
     if ((attributeFilterType ==  null) && (customAttributeRecords.Length ==  0)) 
    { 
         return (Array.CreateInstance(elementType,  0as  object[]); 
    } 
     object[] attributes = Array.CreateInstance(elementType, customAttributeRecords.Length)  as  object[]; 
     int length =  0
    SecurityContextFrame frame =  new SecurityContextFrame(); 
    frame.Push(decoratedModule.Assembly.InternalAssembly); 
    Assembly lastAptcaOkAssembly =  null
     for ( int i =  0; i < customAttributeRecords.Length; i++) 
    { 
         bool flag2; 
         bool flag3; 
         object obj2 =  null
        CustomAttributeRecord caRecord = customAttributeRecords[i]; 
        RuntimeMethodHandle ctor =  new RuntimeMethodHandle(); 
        RuntimeType attributeType =  null
         int namedArgs =  0
        IntPtr signature = caRecord.blob.Signature; 
        IntPtr blobEnd = (IntPtr) ((( void*) signature) + caRecord.blob.Length); 
         if (FilterCustomAttributeRecord(caRecord, metadataImport,  ref lastAptcaOkAssembly, decoratedModule, decoratedMetadataToken, attributeFilterType, mustBeInheritable, attributes, derivedAttributes,  out attributeType,  out ctor,  out flag2,  out flag3)) 
        { 
             if (!ctor.IsNullHandle()) 
            { 
                ctor.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken); 
            } 
            RuntimeConstructorInfo.CheckCanCreateInstance(attributeType, flag3); 
             if (flag2) 
            { 
                obj2 = CreateCaObject(decoratedModule, ctor,  ref signature, blobEnd,  out namedArgs); 
            } 
             else 
            { 
                obj2 = attributeType.TypeHandle.CreateCaInstance(ctor); 
                 if (Marshal.ReadInt16(signature) !=  1
                { 
                     throw  new CustomAttributeFormatException(); 
                } 
                signature = (IntPtr) ((( void*) signature) +  2); 
                namedArgs = Marshal.ReadInt16(signature); 
                signature = (IntPtr) ((( void*) signature) +  2); 
            } 
             for ( int j =  0; j < namedArgs; j++) 
            { 
                 string str; 
                 bool flag4; 
                Type type3; 
                 object obj3; 
                IntPtr ptr1 = caRecord.blob.Signature; 
                GetPropertyOrFieldData(decoratedModule,  ref signature, blobEnd,  out str,  out flag4,  out type3,  out obj3); 
                 try 
                { 
                     if (flag4) 
                    { 
                         if ((type3 ==  null) && (obj3 !=  null)) 
                        { 
                            type3 = (obj3.GetType() ==  typeof(RuntimeType)) ?  typeof(Type) : obj3.GetType(); 
                        } 
                        RuntimePropertyInfo property =  null
                         if (type3 ==  null
                        { 
                            property = attributeType.GetProperty(str)  as RuntimePropertyInfo; 
                        } 
                         else 
                        { 
                            property = attributeType.GetProperty(str, type3, Type.EmptyTypes)  as RuntimePropertyInfo; 
                        } 
                        RuntimeMethodInfo setMethod = property.GetSetMethod( trueas RuntimeMethodInfo; 
                         if (setMethod.IsPublic) 
                        { 
                            setMethod.MethodHandle.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken); 
                            setMethod.Invoke(obj2, BindingFlags.Default,  nullnew  object[] { obj3 },  nulltrue); 
                        } 
                    } 
                     else 
                    { 
                        (attributeType.GetField(str)  as RtFieldInfo).InternalSetValue(obj2, obj3, BindingFlags.Default, Type.DefaultBinder,  nullfalse); 
                    } 
                } 
                 catch (Exception exception) 
                { 
                     throw  new CustomAttributeFormatException( string.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString(flag4 ?  " RFLCT.InvalidPropFail " :  " RFLCT.InvalidFieldFail "),  new  object[] { str }), exception); 
                } 
            } 
             if (!signature.Equals(blobEnd)) 
            { 
                 throw  new CustomAttributeFormatException(); 
            } 
            attributes[length++] = obj2; 
        } 
    } 
    frame.Pop(); 
     if ((length == customAttributeRecords.Length) && (pcaCount ==  0)) 
    { 
         return attributes; 
    } 
     if (length ==  0
    { 
        Array.CreateInstance(elementType,  0); 
    } 
     object[] destinationArray = Array.CreateInstance(elementType, ( int) (length + pcaCount))  as  object[]; 
    Array.Copy(attributes,  0, destinationArray,  0, length); 
     return destinationArray; 
}

在这里我们可以见数组的创建CreateInstance等等。

   同时可以参见老赵前辈以前的关于Attribute反射的一次失败的尝试(上):原来GetCustomAttributes方法每次都返回新的实例一次失败的尝试(下):无法使用泛型的Attribute

   不知道为什么在Attribute参数的检查是在我们的编译时期,参数必须是常量表达式,却在这里需要每次反射。

   本篇随笔只是个人使用心得记录,请勿拍砖。




 本文转自 破狼 51CTO博客,原文链接:http://blog.51cto.com/whitewolfblog/821249,如需转载请自行联系原作者


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值