调用内部或私有方法的N种方法
非公开的类型或者方法被“隐藏”在程序集内部,本就不希望从外部访问,但是有时候调用一个内部或者私有方法可能是唯一的“救命稻草”,这篇文章列出了几种具体的实现方式。以如下这个Foobar类型为例,它具有一个内部属性InternalValue,我们来看看有多少种方式可以从外部获取一个Foobar对象的InternalValue属性值。
public class Foobar
{
internal int InternalValue => 123;
}
一、反射
对于大部分人来说,最先想到的自然是“反射”,具体实现体现再如下所示的InternalValueAccessor类型的GetInternalValue方法中。但是我们都知道反射是一种并不高效的方式,对于需要频繁调用,我们一般不推荐使用。
var foobar = new Foobar();
Debug.Assert(InternalValueAccessor.GetInternalValue(foobar) == 123); public static class InternalValueAccessor
{
public static int GetInternalValue(Foobar foobar)
{
var propertyInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!;
return (int)propertyInfo.GetValue(foobar)!;
}
}
二、MethodInfo.CreateDelegate方法
要获得Foobar对象的InternalValue属性值(int类型),实际上需要一个Func<Foobar,int>类型的委托。由于返回值实际上是通过InternalValue属性的Get方法获得的,而表示方法的MethodInfo类型具有一个CreateDelegate<TDelegate>方法,我们可以采用如下的方式利用InternalValue属性的Get方法来创建所需的Func<Foobar,int>委托。
var foobar = new Foobar();
Debug.Assert(InternalValueAccessor.GetInternalValue(foobar) == 123); public static class InternalValueAccessor
{
private static Func<Foobar, int>? _getInternalValue;
public static int GetInternalValue(Foobar foobar)=> (_getInternalValue??= CreateDelegate())(foobar);
private static Func<Foobar, int> CreateDelegate()
{
var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!;
return methodInfo.CreateDelegate<Func<Foobar, int>>();
}
}
三、表达式(树)
一般来说,所有的反射解决方案都可以转换成基于表达式(树)的解决方案。我们需要的Func<Foobar,int>委托可以按照如下的方式,利用构建的表达式编译生成。
public static class InternalValueAccessor
{
private static Func<Foobar, int>? _getInternalValue;
public static int GetInternalValue(Foobar foobar)=> (_getInternalValue??= CreateDelegate())(foobar);
private static Func<Foobar, int> CreateDelegate()
{
var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!;
var foobar = Expression.Parameter(typeof(Foobar), "foobar");
var getValue = Expression.Call(foobar, methodInfo);
return Expression.Lambda<Func<Foobar, int>>(getValue, foobar).Compile();
}
}
四、动态方法(call)
实际上表达式(树)是对IL代码的抽象表达,所以既然这样的问题自然可以利用IL Emit来解决。在如下的代码中,我们创建了一个DynamicMethod类型表示的动态方法,以IL Emit的方式利用IL指令Call完成了针对InternalValue属性的Get方法的调用。我们所需的Func<Foobar,int>委托最终由这个DynamicMethod对象创建而成。
public static class InternalValueAccessor
{
private static Func<Foobar, int>? _getInternalValue;
public static int GetInternalValue(Foobar foobar) => (_getInternalValue ??= CreateDelegate())(foobar);
private static Func<Foobar, int> CreateDelegate()
{
var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!;
var method = new DynamicMethod("GetInternalValue", typeof(int), new Type[] { typeof(Foobar) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, methodInfo, null);
il.Emit(OpCodes.Ret);
return method.CreateDelegate<Func<Foobar, int>>();
}
}
五、动态方法(calli)
了解IL的朋友应该知道,方法调用涉及的IL治理有三个(Call、Callvir和Calli)。如果使用Calli指令,在完成针对参数的压栈之后,我们还需要执行Ldftn指令将方法指针压入栈中,最终执行Calli指令完成方法的执行。
public static class InternalValueAccessor
{
private static Func<Foobar, int>? _getInternalValue;
public static int GetInternalValue(Foobar foobar) => (_getInternalValue ??= CreateDelegate())(foobar);
private static Func<Foobar, int> CreateDelegate()
{
var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!;
var method = new DynamicMethod("GetInternalValue", typeof(int), new Type[] { typeof(Foobar) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldftn, methodInfo);
il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(int), new Type[] { typeof(Foobar) }, null);
il.Emit(OpCodes.Ret);
return method.CreateDelegate<Func<Foobar, int>>();
}
}
调用内部或私有方法的N种方法的更多相关文章
- Shell脚本中引用、调用另一个脚本文件的2种方法
Shell脚本中引用.调用另一个脚本文件的2种方法 http://www.jb51.net/article/67903.htm
- css控制div显示/隐藏方法及2种方法比较原码 - czf164的专栏 - 博客频道 - CSDN.NET
body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...
- python第三方包安装方法(两种方法)
具体有以下两种方法: 第一种方法(不使用pip或者easy_install): Step1:在网上找到的需要的包,下载下来.eg. rsa-3.1.4.tar.gz Step2:解压缩该文件. Ste ...
- wordpress调用置顶文章sticky_posts的三种方法
有时我们在开发wordpress时需要调用置顶文章sticky_posts,怎么调用呢?几种写法,有用到query_post的,有用到WP_Query,也有用到is_sticky(),下面随ytkah ...
- Ajax跨域的几种方法以及每种方法的原理
js中几种实用的跨域方法原理详解 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协 ...
- javascript实现图片延迟加载方法汇总(三种方法)
看到一些大型网站,页面如果有很多图片的时候,当你滚动到相应的行时,当前行的图片才即时加载的,这样子的话页面在打开只加可视区域的图片,而其它隐藏的图片则不加载,一定程序上加快了页面加载的速度,跟着小编一 ...
- MFC 在对话框显示图片的多种方法(四种方法)
我们先从简单的开始吧.先分一个类: (一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID) (二) 动态载入图片(即只需要在程序中指定图片的路径即可载入) 为方便说明,我们已经建好一个基 ...
- JavaScript中的方法事件和函数的方法的三种方法
js中的很多事件 而事件相对应的就是方法(函数 )那么今天所说的就是这三种方法 已onclick事件为例 1: 基本方法 <div id="a" onclick= ...
- Qt 设置背景图片3种方法(三种方法:QPalette调色板,paintEvent,QSS)
方法1. setStylSheet{"QDialog{background-image:url()"}} //使用styleSheet 这种方法的好处是继承它的dialog都会自 ...
- QT 调用 DLL 方法(三种方法)
Qt调用DLL方法一:使用Win32 API 在显式链接下,应用程序必须进行函数 调用以在运行时显式加载 DLL.为显式链接到 DLL,应用程序必须:? 调用 LoadLibrary(或相似的函 数) ...
随机推荐
- python:调用内置函数
问题描述:尝试下博客园如何上传GIF # hzh 每天进步一点点 # 2022/5/13 17:24 import colorama import time import os colorama.in ...
- 一文吃透Elasticsearch
本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...
- ROS动态调试PID参数
ROS动态调试PID参数 连接小车 注意:必须在同一区域网 ssh clbrobort@clbrobort 激活树莓派主板 roslaunch clbrobot bringup.launch 打开PI ...
- Python OOP之继承封装多态
面向对象的三大特征 继承 封装 多态 继承 子类可以使用父类定义的内容或者行为 继承的实现 父类,基类,超类,被继承的类,Base Class,Super Class 子类:有继承行为的类 所有类都必 ...
- [OpenCV-Python] 8 用滑动条做调色板
文章目录 OpenCV-Python:II OpenCV 中的 Gui 特性 8 用滑动条做调色板 8.1 代码示例 练习 OpenCV-Python:II OpenCV 中的 Gui 特性 8 用滑 ...
- Python-pprint的简单使用
Data pretty printer 一.简介 print()和pprint()都是python的打印模块,功能基本一样,唯一的区别就是pprint()模块打印出来的数据结构 ...
- Redis分布式锁实现及使用
文章目录 分布式锁 全局ID生成器 一人一单实现 超卖问题 一人一单 分布式锁 Redis setnx实现分布式锁 Redis在业内解决秒杀等业务场景有非常广的应用,如何设计实现一个分布式锁是解决超卖 ...
- JavaWeb 中 Filter过滤器
Filter过滤器 每博一文案 师傅说:人生无坦途,累是必须的背负,看多了,人情人暖,走遍了离合聚散,有时会 在心里对自己说,我想,我是真的累了,小时候有读不完的书,长大后有赚不尽的力. 白天在外要奋 ...
- Nginx Web快速入门
Nginx Web快速入门 目录 Nginx Web快速入门 Nginx概述 为什么选择Nginx服务 Nginx的应用场景 源码安装nginx Yum安装nginx Nginx相关命令总结 Ngin ...
- 2021-06-28:最接近目标值的子序列和。给你一个整数数组 nums 和一个目标值 goal 。你需要从 nums 中选出一个子序列,使子序列元素总和最接近 goal 。也就是说,如果子序列元素和
2021-06-28:最接近目标值的子序列和.给你一个整数数组 nums 和一个目标值 goal .你需要从 nums 中选出一个子序列,使子序列元素总和最接近 goal .也就是说,如果子序列元素和 ...