如何使用反射调用私有方法?

在我的类中有一组私有方法,我需要根据输入值动态调用其中一个方法。调用代码和目标方法都在同一个实例中。代码如下所示:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });

在这种情况下,GetMethod()将不会返回私有方法。我需要向GetMethod()提供什么BindingFlags才能定位私有方法?

207363 次浏览

我认为你可以传递它BindingFlags.NonPublic,其中GetMethod方法。

你确定这不能通过继承来实现吗?反射是解决问题时最不应该考虑的事情,它会使重构、理解代码和任何自动化分析变得更加困难。

看起来你应该有一个DrawItem1, DrawItem2等类来覆盖你的dynMethod。

简单地修改你的代码来使用重载的接受BindingFlags的GetMethod的版本:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });

这里是BindingFlags枚举文档

BindingFlags.NonPublic本身不会返回任何结果。事实证明,将它与BindingFlags.Instance结合起来就可以了。

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);

你能不能为你想要绘制的每个类型都有不同的绘制方法?然后调用重载的Draw方法,传入要绘制的itemType类型的对象。

你的问题没有说清楚itemType是否真的引用了不同类型的对象。

如果你真的想让自己陷入麻烦,通过编写一个扩展方法让它更容易执行:

static class AccessExtensions
{
public static object call(this object o, string methodName, params object[] args)
{
var mi = o.GetType ().GetMethod (methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance );
if (mi != null) {
return mi.Invoke (o, args);
}
return null;
}
}

和用法:

    class Counter
{
public int count { get; private set; }
void incr(int value) { count += value; }
}


[Test]
public void making_questionable_life_choices()
{
Counter c = new Counter ();
c.call ("incr", 2);             // "incr" is private !
c.call ("incr", 3);
Assert.AreEqual (5, c.count);
}

微软最近修改反射API渲染大部分这些答案过时。以下内容应该适用于现代平台(包括Xamarin)。表格及UWP):

obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);

或者作为扩展方法:

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
var type = typeof(T);
var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
return method.Invoke(obj, args);
}

注意:

  • 如果想要的方法在obj的超类中,T泛型必须显式设置为超类的类型。

  • 如果方法是异步的,你可以使用await (Task) obj.InvokeMethod(…)

尤其是对私人成员的反思是错误的

  • 反射破坏类型安全。你可以尝试调用一个不存在(不再)的方法,或者有错误的参数,或者有太多的参数,或者不够…甚至顺序也不对(这是我最喜欢的:))。顺便说一下,返回类型也可以改变。
  • 反思是缓慢的。

私有成员反射中断封装原则,从而暴露你的代码如下:

  • 增加复杂性的代码,因为它必须处理类的内部行为。隐藏的东西应该继续隐藏。
  • 使您的代码容易被破坏,因为如果方法更改了名称,它将编译但不会运行。
  • 使得私有代码很容易被破解,因为如果它是私有的,它就不打算这样调用。也许私有方法在被调用之前需要一些内部状态。

如果我必须这么做呢?

有这样的情况,当你依赖于第三方或者你需要一些不公开的api时,你必须做一些反思。有些人还使用它来测试他们拥有的一些类,但他们不想仅仅为了测试而更改接口来访问内部成员。

如果你要做,就要做对

  • 减轻易碎:

为了减轻容易中断的问题,最好是通过在持续集成构建或类似的构建中运行的单元测试中测试来检测任何潜在的中断。当然,这意味着您总是使用相同的程序集(其中包含私有成员)。如果您使用动态加载和反射,您就像在玩火,但是您总是可以捕获调用可能产生的异常。

  • 减缓反射的缓慢:

在最近版本的。net Framework中,CreateDelegate比MethodInfo调用强50倍:

// The following should be done once since this does some reflection
var method = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);


// Here we create a Func that targets the instance of type which has the
// Draw_ItemType method
var draw = (Func<TInput, Output[]>)_method.CreateDelegate(
typeof(Func<TInput, TOutput[]>), this);

draw调用将比MethodInfo.Invoke快50倍左右 使用draw作为标准Func,如下所示:

var res = draw(methodParams);

检查我的职位以查看不同方法调用的基准测试

调用任何方法,不管它在对象实例上的保护级别。享受吧!

public static object InvokeMethod(object obj, string methodName, params object[] methodParams)
{
var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
MethodInfo method = null;
var type = obj.GetType();
while (method == null && type != null)
{
method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
type = type.BaseType;
}


return method?.Invoke(obj, methodParams);
}

阅读这个(补充)答案(这有时是答案)来理解这是怎么去的和为什么一些人在这个线程抱怨“它仍然没有工作”

我写的代码和这里的一个答案完全一样。但我仍然有一个问题。我设置了断点

var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );

它执行了但是mi == null

这种情况一直持续到我在所有相关项目上“重新构建”为止。我在单元测试一个程序集,而反射方法位于第三个程序集。这完全令人困惑,但我使用即时窗口来发现方法,我发现我尝试单元测试的私有方法有旧名称(我重新命名了它)。这告诉我,即使单元测试项目构建了,旧的程序集或PDB仍然存在——由于某种原因,它测试的项目没有构建。“重建”工作

应该注意,从派生类调用可能会有问题。

容易出错:

this.GetType().GetMethod("PrivateTestMethod", BindingFlags.Instance | BindingFlags.NonPublic)

正确的:

typeof(CurrentClass).GetMethod("PrivateTestMethod", BindingFlags.Instance | BindingFlags.NonPublic)