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这个特性标签所有出现的地方,把 ...
随机推荐
- 各种高度的区别及height、clientHeight、scrollHeight、offsetHeight的区分
1.height.clientHeight.scrollHeight.offsetHeight 我们来实现test中的onclick事件 function justAtest() { ...
- nodeJs学习-16 数据字典示例
1.banner ID title 标题 varchar(32) sub_title 副标题 varchar(16) src 图片地址 varchar(64) 2.文章 ID author 作者 va ...
- Web富媒体应用
曾几何时,大家都在以flash开发的富媒体交互应用而感叹,一是叹它的丰富多彩的效果,一是叹它的局限.性能以及加载时长等问题. 如今,市场以及基本上没有flash什么事情了,而是H5的天下,可惜,移动应 ...
- [java]struts2 模型驱动 2016-05-01 21:40 702人阅读 评论(19) 收藏
一开始敲网上商城的时候,对于数据的传递方式我是很惊艳了一把的,感觉特别高大上,就感觉,竟然不用像.net一样取谁的值,给谁赋值这样,只要需要用的时候,简单的get一下就ok了,简直高大上啊. 然后发现 ...
- 【NS2】NS2修改MAC协议(转载)
NS2版本:2.34 涉及NS2代码文件: ns-2.34/mac/channel.h ns-2.34/mac/channel.cc ns-2.34/mac/wireless-phyExt.h n ...
- Win10系统使用Docker安装oracle并通过Navicat for oracle进行登录
一.安装Docker Linux系统可以直接采用命令进行Docker安装: Win7系统安装Dokcer实际通过Boot2Docker在Windows下安装一个VirtualBox来实现: Boot2 ...
- lavarel box 地址
https://atlas.hashicorp.com/laravel/boxes/homestead download URL https://atlas.hashicorp.com/laravel ...
- 01docker简单使用和配置(容器、镜像)
一:容器中运行简单应用程序 1:hello world 使用docker可以在容器中运行应用程序,使用docker run命令即可.比如下面的命令: $ docker run ubuntu /bin ...
- Streamy障碍二:超大排序合并
- 【阿里云新品发布·周刊】第13期:链路追踪 Tracing Analysis 商业化首发
点击订阅新品发布会! 新产品.新版本.新技术.新功能.价格调整,评论在下方,下期更新!关注更多内容,了解更多 最新发布 链路追踪 Tracing Analysis 商业化首发 2019年6月12日15 ...