非公开的类型或者方法被“隐藏”在程序集内部,本就不希望从外部访问,但是有时候调用一个内部或者私有方法可能是唯一的“救命稻草”,这篇文章列出了几种具体的实现方式。以如下这个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种方法的更多相关文章

  1. Shell脚本中引用、调用另一个脚本文件的2种方法

    Shell脚本中引用.调用另一个脚本文件的2种方法 http://www.jb51.net/article/67903.htm

  2. css控制div显示/隐藏方法及2种方法比较原码 - czf164的专栏 - 博客频道 - CSDN.NET

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  3. python第三方包安装方法(两种方法)

    具体有以下两种方法: 第一种方法(不使用pip或者easy_install): Step1:在网上找到的需要的包,下载下来.eg. rsa-3.1.4.tar.gz Step2:解压缩该文件. Ste ...

  4. wordpress调用置顶文章sticky_posts的三种方法

    有时我们在开发wordpress时需要调用置顶文章sticky_posts,怎么调用呢?几种写法,有用到query_post的,有用到WP_Query,也有用到is_sticky(),下面随ytkah ...

  5. Ajax跨域的几种方法以及每种方法的原理

    js中几种实用的跨域方法原理详解 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协 ...

  6. javascript实现图片延迟加载方法汇总(三种方法)

    看到一些大型网站,页面如果有很多图片的时候,当你滚动到相应的行时,当前行的图片才即时加载的,这样子的话页面在打开只加可视区域的图片,而其它隐藏的图片则不加载,一定程序上加快了页面加载的速度,跟着小编一 ...

  7. MFC 在对话框显示图片的多种方法(四种方法)

    我们先从简单的开始吧.先分一个类: (一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID) (二) 动态载入图片(即只需要在程序中指定图片的路径即可载入) 为方便说明,我们已经建好一个基 ...

  8. JavaScript中的方法事件和函数的方法的三种方法

    js中的很多事件  而事件相对应的就是方法(函数 )那么今天所说的就是这三种方法      已onclick事件为例 1: 基本方法 <div id="a" onclick= ...

  9. Qt 设置背景图片3种方法(三种方法:QPalette调色板,paintEvent,QSS)

    方法1. setStylSheet{"QDialog{background-image:url()"}}  //使用styleSheet 这种方法的好处是继承它的dialog都会自 ...

  10. QT 调用 DLL 方法(三种方法)

    Qt调用DLL方法一:使用Win32 API 在显式链接下,应用程序必须进行函数 调用以在运行时显式加载 DLL.为显式链接到 DLL,应用程序必须:? 调用 LoadLibrary(或相似的函 数) ...

随机推荐

  1. 探究公众号接口漏洞:从后台登录口到旁站getshell

    探究公众号接口漏洞:从后台登录口到旁站getshell 1.入口 发现与利用公众号接口安全漏洞 某120公众号提供了一处考核平台,通过浏览器处打开该网站. 打开可以看到一处密码登录口,试了一下常用的手 ...

  2. Node.js躬行记(27)——接口管理

    在页面发生线上问题时,你要做的事情就是去查接口,响应数据是否正确,查接口的方法有两种: 第一种是在浏览器中打开地址,但是你必须得知道详细的 URL,并且有些页面还需要附带参数. 第二种是打开编辑器,启 ...

  3. java POI创建HSSFWorkbook工作簿

    1. POI Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目. 2. 样式设 ...

  4. async/await中的promise返回错误reject

    https://blog.csdn.net/qq_42543244/article/details/123423894 最近在学 node ,之前对 async/await 和 promise 略懂, ...

  5. [C++核心编程] 4.6、继承

    文章目录 4.6 继承 4.6.1 继承的基本语法 4.6.2 继承方式 4.6.3 继承中的对象模型 4.6.4 继承中构造和析构顺序 4.6.5 继承同名成员处理方式 4.6.6 继承同名静态成员 ...

  6. MySQL事务和锁实战篇

    文章目录 MySQL事务和锁 事务 事务的控制语句 事务隔离级别设置 脏读 不可重复读 幻读 锁机制 InnoDB的行级锁 锁实战 死锁 总结 MySQL事务和锁 事务 说到关系型的数据库的事务,相信 ...

  7. ABC294Ex K-Coloring

    Statement 对一张简单无向图进行 \(k\) 染色,满足对于每条边的两个端点颜色不同,求方案数. \(n,m\leq 30\). Solution 无向图 \(k\) 染色问题,很经典的问题. ...

  8. [SWPUCTF 2021 新生赛]简简单单的逻辑

    得到一个.py文件,一般是没壳的,不过还是要养成习惯,查个壳: 意料之中,啥也没有,打开文件: 给了我们一个加密逻辑,然后最后一行给了一个结果:那么就是根据上述的逻辑,反解密出flag就好了 分析一下 ...

  9. 基于.Net5+Vue+iView前后端分离通用权限开源系统

    在Github上,.Net通用的权限框架非常多,功能也都比较强大,但是对于很多初学者来说,想要从零学习框架的搭建,就比较困难了. 所以,今天给大家推荐一套比较简单的前后端分离通用权限系统. 项目简介 ...

  10. 在Bamboo上怎么使用iOS的单元测试

    作者:京东零售 吴滔 本教程将使用北汽登录模块为例,一步一步和大家一起搭建单元测试用例,并在Bamboo上跑起来,最终测试结果和代码覆盖率会Bamboo上汇总. 模块名称:BQLoginModule, ...