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

时间:2008-09-25 19:26:14

标签: c# .net reflection private-methods

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

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

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

12 个答案:

答案 0 :(得分:467)

只需更改代码即可使用接受BindingFlags的重载version of GetMethod

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

这是BindingFlags enumeration documentation

答案 1 :(得分:65)

BindingFlags.NonPublic不会自行返回任何结果。事实证明,将其与BindingFlags.Instance相结合可以解决问题。

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

答案 2 :(得分:50)

如果你真的想要让自己陷入困境,那么通过编写扩展方法让它更容易执行:

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);
    }

答案 3 :(得分:14)

Microsoft最近modified the reflection API将大部分答案都废弃了。以下内容适用于现代平台(包括Xamarin.Forms和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(…)

答案 4 :(得分:11)

你绝对相信这不能通过继承来完成吗?反思是解决问题时应该注意的最后一件事,它使重构,理解代码以及任何自动分析变得更加困难。

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

答案 5 :(得分:6)

特别是关于私人成员的反思是错误的

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

私人成员反思中断encapsulation原则,从而将您的代码暴露给以下内容:

  • 提高代码的复杂性,因为它必须处理类的内部行为。隐藏的东西应该隐藏起来。
  • 使代码容易破解,因为它会编译但如果方法更改名称则不会运行。
  • 使私有代码容易破解,因为如果它是私有的,则无意以这种方式调用。也许私有方法在被调用之前需要一些内部状态。

如果我必须这样做怎么办?

有这样的情况,当你依赖第三方或者你需要一些不暴露的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);

检查此post of mine以查看不同方法调用的基准

答案 6 :(得分:2)

您是否可以为要绘制的每种类型设置不同的Draw方法?然后调用重载的Draw方法传入要绘制的itemType类型的对象。

您的问题并不清楚itemType是否真正引用了不同类型的对象。

答案 7 :(得分:1)

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

答案 8 :(得分:1)

尽管对象实例具有保护级别,但仍调用任何方法。享受!

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);
}

答案 9 :(得分:0)

BindingFlags.NonPublic

答案 10 :(得分:0)

阅读这个(补充)答案(有时候是答案)来了解这种情况以及为什么此线程中的某些人抱怨“它仍然无效”

I wrote exactly same code as one of the answers here。但我还有一个问题。我把断点放在

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

执行但mi == null

它继续这样的行为,直到我对所涉及的所有项目进行“重建”。我是单元测试一个组件,而反射方法坐在第三个组件中。这完全令人困惑,但我使用立即窗口来发现方法,我发现我尝试单元测试的私有方法有旧名称(我重命名)。这告诉我,即使单元测试项目构建,旧的程序集或PDB仍在那里 - 由于某种原因,项目测试没有建立。 “重建”工作

答案 11 :(得分:0)

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

容易出错:

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

正确:

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