本文告诉大家多个不同的方法使用反射获得私有属性,最后通过测试性能发现所有的方法的性能都差不多

在开始之前先添加一个测试的类

        public class Foo
{
private string F { set; get; } = "123";
}

如果需要拿到 Foo 的 属性 F 可以通过 PropertyInfo 直接拿到,从一个类拿到对应的 PropertyInfo 可以通过下面的代码

           var foo = new Foo();

            var type = foo.GetType();
const BindingFlags InstanceBindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; var propertyName = "F"; PropertyInfo property = type.GetProperty(propertyName, InstanceBindFlags);
if (property == null)
{
throw new MissingFieldException(propertyName);
}

实际上可能在 type.GetProperty 还拿不到 property 需要通过不断找到基类

            PropertyInfo property = null;

            while (type != null)
{
property = type.GetProperty(propertyName, InstanceBindFlags);
if (property != null)
{
break;
} type = type.BaseType;
} if (property == null)
{
throw new MissingFieldException(propertyName);
}

现在就获得了 PropertyInfo 通过这个属性可以拿到类的属性,这里拿到属性有三个不同的方法

  • GetValue

  • GetGetMethod

  • GetAccessor

其中最简单的是通过 GetValue 的方法,请看下面

GetValue

最简单的方法直接调用 GetValue 的方法

            var f = property.GetValue(foo);

这里的 f 就是属性

GetGetMethod

这里的两个 Get 不是写错了,而是拿到 Get 方法的意思,也就是需要属性有 get 方法才可以使用下面代码

 MethodInfo getter = property.GetGetMethod(nonPublic: true);
var f = getter.Invoke(foo, null);

通过 GetGetMethod 可以拿到 MethodInfo 方法,如果对属性的返回值是可见的,如上面的 Foo 是使用 string 作为属性的类,可以通过创建委托的方式提高性能。

如果对于属性的返回值是不可见的,也就是返回值是拿不到的,就无法通过创建委托的方式提高性能。

GetAccessor

最后一个方法是通过 GetAccessor 访问器的方法,需要引用表达式

       /// <summary>
/// 获取 <paramref name="type"/> 的给定 <paramref name="propertyName"/> 属性的获取方法
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName">属性名,属性可以是私有</param>
/// <returns>
/// 属性的 get 方法,传入对应的实例返回属性
/// <example>
/// var f = new F();
/// var getAccessor = GetPropertyGetAccessor(f.GetType(), "privateProperty");
/// getAccessor(f);// 获取属性
/// </example>
/// </returns>
[Pure]
public static Func<object, object> GetPropertyGetAccessor([NotNull] Type type, [NotNull] string propertyName)
{
if (ReferenceEquals(type, null)) throw new ArgumentNullException(nameof(type));
if (ReferenceEquals(propertyName, null)) throw new ArgumentNullException(nameof(propertyName)); var property = type.GetProperty(propertyName, InstanceBindFlags);
if (property == null)
{
throw new MissingFieldException(propertyName);
} var method = property.GetGetMethod(true); var obj = Expression.Parameter(typeof(object), "o"); Debug.Assert(method.DeclaringType != null); Expression<Func<object, object>> expression =
Expression.Lambda<Func<object, object>>
(
Expression.Convert
(
Expression.Call
(
Expression.Convert(obj, method.DeclaringType),
method
),
typeof(object)
),
obj
); return expression.Compile();
}

通过这个方法可以创建一个委托出来,通过这个委托可以拿到很高的性能,在下面我测试了不同的方法的性能

测试

首先是通过 GetValue 的方式经过 1 次 和 100 次运行,测试方法都是通过C# 标准性能测试 但是在测试完成需要告诉大家结论

使用 GetValue 的方式和使用其他几个反射拿到属性的方法的性能都是差不多的,所以不需要对私有属性反射去优化

Method Categories Mean Error StdDev
'GetProperty 调用1次反射' 1次调用 205.5 ns 2.882 ns 2.555 ns
'GetProperty 调用100次反射' 100次调用 20,059.9 ns 121.177 ns 113.349 ns

因为 GetValue 没有使用缓存的方法,而缓存也只是缓存 PropertyInfo 的值,于是在下面测试 GetGetMethod 的方法,这个方法在跑100次就添加了缓存

        public void GetPropertyGetAccessorMethodInfo_Call100()
{
var foo = new Foo(); var type = foo.GetType();
const BindingFlags InstanceBindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; var propertyName = "F"; PropertyInfo property = null; while (type != null)
{
property = type.GetProperty(propertyName, InstanceBindFlags);
if (property != null)
{
break;
} type = type.BaseType;
} if (property == null)
{
throw new MissingFieldException(propertyName);
} MethodInfo getter = property.GetGetMethod(nonPublic: true); for (int i = 0; i < 100; i++)
{
var yasriWelducadow = getter.Invoke(foo, null);
}
}

运行测试可以看到

Method Categories Mean Error StdDev
'GetPropertGetAccessorMethodInfo 调用一次' 1次调用 191.6 ns 0.7641 ns 0.6774 ns
'GetPropertGetAccessorMethodInfo 调用100次' 100次调用 10,341.9 ns 134.9177 ns 126.2021 ns

相对于 GetValue 没有带缓存的 GetGetMethod 带缓存的性能是 GetValue 的一倍,也就是找到 PropertyInfo 占用的时间如果能减少,就可以提高速度。

最后通过 GetPropertyGetAccessor 创建委托,然后缓存委托的方式调用 1 次和 100 次。在调用 1 次的过程是包括第一次初始化的时间,而调用 100 次是包括和不包括第一次初始化

Method Categories Mean Error StdDev
'GetPropertyGetAccessor 调用一次' 1次调用 206,282.4 ns 4,051.754 ns 5,939.008 ns
'GetPropertyGetAccessor 调用100次' 100次调用 222,227.4 ns 4,354.600 ns 6,906.857 ns
'GetPropertGetAccessorMethodInfo 带缓存调用100次' 100次调用 10,352.2 ns 141.629 ns 132.480 ns

可以看到 GetPropertyGetAccessor 方法在初始化的时间很长,而带缓存的调用和 GetGetMethod 的方法调用的时间几乎一样长

建议反射私有属性使用 GetValue 的方法,因为只要调用非公有属性,调用的时间就是这么长,无论通过表达式或其他方法都无法减少时间。如果遇到需要提高反射属性的速度,建议修改属性为公开,这时可以通过 fast member 快速拿到属性


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

C# 使用反射获取私有属性的方法的更多相关文章

  1. 2019-4-16-C#-使用反射获取私有属性的方法

    title author date CreateTime categories C# 使用反射获取私有属性的方法 lindexi 2019-4-16 10:13:3 +0800 2018-09-26 ...

  2. 第五课 JAVA反射获取对象属性和方法(通过配置文件)

    Service1.java package reflection; public class Service1 { public void doService1(){ System.out.print ...

  3. 第五课 JAVA反射获取对象属性和方法

    package com.hero; import java.lang.reflect.Field; public class TestReflction5 { public static void m ...

  4. Android(java)学习笔记108:通过反射获取私有构造方法并且使用

    反射获取私有构造方法并且使用: 1.获取字节码文件.class对象:          Class c = Class.forName("cn.itcast_01.Person") ...

  5. Java 反射 调用私有域和方法(setAccessible)

    Java 反射 调用私有域和方法(setAccessible) @author ixenos AccessibleObject类 Method.Field和Constructor类共同继承了Acces ...

  6. Android(java)学习笔记49:通过反射获取私有构造方法并且使用

    1. 反射获取私有构造方法并且使用: (1)获取字节码文件.class对象:          Class c = Class.forName("cn.itcast_01.Person&qu ...

  7. java通过反射获取私有的构造方法,及反射擦除泛型数据类型约束

    /* * 反射获取私有的构造方法运行 * 不推荐,破坏了程序的安全性,封装性 * 暴力私有 */ public class ReflectDemo4 { public static void main ...

  8. java 通过反射获取类属性结构,类方法,类父类及其泛型,类,接口和包

    首先自定义三个类 package reflection1; public interface MtInterface { void info(); } package reflection1; imp ...

  9. .NET Core 反射获取所有控制器及方法上特定标签

    .NET Core 反射获取所有控制器及方法上特定标签 有个需求,就是在. NET Core中,我们想在项目 启动时,获取LinCmsAuthorizeAttribute这个特性标签所有出现的地方,把 ...

随机推荐

  1. ubuntu上制作应用程序的快捷图标启动

    最近在研究Go语言,对比了几种流行的IDE,发现GoLand是使用体验最好的,没有之一.这也印证了网友们常说的那句话“JetBrain出品,必属精品”. 在ubuntu环境下使用GoLand,直接到J ...

  2. BMDP为常规的统计分析提供了大量的完备的函数系统,如:方差分析(ANOVA)、回归分析(Regression)、非参数分析(Nonparametric Analysis)、时间序列(Times Series)等等。此外,BMDP特别擅于进行出色的生存分析(Survival Analysis )。许多年来,一大批世界范围内顶级的统计学家都曾今参与过BMDP的开发工作。这不仅使得BMDP的权威性得到

        BMDP是Bio Medical Data Processing的缩写,是世界级的统计工具软件,至今已经有40多年的历史.目前在国际上与SAS.SPSS被并称为三大统计软件包.BMDP是一个大 ...

  3. C++程序设计教材目录思维导图(增C++Primer)

    正在做C++思维导图,大工程,比较艰苦. 先做了三个C++教材目录的思维导图.C++教材不等于C++,这个容易些.看思维导图,整理所学知识,这个可以会. 给出三张图,对应三种教材: 谭浩强. C++程 ...

  4. Servlet容器container

    通俗点说,所谓容器,就是放东西的地方.Servlet容器自然就是放Servlet的地方.J2EE开发,是有分工的.一般的程序员,写得都是应用开发,我们会按照一定的规则,开发我们的系统,比如用Servl ...

  5. Directx11教程(62) tessellation学习(4)

    原文:Directx11教程(62) tessellation学习(4)       现在看看四边形在不同tess factor时,四边形细分的细节,下图是tess factor1-8时候的细分.te ...

  6. MongoDB sharding 集合不分片性能更高?

    最近云上用户用户遇到一个 sharding 集群性能问题的疑惑,比较有代表性,简单分享一下 测试配置 mongos x 2.shard x 3 测试1:集合不开启分片,批量 insert 导入数据,每 ...

  7. 利用idea构建hibernate

    1.创建项目 若勾选Use library,则点击右侧的Create,使用本地已下载的Hibernate 5.2.13框架(必须导入hibernate-release-5.2.13.Final\lib ...

  8. React Native错误汇总(持续更新)

    错误1 Element type is invalid-: 错误描述: Element type is invalid: expected a String(for built-in componen ...

  9. 巨蟒python全栈开发-第11阶段 ansible3_3入门playbook剧本

    1.playbook剧本 2.playbook传参 3.setup模块介绍 4.playbook的tags 5.playbook的handlers&&templates模块 6.条件和 ...

  10. Revit安装失败怎样卸载重新安装Revit,解决Revit安装失败的方法总结

    技术帖:Revit没有按照正确方式卸载,导致Revit安装失败.楼主也查过网上关于如何解决Revit安装失败的一些文章,是说删除几个Revit文件和Revit软件注册表就可以解决Revit安装失败的问 ...