C#非常动态的调用



我想用C#写这个:

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

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

我认为这样的事情是可能的使用动态框架。。。我就是不知道怎么。。。

我知道我可以用反射来做到这一点,但它很丑陋,也很烦人。。。


我会稍微解释一下自己。

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

我知道你写过你不喜欢Reflection,但这真的那么丑陋吗?

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,这将不起作用。

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

编辑:代码已更新。它现在可以处理重载的成员和任意数量的参数。只要您知道参数的类型,或者参数已正确初始化为各自的类型,这将起作用。

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)这样的方法,并且你只有一个字符串值要转换为这两个方法中的任何一个,你将需要更多关于你的方法的信息,因为如果所有参数值都是字符串化的,就无法预先知道你应该调用其中的哪一个方法。

使用dynamic关键字时,您确实需要在编译时知道方法名称,但实际编译成的是DLR API调用,方法名称为字符串常量。当然可以自己调用这些,但更棘手的是,dlr的性能取决于在这些api调用的同时创建的静态缓存站点。

开源框架ImpromptuInterface(在Nuget中找到)用一些静态调用方法封装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许可证),如果您不想要依赖关系,您可以随时转到InvokeMember等的源代码来帮助实现SuperDuperInvoke

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

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

现在,这是相当天真的,因为它没有寻找一个特定的方法。最好的办法是获取该名称的所有方法,并向下筛选到具有正确数字参数的方法,然后向下筛选到那些具有可从给定参数的类型中赋值的类型的方法,再选择具有最少upcast的方法。

相关内容

  • 没有找到相关文章

最新更新