可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Right now, I have code that looks something like this:
Private Sub ShowReport(ByVal reportName As String)
Select Case reportName
Case "Security"
Me.ShowSecurityReport()
Case "Configuration"
Me.ShowConfigurationReport()
Case "RoleUsers"
Me.ShowRoleUsersReport()
Case Else
pnlMessage.Visible = True
litMessage.Text = "The report name """ + reportName + """ is invalid."
End Select
End Sub
Is there any way to create code that would use my method naming conventions to simplify things? Here's some pseudocode that describes what I'm looking for:
Private Sub ShowReport(ByVal reportName As String)
Try
Call("Show" + reportName + "Report")
Catch ex As Exception
'method not found
End Try
End Sub
回答1:
Type type = GetType();
MethodInfo method = type.GetMethod("Show"+reportName+"Report");
if (method != null)
{
method.Invoke(this, null);
}
This is C#, should be easy enough to turn it into VB. If you need to pass parameter into the method, they can be added in the 2nd argument to Invoke.
回答2:
You've got a deeper problem. Your strings are too important. Who is passing you strings? can you make them not do that?
Stick with the switch statement, as it decouples your internal implementation (method names) from your external view.
Suppose you localize this to German. You gonna rename all those methods?
回答3:
Reflection API allows you to get a MethodInfo
from a method, then calling Invoke
dynamically on it. But it is overkill in your case.
You should consider having a dictionary of delegates indexed by strings.
回答4:
You could use reflection to do this but to be honest I think it's overcomplicating things for your particular scenario i.e. code and switch() in the same class.
Now, if you had designed the app to have each report type in its own assembly (kinda like an add-in/plugin architecture) or bundled in a single external assembly then you could load the reporting assemblie(s) into an appdomain and then use reflection to do this kinda thing.
回答5:
Use reflection. In the System.Reflection
namespace you need to get a MethodInfo
object for the method you want, using GetMethod("methodName")
on the type containing the method.
Once you have the MethodInfo
object, you can call .Invoke()
with the object instance and any parameters.
For Example:
System.Reflection.MethodInfo method = this.GetType().GetMethod("foo");
method.Invoke(this, null);
回答6:
You can use reflection. Though personally, I think you should just stick with the switch statement.
private void ShowReport(string methodName)
{
Type type = this.GetType();
MethodInfo method = type.GetMethod("Show"+methodName+"Report", BindingFlags.Public)
method.Invoke(this, null);
}
Sorry, I'm doing C#. Just translate it to VB.NET.
回答7:
Python (and IronPython) can do this thing very easily. With .Net though, you need to use reflection.
In C#: http://www.dotnetspider.com/resources/4634-Invoke-me-ods-dynamically-using-reflection.aspx
My quick port to VB.Net:
Private Sub InvokeMethod(instance as object, methodName as string )
'Getting the method information using the method info class
Dim mi as MethodInfo = instance.GetType().GetMethod(methodName)
'invoing the method
'null- no parameter for the function [or] we can pass the array of parameters
mi.Invoke(instance, Nothing)
End Sub
回答8:
If i understand the question correctly, you'll have to use Reflection to find the method "show" + reportName and then invoke it indirectly:
Half-baked example:
Case "financial" :
{
Assembly asm = Assembly.GetExecutingAssembly ();
MethodInfo mi = asm.GetType ("thisClassType").GetMethod ("showFinancialReport");
if (mi != null)
mi.Invoke (null, new object[] {});
}
Insert your own logic there to make up the name for the method to call.
See MSDN documentation of MethodInfo and Assembly for details.
回答9:
Using reflection:
Type t = this.GetType();
try
{
MethodInfo mi = t.GetMethod(methodName, ...);
if (mi != null)
{
mi.Invoke(this, parameters);
}
}
But I agree with ago, better not change your original code ;-)
回答10:
The "Closest to your question" solution.
You could make delegates out of those reports, and call them by looking up the matching String in a Hashtable:
Public Sub New()
'...
ReportTable.Add("Security", New ReportDelegate(AddressOf ShowSecurityReport))
ReportTable.Add("Config", New ReportDelegate(AddressOf ShowConfigReport))
ReportTable.Add("RoleUsers", New ReportDelegate(AddressOf ShowRoleUsersReport))
'...
End Sub
Private Sub ShowSecurityReport()
'...
End Sub
Private Sub ShowConfigReport()
'...
End Sub
Private Sub ShowRoleUsersReport()
'...
End Sub
Private Delegate Sub ReportDelegate()
Private ReportTable As New Dictionary(Of String, ReportDelegate)
Private Sub ShowReport(ByVal reportName As String)
Dim ReportToRun As ReportDelegate
If ReportTable.TryGetValue(reportName, ReportToRun) Then
ReportToRun()
Else
pnlMessage.Visible = True
litMessage.Text = "The report name """ + reportName + """ is invalid."
End If
End Sub
That way you can add as many reports as you like, and your ability to reflect them, and the perf hit of reflection, aren't an issue.
回答11:
You can, using System.Reflection. See this code project article for more information.
string ModuleName = "TestAssembly.dll";
string TypeName = "TestClass";
string MethodName = "TestMethod";
Assembly myAssembly = Assembly.LoadFrom(ModuleName);
BindingFlags flags = (BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
Module [] myModules = myAssembly.GetModules();
foreach (Module Mo in myModules)
{
if (Mo.Name == ModuleName)
{
Type[] myTypes = Mo.GetTypes();
foreach (Type Ty in myTypes)
{
if (Ty.Name == TypeName)
{
MethodInfo[] myMethodInfo = Ty.GetMethods(flags);
foreach(MethodInfo Mi in myMethodInfo)
{
if (Mi.Name == MethodName)
{
Object obj = Activator.CreateInstance(Ty);
Object response = Mi.Invoke(obj, null);
}
}
}
}
}
}
回答12:
Worked for me in VB .Net MSVS 2015
Dim tip As Type = GetType(MODULENAME)'if sub() or function() in module
Dim method As MethodInfo = tip.GetMethod("MaxV") 'name of function (gets 2 params double type)
Dim res As Double = 0 'temporary variable
If (Not Nothing = method) Then 'if found function "MaxV"
res = method.Invoke(Me, New Object() {10, 20})
End If
MsgBox(res.ToString())