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 BindingFlags
do I need to supply to GetMethod()
so that it can locate private methods?
在这种情况下,GetMethod()将不会返回私有方法。我需要向GetMethod()提供哪些绑定标志,以便它能够定位私有方法?
11 个解决方案
#1
420
Simply change your code to use the overloaded version of GetMethod
that 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枚举记录。
#2
64
BindingFlags.NonPublic
will not return any results by itself. As it turns out, combining it with BindingFlags.Instance
does the trick.
BindingFlags。非公众将不会自己返回任何结果。事实证明,它与BindingFlags结合在一起。实例就可以了。
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);
#3
46
And if you really want 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);
}
#4
11
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。
#5
10
Microsoft recently modified the reflection API rendering most of these answers obsolete. The following should work on modern platforms (including Xamarin.Forms and UWP):
微软最近修改了反射API,将这些答案中的大部分作废。以下内容应该适用于现代平台(包括Xamarin)。形式和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
obj
theT
generic must be explicitly set to the type of the superclass.如果所需的方法位于obj的超类中,则必须显式地将T泛型设置为超类的类型。
-
If the method is asynchronous you can use
await (Task) obj.InvokeMethod(…)
.如果该方法是异步的,您可以使用等待(任务)。invokemethod(…)
#6
3
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 encapsulation principle and thus exposing your code to the following :
私有成员反射打破了封装原则,从而将您的代码暴露给以下人员:
- Increase complexity of your code because it has to handle the inner behavior of the classes. What is hidden should remain hidden.
- 增加代码的复杂性,因为它必须处理类的内部行为。隐藏的东西应该隐藏起来。
- Makes your code easy to break as it will compile but won't run if the method changed its name.
- 使您的代码容易被破坏,因为它将被编译,但是如果方法更改了它的名称,它将不会运行。
- Makes the private code easy to break because 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.
为了减轻容易中断的问题,最好的方法是通过在将在持续集成构建中运行的单元测试中进行测试来检测任何潜在的中断。当然,这意味着您总是使用相同的程序集(其中包含私有成员)。如果您使用动态加载和反射,您喜欢使用fire,但是您总是可以捕获调用可能产生的异常。
- Mitigate the slowness of reflection:
- 缓解反射的缓慢:
In the recent versions of .Net Framework, CreateDelegate beat by a factor 50 the MethodInfo invoke:
在.Net框架的最新版本中,CreateDelegate比method dinfo所调用的方法多出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
calls will be around 50x faster than MethodInfo.Invoke
use draw
as a standard Func
like that:
抽签将比甲氨蝶呤快50倍。调用使用画图作为一个标准的函数:
var res = draw(methodParams);
Check this post of mine to see benchmark on different method invocations
检查我的这篇文章,查看不同方法调用的基准
#7
2
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.
你能不能为你想要画的每种类型都有一个不同的绘制方法?然后调用重载的绘制方法,传入要绘制的itemType对象。
Your question does not make it clear whether itemType genuinely refers to objects of differing types.
您的问题没有明确说明itemType是否真的指不同类型的对象。
#8
1
I think you can pass it BindingFlags.NonPublic
where it is the GetMethod
method.
我认为你可以通过绑定标志。非公共的,它是GetMethod方法。
#9
0
BindingFlags.NonPublic
BindingFlags.NonPublic
#10
0
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);
}
#11
0
Read this (supplementary) answer (that is sometimes the answer) to understand where this is going and why some people in this thread complain that "it is still not working"
阅读这个(补充的)答案(有时是答案),以了解它将走向何方,以及为什么这篇文章中的一些人抱怨“它仍然不起作用”
I wrote exactly same code as one of the answers here. But I still had an issue. I placed break point on
我写的代码和其中一个答案是一样的。但我还是有个问题。我放了断点。
var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );
It executed but mi == null
它执行了,但是mi = null
And it continued behavior like this until I did "re-build" on all projects involved. I was unit testing one assembly while the reflection method was sitting in third assembly. It was totally confusing but I used Immediate Window to discover methods and I found that a private method I tried to unit test had old name (I renamed it). This told me that old assembly or PDB is still out there even if unit test project builds - for some reason project it tests didn't built. "rebuild" worked
它继续这样的行为,直到我在所有涉及的项目上“重新构建”。我在单元测试一个程序集,而反射方法在第三个程序集。这完全令人困惑,但是我使用了直接窗口来发现方法,并且我发现我尝试的一个私有方法的名称是旧的(我重新命名了它)。这告诉我,即使单元测试项目构建,旧的汇编或PDB仍然存在——出于某种原因,它测试的项目没有构建。“重建”工作
#1
420
Simply change your code to use the overloaded version of GetMethod
that 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枚举记录。
#2
64
BindingFlags.NonPublic
will not return any results by itself. As it turns out, combining it with BindingFlags.Instance
does the trick.
BindingFlags。非公众将不会自己返回任何结果。事实证明,它与BindingFlags结合在一起。实例就可以了。
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);
#3
46
And if you really want 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);
}
#4
11
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。
#5
10
Microsoft recently modified the reflection API rendering most of these answers obsolete. The following should work on modern platforms (including Xamarin.Forms and UWP):
微软最近修改了反射API,将这些答案中的大部分作废。以下内容应该适用于现代平台(包括Xamarin)。形式和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
obj
theT
generic must be explicitly set to the type of the superclass.如果所需的方法位于obj的超类中,则必须显式地将T泛型设置为超类的类型。
-
If the method is asynchronous you can use
await (Task) obj.InvokeMethod(…)
.如果该方法是异步的,您可以使用等待(任务)。invokemethod(…)
#6
3
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 encapsulation principle and thus exposing your code to the following :
私有成员反射打破了封装原则,从而将您的代码暴露给以下人员:
- Increase complexity of your code because it has to handle the inner behavior of the classes. What is hidden should remain hidden.
- 增加代码的复杂性,因为它必须处理类的内部行为。隐藏的东西应该隐藏起来。
- Makes your code easy to break as it will compile but won't run if the method changed its name.
- 使您的代码容易被破坏,因为它将被编译,但是如果方法更改了它的名称,它将不会运行。
- Makes the private code easy to break because 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.
为了减轻容易中断的问题,最好的方法是通过在将在持续集成构建中运行的单元测试中进行测试来检测任何潜在的中断。当然,这意味着您总是使用相同的程序集(其中包含私有成员)。如果您使用动态加载和反射,您喜欢使用fire,但是您总是可以捕获调用可能产生的异常。
- Mitigate the slowness of reflection:
- 缓解反射的缓慢:
In the recent versions of .Net Framework, CreateDelegate beat by a factor 50 the MethodInfo invoke:
在.Net框架的最新版本中,CreateDelegate比method dinfo所调用的方法多出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
calls will be around 50x faster than MethodInfo.Invoke
use draw
as a standard Func
like that:
抽签将比甲氨蝶呤快50倍。调用使用画图作为一个标准的函数:
var res = draw(methodParams);
Check this post of mine to see benchmark on different method invocations
检查我的这篇文章,查看不同方法调用的基准
#7
2
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.
你能不能为你想要画的每种类型都有一个不同的绘制方法?然后调用重载的绘制方法,传入要绘制的itemType对象。
Your question does not make it clear whether itemType genuinely refers to objects of differing types.
您的问题没有明确说明itemType是否真的指不同类型的对象。
#8
1
I think you can pass it BindingFlags.NonPublic
where it is the GetMethod
method.
我认为你可以通过绑定标志。非公共的,它是GetMethod方法。
#9
0
BindingFlags.NonPublic
BindingFlags.NonPublic
#10
0
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);
}
#11
0
Read this (supplementary) answer (that is sometimes the answer) to understand where this is going and why some people in this thread complain that "it is still not working"
阅读这个(补充的)答案(有时是答案),以了解它将走向何方,以及为什么这篇文章中的一些人抱怨“它仍然不起作用”
I wrote exactly same code as one of the answers here. But I still had an issue. I placed break point on
我写的代码和其中一个答案是一样的。但我还是有个问题。我放了断点。
var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );
It executed but mi == null
它执行了,但是mi = null
And it continued behavior like this until I did "re-build" on all projects involved. I was unit testing one assembly while the reflection method was sitting in third assembly. It was totally confusing but I used Immediate Window to discover methods and I found that a private method I tried to unit test had old name (I renamed it). This told me that old assembly or PDB is still out there even if unit test project builds - for some reason project it tests didn't built. "rebuild" worked
它继续这样的行为,直到我在所有涉及的项目上“重新构建”。我在单元测试一个程序集,而反射方法在第三个程序集。这完全令人困惑,但是我使用了直接窗口来发现方法,并且我发现我尝试的一个私有方法的名称是旧的(我重新命名了它)。这告诉我,即使单元测试项目构建,旧的汇编或PDB仍然存在——出于某种原因,它测试的项目没有构建。“重建”工作