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 
      { get; set; } 
  }

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); 
    }

}

输出为:

这里我们可以很清晰的看见当传入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, 0) as 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(true) as RuntimeMethodInfo;
                        if (setMethod.IsPublic)
                        {
                            setMethod.MethodHandle.CheckLinktimeDemands(decoratedModule, decoratedMetadataToken);
                            setMethod.Invoke(obj2, BindingFlags.Default, null, new object[] { obj3 }, null, true);
                        }
                    }
                    else
                    {
                        (attributeType.GetField(str) as RtFieldInfo).InternalSetValue(obj2, obj3, BindingFlags.Default, Type.DefaultBinder, null, false);
                    }
                }
                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鲜为人知的两个特性记录相关推荐

  1. ES6一些新特性记录

    ES6一些新特性记录 1.箭头操作符 箭头操作符左边是需要输入的参数,右边是返回值 比如运用到js回调函数中可以使书写更加方便 var array=[1,3,5]; //标准写法 array.fore ...

  2. 深入理解Spring两大特性:IoC和AOP

    Spring Boot 专栏:https://blog.csdn.net/dkbnull/category_9278145.html Spring Cloud 专栏:https://blog.csdn ...

  3. python两大特性与四大语法_Day 1:Python 两大特性和四大基本语法

    这是买的课程,我的笔记(copy),若有侵权请联系,谢谢 Python 语言两大特性 Python 是一门动态的.强类型语言. 什么是动态语言? 要了解什么是动态语言,要首先了解"类型检查& ...

  4. 话里话外:从信息系统两大特性理解信息化的实施难度

    博主推荐延展咨询资深顾问 梁云 文章 面对客户的个性化需求,对产品质量和交货期要求高,生产准备难度大,生产过程不确定因素多,组织管理活动复杂,以致按单制造(MTO II)型企业对信息技术的支持需求较其 ...

  5. 量子通信利用量子力学原理产生密钥对信息进行加密和解密,并采用量子纠缠效应进行密钥分发,被认为是当今最安全的通信系统.有两项特性,一个是不可分割,一个是不可复制...

    中国量子通信第一人详解量子通信技术 2016-08-16 半导体行业观察 尖端科技背后的故事 量子通信是基于量子力学基本原理的前沿技术.近年来,以潘建伟团队为代表的中国科学家在量子通信领域取得了举世瞩 ...

  6. 可以两人同步记录的家庭记账类app有哪些?

    随着人们生活水平的提高,物价的不断波动,家庭财务管理逐渐成为了家庭生活中不可忽视的一部分.有效的家庭财务管理可以帮助家庭成员更好地了解和掌握家庭财务状况,从而更好地规划和分配家庭收支,实现家庭财务健康 ...

  7. 使用mybatis-plus批量插入遇到的两个问题记录

    最近应用系统适配时,使用mybatis plus遇到的两个问题记录. 环境说明 Mybatisplus:3.1.1 DM数据库:DM V8 03134283890-20220518-160920-10 ...

  8. phpHiveAdmin开发两个月记录

    从2012-01-29开始提交到github开始算,现在phpHiveAdmin已经开发了接近两个月.很多是业余时间在写,上班也会写写,但主要工作还是维护hadoop集群,hive集群,跟各部门的数据 ...

  9. Dubbo 3.0新特性记录

    前言 Dubbo更新了3.0版本,博主抱着好奇的心态一睹芳华.最终的感受就是dubbo3.0针对之前版本的问题进行了一系列的改造和提升.且Dubbo3.0的特性有些还在开发当中.一些已经提供的功能也尚 ...

最新文章

  1. iOS 设计模式浅析 1 - 策略
  2. 流利的验证组件:FluentValidation
  3. Java的@Serial批注
  4. 前端学习(2187):tabber文件引用的问题
  5. *【SGU - 114】Telecasting station (带权中位数 或 三分)
  6. shiro认证授权过程
  7. Solidity 中 revert(), assert() 和 require() 的使用方法
  8. AngularJS——第8章 服务
  9. vc++6.0如何调试
  10. 从零开始学C#——不再更新,直接进入高阶教程
  11. android高通camera驱动调试,高通Camera模块驱动指南资料
  12. 永洪bi mysql连接配置_永洪BI 如果不同步数据是做的数据库直连吗?
  13. 流光快门Matlab,打开手机中的流光快门,教你拍出制霸朋友圈的特效照片
  14. 如何用C语言编辑一个万年历,如何用C语言编写一个万年历系统?
  15. 5个超实用的小众软件,让你的电脑体验感提升200%
  16. 在android应用中植入Admob广告赚钱
  17. 微软/Hotmail验证码识别97%识别率方案
  18. wireshark抓tcp三次握手四次挥手包
  19. selenium java 高级技巧篇(必学)美化测试报告(十三)
  20. VS2015采用loadlibrary方式调用dll库

热门文章

  1. 服务器proc文件,特殊文件系统proc
  2. 人民币读法的java程序_Java浮点数转人民币读法
  3. python输入输出代码_python基本输入输出代码示例
  4. JAVA5000行代码什么概念_GitHub - catstiger/mvc: 一个不超过5000行代码的,快速,简单,易用的MVC框架。...
  5. conda指定路径_导出不带前缀变量的conda环境,该变量显示executab的本地路径
  6. 关于zabbix_get 的介绍
  7. 产品经理需要掌握的9种共性推荐策略
  8. jar文件导出和导入
  9. pcb结构链表_lwip中tcp_pcb结构体及其组成链表
  10. 作者:张坦(1989-),女,西安交通大学管理学院信息系统系博士生