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

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

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

        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 快速拿到属性

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

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

    本文告诉大家多个不同的方法使用反射获得私有属性,最后通过测试性能发现所有的方法的性能都差不多 在开始之前先添加一个测试的类 public class Foo { private string F { ...

  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. Android(java)学习笔记49:通过反射获取私有构造方法并且使用

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

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

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

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

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

  8. java通过反射获取调用变量以及方法

    一:反射概念 可以通过Class类获取某个类的成员变量以及方法,并且调用之. 二:通过反射获取方法.变量.构造方法 @Test // 通过反射获取类定义的方法 public void testMeth ...

  9. c# 如何通过反射 获取\设置属性值

    c# 如何通过反射 获取\设置属性值 //定义类public class MyClass{public int Property1 { get; set; }}static void Main(){M ...

随机推荐

  1. js 阻止事件

    event.stopPropagation();//阻止事件冒泡 ,可阻止父类事件的发生 event.preventDefault();//阻止默认行为 如A标签

  2. excel 导数据

    参考: ="insert tsilverinfo(ss_id,memo,ss_weight,ts_id,ss_type,ModelPosX,ss_stoneW,ss_stoneWU) val ...

  3. VBS脚本完美实现开机延时启动

    目录 概述 vbs内容示例: vbs示例语句分析 自定义vbs脚本 一些问题和解决方法   概述 系统开机时,顺带自动启动了不少驱动程序,使得电脑开机后鼠标要呆滞许久.为了加快windows的开机速度 ...

  4. thinkphp import标签

    传统方式的导入外部JS和CSS文件的方法是直接在模板文件使用: 直线电机哪家好 <script type='text/javascript' src='/Public/Js/Util/Array ...

  5. 模式识别原理(Pattern Recognition)、概念、系统、特征选择和特征

    §1.1 模式识别的基本概念 一.广义定义 1.模式:一个客观事物的描述,一个可用来仿效的完善的例子. 2.模式识别:按哲学的定义是一个“外部信息到达感觉器官,并被转换成有意义的感觉经验”的过程. 例 ...

  6. day22_1-课前上节复习+os模块

    # ********************day22_1-课前上节复习+os模块 *******************# ********************day22_1-课前上节复习+os ...

  7. mybatis-plus分页查询

    在springboot中整合mybatis-plus 按照官方文档进行的配置:快速开始|mybatis-plus 引入依赖: <!-- 引入mybatisPlus --> <depe ...

  8. Eclipse 连接MySql数据库总结

    Eclipse 连接MySql数据库总结 一.在MySql中创建数据库,并创建表,向表中插入数据 1.创建数据库 create database select_test 2.创建表 create ta ...

  9. wpf datepicker 样式

    在项目中用到的 <Style TargetType="{x:Type DatePicker}"> <Setter Property="Foregroun ...

  10. css盒模型问题

    css盒模型问题 1.基本概念:标准模型和ie模型 2.标准模型和ie模型的区别 3.css如果设置这两种模型 4.js如何获取盒模型的宽高 5.边距重叠 6.BFC 1.CSS盒模型本质上是一个盒子 ...