我如何使用reflection来调用私有方法?
在我的class级有一组私人的方法,我需要根据input值dynamic调用一个。 调用代码和目标方法都在同一个实例中。 代码如下所示:
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType); dynMethod.Invoke(this, new object[] { methodParams });
在这种情况下, GetMethod()
不会返回私有方法。 我需要提供什么BindingFlags
到GetMethod()
以便它可以find私有方法?
只需更改您的代码以使用接受BindingFlags 的GetMethod
的重载版本即可 :
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, BindingFlags.NonPublic | BindingFlags.Instance); dynMethod.Invoke(this, new object[] { methodParams });
这里是BindingFlags枚举文档 。
BindingFlags.NonPublic
不会自行返回任何结果。 事实certificate,将其与BindingFlags.Instance
结合起来就可以实现。
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, BindingFlags.NonPublic | BindingFlags.Instance);
如果你真的想让自己陷入困境,可以通过编写一个扩展方法来更容易执行:
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); }
你确定这不能通过inheritance来完成吗? 反思是解决问题时应该考虑的最后一件事情,它使得重构,理解代码,以及自动分析变得更加困难。
看起来你应该只有一个DrawItem1,DrawItem2等类来覆盖你的dynMethod。
微软最近修改了reflectionAPI渲染大部分这些答案已经过时。 以下应该在现代平台(包括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
generics必须显式设置为超类的types。 -
如果方法是asynchronous的,可以使用
await (Task) obj.InvokeMethod(…)
。
你不能只为每个你想绘制的types有不同的Draw方法吗? 然后调用传入types为itemType的对象的重载Draw方法进行绘制。
你的问题并没有说明itemType是否真正指向不同types的对象。
特别是对私人成员的反思是错误的
- 反思打破了types安全。 你可以尝试调用一个不存在的方法,或者使用错误的参数,或者参数太多,或者不够多,甚至错误的顺序(这是我最喜欢的:))。 顺便返回types也可以改变。
- 反思很慢。
私有成员reflection破坏封装原则 ,从而暴露您的代码如下:
- 增加代码的复杂性 ,因为它必须处理类的内部行为。 隐藏的内容应该隐藏起来。
- 使代码容易中断,因为它会编译,但如果方法更改了名称,将不会运行。
- 使私人代码易于打破,因为如果它是私人的,它不打算被这样调用。 也许private方法在调用之前需要一些内部状态。
如果我必须这样做呢?
有这样的情况,当你依赖第三方,或者你需要一些没有暴露的API时,你必须做一些反思。 有些人也使用它来testing他们自己拥有的一些类,但是他们不想改变接口来访问内部成员只是为了testing。
如果你这样做,做对了
- 减轻容易中断:
为了减轻容易中断的问题,最好的办法是通过unit testing中的testing来检测任何潜在的中断,这些中断可以在持续集成构build或者类似的过程中运行。 当然,这意味着你总是使用相同的程序集(包含私有成员)。 如果你使用dynamic加载和reflection,你喜欢玩火,但你总是可以捕捉到可能产生的exception。
- 缓解反思的缓慢:
在.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);
draw
调用将比MethodInfo.Invoke
快大约50倍使用draw
作为一个标准的Func
像这样:
var res = draw(methodParams);
查看我的这篇文章,以查看不同方法调用的基准
我认为你可以通过它BindingFlags.NonPublic
GetMethod
方法。
BindingFlags.NonPublic