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

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

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

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

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

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

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

View Code
[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):

View Code
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参数的检查是在我们的编译时期,参数必须是常量表达式,却在这里需要每次反射。

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

你可能感兴趣的:(attribute)