我想在C#中写这个:
SomeUnknownType x;
SuperDuperInvoke(x, "MethodName", param1, param2, param3);
SuperDuperInvoke2(x, "MethodName", "param1String", "param2String", "param3String");
获取我一无所知的对象,方法名称和参数列表,然后调用该方法。 SuperDuperInvoke2
假设参数可以从字符串转换。
我认为使用动态框架可以做到这样的事情......我只是找不到......
我知道我可以用反射做到这一点,但它很丑陋而烦人......
我会稍微解释一下。
我想将它用于某些业务服务器的集成测试。 服务器有许多可以处理请求的不同组件,所有组件都加载到IoC容器中。 我需要公开一些主要用于测试的组件,所以我想只接收组件的名称,我应该使用什么参数调用哪种方法,然后调用它。
答案 0 :(得分:4)
我知道你写过你不喜欢反思,但这真的很难看吗?
var result = x.GetType().GetMethod( "MethodName" ).Invoke( x, new object[] { methodParams });
如果方法可能重载,则不能仅按名称去,而是需要知道要调用它的参数数量。像这样的东西
var method = x.GetType()
.GetMethods()
.First(m => m.Name == "MethodName" && m.GetParameters().Length == 2);
var result = method.Invoke( x, new object[] { methodParams });
如果您还需要按类型匹配methodParams,则无效。
答案 1 :(得分:1)
你说你不喜欢使用反射,但是,因为你提到你只知道方法名是一个字符串,所以只有一种方法:反射。这并不像你想象的那么难。这是一种方式:
编辑:代码已更新。它现在适用于重载成员和任意数量的参数。如果你知道参数的类型,或参数是否正确初始化为各自的类型,这将有效。
public static object InvokeMethod(object o, string MethodName, object[] parameters)
{
// get the types of the params
List<Type> paramTypes = new List<Type>();
foreach (object p in parameters)
{
paramTypes.Add(p.GetType());
}
try
{
// get the method, equal to the parameter types
// considering overloading
MethodInfo methodInfo = o.GetType().GetMethod(MethodName, paramTypes.ToArray());
// and invoke the method with your parameters
return methodInfo.Invoke(o, parameters);
}
catch (MissingMethodException e)
{
// discard or do something
}
}
注意:在您的问题文本中,您提到所有参数都可以转换为字符串,并且您将值作为字符串传递。但是,如果你有Calc(int)
和Calc(long)
这样的方法,并且只有一个字符串值可以转换为其中任何一个,那么你需要更多关于你的方法的信息,因为没有办法预先了解如果所有参数值都是字符串化的话,你应该调用哪些方法。
答案 2 :(得分:1)
使用dynamic
关键字时,您需要在编译时知道方法名称,但实际编译的是DLR API调用,方法名称使用字符串常量。当然可以自己调用它们,它变得棘手的是dlr性能依赖于在这些api调用旁边创建的静态缓存站点。
开源框架ImpromptuInterface(在Nuget中找到)用一些static invoke methods包装DLR api。它会缓存缓存网站,因此它不如动态关键字快,但它比反射速度至少快2倍。唯一的技巧是,如果你试图调用一个void返回方法,期望一个值,它会在尝试绑定时抛出异常。示例实现:
public static dynamic SuperDuperInvoke(object target, string methodName, params object[] args){
try{
return Impromptu.InvokeMember(target, methodName, args);
}catch(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException){
Impromptu.InvokeMemberAction(target, methodName, args);
return null;
}
}
由于它是开源(apache许可证),如果您不想要依赖,您可以随时转到source of InvokeMember etc以帮助实施SuperDuperInvoke
。
SuperDuperInvoke2
更难,因为DLR将根据参数类型尝试匹配方法,它会考虑隐式转换,但只会静态定义TryConvert
DynamicObject
SuperDuperInvoke2
1}}无法工作),因此您需要一个具有静态定义的隐式转换为所有预期类型的代理,这可能是危险的,注意方法重载,它们可能与public static dynamic SuperDuperInvoke2(object target, string methodName, params ConvertableProxy[] args){
return SuperDuperInvoke(target,methodName,args);
}
public class ConvertableProxy{
private IConvertible _value;
public ConvertableProxy(IConvertible value){
_value =value;
}
//Automatically convert strings to proxy
public static implicit operator ConvertableProxy(string value){
return new ConvertableProxy(value);
}
public static implicit operator bool(ConvertableProxy proxy)
{
return proxy._value.ToBoolean(null);
}
public static implicit operator int(ConvertableProxy proxy)
{
return proxy._value.ToInt32(null);
}
public static implicit operator string(ConvertableProxy proxy)
{
return proxy._value.ToString(null);
}
//.. Add Char, DateTime, etc.
}
不明确
{{1}}
答案 3 :(得分:0)
如果没有动态,你会这样做:
public static SuperDuperInvoke(object o, string methodName, params object parameters)
{
Type t = o.GetType();
MethodInfo mi = t.GetMethod(methodName);
if (mi == null) throw new Exception("no such method: " + methodName);
mi.invoke(mi, o, parameters.Length == 0 ? null : parameters);
}
现在这很天真,因为它没有寻找特定的方法。最好的办法是获取该名称的所有方法并过滤到具有正确数字参数的方法,然后将这些方法过滤到具有可从给定参数类型分配的类型的那些方法,然后选择具有最小向上的类型的方法。