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

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


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

评论

BindingFlags.NonPublic

#1 楼

只需更改代码即可使用接受BindingFlags的GetMethod的重载版本:

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


这里是BindingFlags枚举文档。

评论


我要为此陷入很多麻烦。

–弗兰克·施维特曼(Frank Schwieterman)
09年10月10日在18:10

BindingFlags.NonPublic不返回私有方法.. :(

–Moumit
14-10-22在10:25

@MoumitMondal您的方法是静态的吗?您必须为非静态方法指定BindingFlags.Instance以及BindingFlags.NonPublic。

– Brians
2014年11月26日16:06

没有@BrianS ..该方法是非静态且私有的,并且该类是从System.Web.UI.Page继承的。.无论如何,它使我傻瓜..我没有找到原因.. :(

–Moumit
2014年11月27日10:33



添加BindingFlags.FlattenHierarchy将使您能够将方法从父类获取到实例。

–龙的思想
18年8月22日在9:29

#2 楼

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

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


评论


同样的逻辑也适用于内部功能

– supertopi
2015年3月5日13:44



我有一个类似的问题。如果“这”是一个子类,而您尝试调用父类的私有方法怎么办?

–persianLife
16年11月24日在20:34

是否可以使用它来调用base.base类的受保护方法?

– Shiv
18年1月1日在22:37

#3 楼

而且,如果您真的想惹麻烦,请编写扩展方法使其更容易执行:

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


评论


有危险吗是。但是当包裹在我的单元测试名称空间中时,它是一个很好的帮助扩展。谢谢你

– Robert Wahler
2014年8月29日在13:38

如果您关心从调用的方法引发的实际异常,则最好将其包装到try catch块中,然后在捕获TargetInvokationException时重新抛出内部异常。我在单元测试助手扩展中做到了。

– Slobodan Savkovic
16-2-24在18:40

反射危险吗?嗯... C#,Java,Python ...实际上一切都是危险的,甚至对世界也是如此:D您只需要注意如何安全地进行操作...

–传奇
18年2月1日在12:19

#4 楼

Microsoft最近修改了反射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(…)


评论


它至少不适用于UWP .net版本,因为它仅适用于公共方法:“返回一个集合,该集合包含在当前类型上声明的,与指定名称匹配的所有公共方法”。

– Dmytro Bondarenko
17-2-16在23:26



@DmytroBondarenko我针对私有方法对其进行了测试,并且该方法有效。我确实看到了。不知道为什么它的行为与文档不同,但至少它能起作用。

–欧文·詹姆斯(Owen James)
17年2月16日在23:42

是的,如果文档说GetDeclareMethod()仅用于检索公共方法,那么我不会将所有其他答案称为过时的。

–质点网
19年8月27日在12:20

#5 楼

您是否绝对确定无法通过继承完成此操作?反射是解决问题时应注意的最后一件事,它使重构,理解代码和任何自动化分析变得更加困难。

看起来您应该只拥有DrawItem1,DrawItem2,覆盖您的dynMethod的etc类。

评论


@Bill K:在其他情况下,我们决定对此不使用继承,因此使用反射。在大多数情况下,我们会这样做。

–Jeromy Irvine
08-09-25 at 19:32

#6 楼

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




反思会破坏类型安全。您可以尝试调用一个不存在的方法(或不再存在),使用错误的参数,使用过多的参数,或者使用的方法不足够……甚至以错误的顺序(这是我最喜欢的:))。顺便说一句,返回类型也可能发生变化。
反射很慢。

私有成员反射破坏了封装原理,因此将您的代码暴露于以下内容:



增加代码的复杂度,因为它必须处理类的内部行为。隐藏的内容应保持隐藏状态。

使您的代码易于破解,因为它可以编译,但如果方法更改了名称,它将无法运行。

使私有代码变得容易中断,因为如果它是私有的,则不应以这种方式调用它。也许私有方法在被调用之前就需要内部状态。

如果我仍然必须这样做,该怎么办?

在某些情况下,当您依赖第三方或需要一些不暴露的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倍。 br />将draw用作标准Func,例如:

var res = draw(methodParams);


检查我的这篇文章,以查看不同方法调用的基准

评论


尽管我认为依赖注入应该是单元测试的首选方式,但我想谨慎地使用反射来访问原本无法进行测试的单元并不是完全有害的。我个人认为,与普通的[public] [protected] [private]修饰符一样,我们还应该具有[Test] [Composition]修饰符,以便可以在这些阶段使某些内容可见,而不必强制将所有内容完全公开(因此必须完全记录这些方法)

– andrew pate
17年7月7日在10:08

感谢Fab列出了对私有成员进行反思的问题,这使我重新审视了使用它的感受并得出了结论...使用反思对私有成员进行单元测试是错误的,但是未对代码路径进行测试是真的很错。

– andrew pate
17年7月7日在15:46

但是,当涉及通常不可单元测试的遗留代码的单元测试时,这是一种绝妙的方法

– T.S.
18年5月23日在4:54

#7 楼

您不仅可以为每种要绘制的类型使用不同的Draw方法吗?然后调用重载的Draw方法,传入要绘制的itemType类型的对象。

您的问题不清楚,itemType是否确实引用了不同类型的对象。

#8 楼

我认为您可以在BindingFlags.NonPublic方法中通过它。

#9 楼

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

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


#10 楼

阅读此(补充)答案(有时是答案),以了解问题的发展方向以及为什么该线程中的某些人抱怨“它仍然不起作用”

我编写了与代码完全相同的代码这里的答案。但是我仍然有一个问题。我将断点放在

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


执行,但是mi == null

并继续这样的行为,直到我对所有对象进行“重建”为止涉及的项目。当反射方法位于第三组件中时,我正在对一个组件进行单元测试。这完全令人困惑,但是我使用即时窗口来发现方法,并且发现尝试进行单元测试的私有方法具有旧名称(我将其重命名)。这告诉我,即使建立了单元测试项目,旧的程序集或PDB仍然存在-由于某种原因,它未测试的项目。 “重建”成功了