首页 > 其他分享 >调用内部或私有方法的N种方法

调用内部或私有方法的N种方法

时间:2023-07-13 12:23:06浏览次数:29  
标签:调用 私有 Foobar CreateDelegate static Func foobar var 方法

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

标签:调用,私有,Foobar,CreateDelegate,static,Func,foobar,var,方法
From: https://www.cnblogs.com/artech/p/17547246.html

相关文章

  • 做计划的方法
    1、任务分解模板:包括:总体目标、子项目名称、子项目目标、关键举措、衡量标准(重要)、责任人、计划完成时间(重要)、状态、以及后续的进展(按周/月等)。总体目标  子项目名称子项目目标关键举措  衡量标准  责任人  计划完成时间  状态(>90%达标)  3月进展  4......
  • 关于问题定位的方法定律
    关于问题定位的方法定律定律1最难定位的问题要么是最疑难的问题,要么是最低级的问题,这两种问题都有一个共同特征,就是让你意想不到。举一个例子,一次代码编译不过,报函数没有定义,开始怀疑是类没有“;”结束符,然后怀疑有没有匹配的“{”,折腾了好久,最后才发现是开头的“#ifndef”定义......
  • 关于学习的方法定律
    关于学习的方法定律定律1人们往往善于从事情的内容学习,而不善于从事情本身学习。公司的PLDP/PMDP培训效果很好,参加的人学到了如何做一个合格的PL/PM,却没有学会如何做好培训。从事情本身学习,是向别人学习的关键。定律2人们往往善于从失败中学习,而不善于从成功中学习。一件事......
  • 基于GPT搭建私有知识库聊天机器人(四)问答实现
    前文链接:基于GPT搭建私有知识库聊天机器人(一)实现原理基于GPT搭建私有知识库聊天机器人(二)环境安装基于GPT搭建私有知识库聊天机器人(三)向量数据训练在前面的文章中,我们介绍了如何使用GPT模型搭建私有知识库聊天机器人的基本原理、环境安装、数据向量化。本文将进一步介绍如何使......
  • WPF border解决超出圆角边界的方法
    使用Border并设置圆角,Border内部的其他元素会超出圆角而导致灾难级的视觉体验,通过设置Border的clip属性,来解决这个问题<BorderBorderThickness="1"BorderBrush="Black"CornerRadius="8"><Border.Clip>......
  • Error response from daemon:connect: no route to host——客户端远程登录私有仓库报
    报错:[root@client~]#dockerlogin-uadmin-pHarbor12345http://192.168.11.131WARNING!Using--passwordviatheCLIisinsecure.Use--password-stdin.Errorresponsefromdaemon:Gethttps://192.168.11.131/v2/:dialtcp192.168.11.131:443:connect:norout......
  • Ubuntu系统中安装RPM格式包的方法
    Ubuntu的软件包格式为.bed, RedHat的相关版本的软件包是.rmp。所以Ubuntu的系统想要安装rmp包的话,是需要转换格式的。sudoapt-getinstallalien#默认没有安装,自行安装sudoalienxxxx.rpm#使用rpm包生成一个deb的包。sudodpkgxxxx.deb......
  • 魔法方法之__getitem__(self, key)、__setitem__(self, key, value) 和 __delitem__(s
    1'''2__getitem__(self,key)、__setitem__(self,key,value)和__delitem__(self,key)是Python中的特殊方法,用于定义对对象进行索引操作时的行为。3它们分别用于获取、设置和删除对象中的元素41.__getitem__(self,key):该方法用于通过索引或键来获取对象中的......
  • 魔法方法__len__
    1'''2__len__()是一个魔法方法,用于返回对象的长度或元素个数。3它通常被用于支持内置函数len()的调用,并在自定义的容器类中实现。4最佳实践:51.在容器类中实现__len__()方法可以提供对象的长度信息,使其能够像内置容器(如列表、字典等)一样使用。62.......
  • 面向对象之魔法方法
    什么是魔法方法?Python类中的魔法方法(MagicMethods)是特殊的方法,以双下划线(__)开头和结尾的方法。这些方法在对象的创建、运算符重载、属性访问等方面提供了特定的行为。都是内置方法,一般方法命名不建议使用这种格式即双下划线开头和结尾的方法__init__(self,...):构造函数,用于......