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


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
C# 使用反射获取私有属性的方法的更多相关文章
- 2019-4-16-C#-使用反射获取私有属性的方法
title author date CreateTime categories C# 使用反射获取私有属性的方法 lindexi 2019-4-16 10:13:3 +0800 2018-09-26 ...
- 第五课 JAVA反射获取对象属性和方法(通过配置文件)
Service1.java package reflection; public class Service1 { public void doService1(){ System.out.print ...
- 第五课 JAVA反射获取对象属性和方法
package com.hero; import java.lang.reflect.Field; public class TestReflction5 { public static void m ...
- Android(java)学习笔记108:通过反射获取私有构造方法并且使用
反射获取私有构造方法并且使用: 1.获取字节码文件.class对象: Class c = Class.forName("cn.itcast_01.Person") ...
- Java 反射 调用私有域和方法(setAccessible)
Java 反射 调用私有域和方法(setAccessible) @author ixenos AccessibleObject类 Method.Field和Constructor类共同继承了Acces ...
- Android(java)学习笔记49:通过反射获取私有构造方法并且使用
1. 反射获取私有构造方法并且使用: (1)获取字节码文件.class对象: Class c = Class.forName("cn.itcast_01.Person&qu ...
- java通过反射获取私有的构造方法,及反射擦除泛型数据类型约束
/* * 反射获取私有的构造方法运行 * 不推荐,破坏了程序的安全性,封装性 * 暴力私有 */ public class ReflectDemo4 { public static void main ...
- java 通过反射获取类属性结构,类方法,类父类及其泛型,类,接口和包
首先自定义三个类 package reflection1; public interface MtInterface { void info(); } package reflection1; imp ...
- .NET Core 反射获取所有控制器及方法上特定标签
.NET Core 反射获取所有控制器及方法上特定标签 有个需求,就是在. NET Core中,我们想在项目 启动时,获取LinCmsAuthorizeAttribute这个特性标签所有出现的地方,把 ...
随机推荐
- ie6中兼容性问题总结
针对firefox ie6 ie7 ie8的css样式中的line-height属性,以前我们大部分都是用!important来hack,对于ie6和firefox测试可以正常显示,但是ie7以上对! ...
- hackerrank--- challenges/fp-update-list
纯属为了练习haskell, 竟然贴代码都没办法高亮. challenges/fp-update-list Update the values of a list with their absolut ...
- Limboy:自学 iOS 开发的一些经验
不知不觉作为 iOS 开发也有两年多的时间了,记得当初看到 OC 的语法时,愣是被吓了回去,隔了好久才重新耐下心去啃一啃.啃了一阵,觉得大概有了点概念,看到 Cocoa 那么多的 Class,又懵了, ...
- C++临时对象的生命期
class Test{ public: Test(int a):m_int(a){ printf("this is Test(%d) ctor\n", m_int); } ~Tes ...
- 使用multiprocessing的问题总结
Python2中的multiprocessing模块,规避了GIL(Global Interpreter Lock)带来的多线程无法实际并发的缺点,设计了几乎与threading模块一模一样的API, ...
- React 入门笔记
一.什么是React React: A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES 上面的话直译过来就是,React是一个用于构建用户界面的Java ...
- Windows中查看PowerShell版本和virbox版本,vagrant 版本
我并不是很熟悉什么是PowerShell,但是有种直觉是:如果想在Windows中使用系统自带的功能取代bash shell,PowerShell或许是比DOS批处理更好的选择.不过,从头开始再来一门 ...
- poj3294 后缀数组
后缀数组多个字符串问题. 先求出height[]数组,然后二分求最大的长度. 但是条件需要改变.如果出现次数大于一般那就满足.然后就要解决如何判断那一段属于其中一个字符串. 所以先处理出长度.并且不断 ...
- iPhone 7 Plus 维修记 (一)(2019-08-07)
iPhone 7 Plus 维修记 问题 电池鼓包,屏幕已经被撑起,偶尔死机突然关机. 分析 初步分析是电池损坏. 维修 由于电池没有双易拉条需要将后壳加热后再取出电池. 更换电池后测试,发现电量一会 ...
- Java练习 SDUT-2728_最佳拟合直线
最佳拟合直线 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 在很多情况下,天文观测得到的数据是一组包含很大数量的序列点 ...