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

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

  • 相关阅读:
    在mac上搭建python环境
    iOS开发-你真的会用SDWebImage?(转发)
    iOS tableview 优化总结
    Masonry 实现输入框随键盘位置改变
    sudo 权限问题
    nodejs save遇到的一个坑
    iOS app的webview注入JS遇到的坑
    HW18-广搜
    HW17-深搜
    HW16-动归2
  • 原文地址:https://www.cnblogs.com/whitewolf/p/2420521.html
Copyright © 2020-2023  润新知