2019-4-16-C#-使用反射获取私有属性的方法
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#-使用反射获取私有属性的方法的更多相关文章
- C# 使用反射获取私有属性的方法
本文告诉大家多个不同的方法使用反射获得私有属性,最后通过测试性能发现所有的方法的性能都差不多 在开始之前先添加一个测试的类 public class Foo { private string F { ...
- 第五课 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") ...
- Android(java)学习笔记49:通过反射获取私有构造方法并且使用
1. 反射获取私有构造方法并且使用: (1)获取字节码文件.class对象: Class c = Class.forName("cn.itcast_01.Person&qu ...
- java通过反射获取私有的构造方法,及反射擦除泛型数据类型约束
/* * 反射获取私有的构造方法运行 * 不推荐,破坏了程序的安全性,封装性 * 暴力私有 */ public class ReflectDemo4 { public static void main ...
- Java 反射 调用私有域和方法(setAccessible)
Java 反射 调用私有域和方法(setAccessible) @author ixenos AccessibleObject类 Method.Field和Constructor类共同继承了Acces ...
- java通过反射获取调用变量以及方法
一:反射概念 可以通过Class类获取某个类的成员变量以及方法,并且调用之. 二:通过反射获取方法.变量.构造方法 @Test // 通过反射获取类定义的方法 public void testMeth ...
- c# 如何通过反射 获取\设置属性值
c# 如何通过反射 获取\设置属性值 //定义类public class MyClass{public int Property1 { get; set; }}static void Main(){M ...
随机推荐
- MySQL sql_mode 说明(及处理一起sql_mode引发的问题)
转自:https://segmentfault.com/a/1190000005936172 1. MySQL 莫名变成了 Strict SQL Mode 最近测试组那边反应数据库部分写入失败,app ...
- RAKsmart服务器受消费者青睐的原因
随着互联网的快速发展,网站建设变得越来越重要,现在很多做外贸网站的都会选择美国服务器来建设网站,近年来RAKsmart服务器受到广大站长的欢迎,那RAKsmart服务器受消费者青睐的关键是什么呢. 1 ...
- vue项目 上传到git
1. git add . 效果: 2. git commit -m '' 3. git push 效果: 4.在码云上就可以看到修改啦 2018-05-19 08:52:52
- 使用Fiddler模拟弱网络环境测试
https://blog.csdn.net/swordgirl2011/article/details/51765237 https://www.cnblogs.com/longronglang/p/ ...
- 手写代码注意点 -- int[]
int[].length char[] 的数组长度,是小写的l开头: char[].length; 不是:char[].Length int[] 没有.contains(), .indexOf() . ...
- vim编码方式设置
建议vim的_vimrc文件里设置如下的编码方式: set encoding=utf-8 set fileencodings=ucs-bom,utf-8,cp936 set fileencoding= ...
- SQL Server 2008 install
双击sql server 2008的.exe安装文件,进入[SQL Server 安装中心]. 2 点击界面左侧的[安装],然后点击右侧的[全新SQL Server 独立安装或向现有安装添加功能],进 ...
- Django项目:堡垒机(Linux服务器主机管理系统)--01--01堡垒机重写DJANGO账户表
python相关软件安装流程图解————————python安装——————python-3.7.1-amd64 https://www.cnblogs.com/ujq3/p/10098166.htm ...
- python处理多线程之间事件通讯方法
一.什么是事件 每执行一个事情,肯定有该事情的执行后状态,那事件就是该事情发生的信号 在程序中,多线程之间需要通讯,而事件就是方便线程之间的通讯 案例: 1.服务器启动需要5秒 2.客服端启动后去链接 ...
- AutoMapper 在你的项目里飞一会儿
先说说DTO DTO是个什么东东? DTO(Data Transfer Object)就是数据传输对象,说白了就是一个对象,只不过里边全是数据而已. 为什么要用DTO? 1.DTO更注重数据,对领域对 ...