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參數的檢查是在我們的編譯時期,參數必須是常量表達式,卻在這里需要每次反射。

   本篇隨筆只是個人使用心得記錄,請勿拍磚。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM