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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/135443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 15:02:12  来源:igfitidea点击:

How do I use reflection to invoke a private method?

提问by Jeromy Irvine

There are a group of private methods in my class, and I need to call one dynamically based on an input value. Both the invoking code and the target methods are in the same instance. The code looks like this:

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

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

In this case, GetMethod()will not return private methods. What BindingFlagsdo I need to supply to GetMethod()so that it can locate private methods?

在这种情况下,GetMethod()不会返回私有方法。什么BindingFlags,我需要供应GetMethod(),以便它可以找到私有方法?

采纳答案by wprl

Simply change your code to use the overloaded version of GetMethodthat accepts BindingFlags:

只需更改您的代码以使用接受 BindingFlags的重载版本GetMethod

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

Here's the BindingFlags enumeration documentation.

这是BindingFlags 枚举文档

回答by Khoth

BindingFlags.NonPublic

BindingFlags.NonPublic

回答by Armin Ronacher

I think you can pass it BindingFlags.NonPublicwhere itis the GetMethodmethod.

我想,你可以通过它BindingFlags.NonPublic在那里GetMethod方法。

回答by Bill K

Are you absolutely sure this can't be done through inheritance? Reflection is the very last thing you should look at when solving a problem, it makes refactoring, understanding your code, and any automated analysis more difficult.

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

It looks like you should just have a DrawItem1, DrawItem2, etc class that override your dynMethod.

看起来您应该只有一个 DrawItem1、DrawItem2 等覆盖您的 dynMethod 的类。

回答by Jeromy Irvine

BindingFlags.NonPublicwill not return any results by itself. As it turns out, combining it with BindingFlags.Instancedoes the trick.

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

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

回答by Peter Hession

Could you not just have a different Draw method for each type that you want to Draw? Then call the overloaded Draw method passing in the object of type itemType to be drawn.

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

Your question does not make it clear whether itemType genuinely refers to objects of differing types.

您的问题没有说明 itemType 是否真正指的是不同类型的对象。

回答by cod3monk3y

And if you reallywant to get yourself in trouble, make it easier to execute by writing an extension method:

如果你真的想让自己陷入困境,可以通过编写扩展方法使其更容易执行:

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

And usage:

和用法:

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

回答by Owen James

Microsoft recently modified the reflection APIrendering most of these answers obsolete. The following should work on modern platforms (including Xamarin.Forms and UWP):

Microsoft 最近修改了反射 API,使这些答案中的大部分都过时了。以下应该适用于现代平台(包括 Xamarin.Forms 和 UWP):

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

Or as an extension method:

或者作为扩展方法:

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

Note:

笔记:

  • If the desired method is in a superclass of objthe Tgeneric must be explicitly set to the type of the superclass.

  • If the method is asynchronous you can use await (Task) obj.InvokeMethod(…).

  • 如果所需的方法在泛型的超类中,objT必须显式设置为超类的类型。

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

回答by Fab

Reflection especially on private members is wrong

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

  • Reflection breaks type safety.You can try to invoke a method that doesn't exists (anymore), or with the wrong parameters, or with too much parameters, or not enough... or even in the wrong order (this one my favourite :) ). By the way return type could change as well.
  • Reflection is slow.
  • 反射破坏类型安全。您可以尝试调用一个不存在的方法(不再存在),或者使用错误的参数,或者使用过多的参数,或者不够......或者甚至以错误的顺序(这是我最喜欢的 :) )。顺便说一下,返回类型也可能会改变。
  • 反射很慢。

Private members reflection breaks encapsulationprincipleand thus exposing your code to the following :

私有成员反射破坏了封装原则,从而将您的代码暴露给以下内容:

  • Increase complexityof your code because it has to handle the inner behavior of the classes. What is hidden should remain hidden.
  • Makes your code easy to breakas it will compile but won't run if the method changed its name.
  • Makes the private code easy to breakbecause if it is private it is not intended to be called that way. Maybe the private method expects some inner state before being called.
  • 增加代码的复杂性,因为它必须处理类的内部行为。隐藏的东西应该保持隐藏。
  • 使您的代码易于破坏,因为它将编译但如果方法更改其名称则不会运行。
  • 使私有代码易于破解,因为如果它是私有的,则不打算以这种方式调用。也许私有方法在被调用之前需要一些内部状态。

What if I must do it anyway ?

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

There are so cases, when you depend on a third party or you need some api not exposed, you have to do some reflection. Some also use it to test some classes they own but that they don't want to change the interface to give access to the inner members just for tests.

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

If you do it, do it right

如果你这样做,就做对

  • Mitigate the easy to break:
  • 减轻易断点:

To mitigate the easy to break issue, the best is to detect any potential break by testing in unit tests that would run in a continuous integration build or such. Of course, it means you always use the same assembly (which contains the private members). If you use a dynamic load and reflection, you like play with fire, but you can always catch the Exception that the call may produce.

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

  • Mitigate the slowness of reflection:
  • 减轻反射的缓慢:

In the recent versions of .Net Framework, CreateDelegate beat by a factor 50 the MethodInfo invoke:

在 .Net Framework 的最新版本中,CreateDelegate 以 50 倍的因子击败 MethodInfo 调用:

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

drawcalls will be around 50x faster than MethodInfo.Invokeuse drawas a standard Funclike that:

draw调用将比MethodInfo.Invoke用作draw这样的标准快 50 倍左右Func

var res = draw(methodParams);

Check this post of mineto see benchmark on different method invocations

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

回答by Maksim Shamihulau

Invokes any method despite its protection level on object instance. Enjoy!

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

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