C#非常动态的调用

我想用C#写这个:

SomeUnknownType x; SuperDuperInvoke(x, "MethodName", param1, param2, param3); SuperDuperInvoke2(x, "MethodName", "param1String", "param2String", "param3String"); 

获取我一无所知的对象,方法名称和参数列表,然后调用该方法。 SuperDuperInvoke2假设参数可以从字符串转换。

我假设使用动态框架这样的事情是可能的…我只是找不到如何……

我知道我可以用reflection做到这一点,但它很丑陋而烦人……


我会解释一下自己。

我想用它来进行某些业务服务器的集成测试。 服务器有许多可以处理请求的不同组件,所有组件都加载到IoC容器中。 我需要暴露一些组件,主要用于测试,所以我想只接收组件的名称,我应该用什么参数调用什么方法,然后调用它。

我知道你写过你不喜欢反思,但这真的很难看吗?

 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,则此方法无效。

你说你不喜欢使用reflection,但是,因为你提到你只知道方法名是一个字符串,所以只有一种方法:reflection。 这并不像你想象的那么难。 这是一种方式:

编辑:代码更新。 它现在适用于重载成员和任意数量的参数。 如果您知道参数的类型,或者参数已正确初始化为各自的类型,则可以使用。

 public static object InvokeMethod(object o, string MethodName, object[] parameters) { // get the types of the params List paramTypes = new List(); 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)并且你只有一个字符串值要转换为其中任何一个,你需要更多关于你的方法的信息,因为没有办法知道前面哪个如果所有参数值都是字符串化的,那么应该调用这些方法。

使用dynamic关键字时,您需要在编译时知道方法名称,但实际编译的是DLR API调用,方法名称使用字符串常量。 当然可以自己调用它们,因为dlr性能依赖于在这些api调用旁边创建的静态缓存站点。

开源框架ImpromptuInterface(在Nuget中找到)用一些静态调用方法包装DLR api。 它会缓存缓存网站,因此它不如动态关键字快,但它至少比reflection速度快2倍。 唯一的技巧是,如果你试图调用一个void返回方法,期望一个值,它会在尝试绑定时抛出exception。 示例实现:

 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许可证),如果你不想要依赖,你总是可以去InvokeMember等源来帮助实现你的SuperDuperInvoke

SuperDuperInvoke2更难,因为DLR将尝试匹配基于参数类型的方法,它将考虑隐式转换,但只静态定义一个( DynamicObject上的TryConvert将无法工作),因此您需要一个具有静态定义的代理隐式转换为所有预期类型,这可能是危险的,注意方法重载,它们可能对SuperDuperInvoke2不明确。

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

没有动态,你会这样做:

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

现在这很天真,因为它没有寻找特定的方法。 最好的办法是获取该名称的所有方法并过滤到具有正确数字参数的方法,然后将这些方法过滤到具有可从给定参数的类型分配的类型的那些,然后选择具有最小向上的类型的那些。