C# 非常动态的调用

发布于 2024-12-27 01:10:24 字数 515 浏览 2 评论 0原文

我想用 C# 编写:

SomeUnknownType x;

SuperDuperInvoke(x, "MethodName", param1, param2, param3);

SuperDuperInvoke2(x, "MethodName", "param1String", "param2String", "param3String");

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

我认为使用动态框架可以实现这样的事情...我只是找不到如何...

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


我会解释一下自己。

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

I want to write this in C#:

SomeUnknownType x;

SuperDuperInvoke(x, "MethodName", param1, param2, param3);

SuperDuperInvoke2(x, "MethodName", "param1String", "param2String", "param3String");

Get some object I know nothing about, a method name, and a list of parameters, and just call the method. SuperDuperInvoke2 assumes the parameters are convertible from string.

I assume something like this is possible using the dynamic framework... I just can't find how...

I know I can do this with Reflection, but it's ugly and annoying...


I'll explain myself a little.

I want to use this for integration testing of some business server.
The server has a lot of different components that can handle requests, all loaded into an IoC container.
I need to expose some of thees components, mostly for testing, so I want to just receive the name of the component, what method i should call with what parameters and just call it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

野稚 2025-01-03 01:10:24

我知道你写过你不喜欢 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,则这将不起作用。

I know you wrote that you don't like Reflection but is this really all that ugly?

var result = x.GetType().GetMethod( "MethodName" ).Invoke( x, new object[] { methodParams });

If the method could be overloaded you can't go by name only but also need to know how many parameters you are going to invoke it with. Something like this

var method = x.GetType()
              .GetMethods()
              .First(m => m.Name == "MethodName" && m.GetParameters().Length == 2);
var result = method.Invoke( x, new object[] { methodParams });

This will not work if you also need to match your methodParams by type.

掌心的温暖 2025-01-03 01:10:24

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

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

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

You say that you do not like to use reflection, however, since you mention that you only know the methodname as a string, there's only one way: reflection. It's not so hard as you might think. Here's one way:

EDIT: code updated. It now works with overloaded members and any number of parameters. Provided that you know either the type of the parameters, or the parameters are properly initialized to their respective types, this will work.

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

Note: in your question-text you mention that all params are convertible to string and that you pass the values as strings. But if you have a method like Calc(int) and Calc(long) and you have only a string value to be converted to either of these, you'll need more info about your method because there's no way to know up front which of those methods you should call if all parameters values are stringized.

-残月青衣踏尘吟 2025-01-03 01:10:24

使用 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 将尝试根据参数类型匹配方法,它将考虑隐式转换,但仅静态定义转换(TryConvertDynamicObject 上不起作用),因此您需要一个静态定义的隐式转换为所有预期类型的​​代理,这可能是危险的,请注意方法重载,它们可能会产生歧义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.


}

When using the dynamic keyword you do need to know the method name at compile time, however what that actually compiles down into, are DLR API calls with a string constant for the method name. It's certainly possible to call these yourself, where it become tricky is that the dlr performance is dependent on static caching sites created along side these api calls.

The open source framework ImpromptuInterface (found in Nuget) wraps the DLR api with some static invoke methods. It hashes the caching sites, so it's not as fast as the dynamic keyword, but it's at least 2x faster than reflection. The only trick is that if you try to call a void returning method expecting a value it throws an exception when trying to bind. Example implementation:

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

Since it's open source (apache license) you can always go to the source of InvokeMember etc to help implement your SuperDuperInvoke, if you don't want the dependency.

SuperDuperInvoke2 is more difficult because the DLR is going to try and match the method based on the argument types, it will take into account implicit conversions, but only statically define ones (TryConvert on DynamicObject won't work), so you would then need a proxy that has staticaly defined implicit converts to all your expected types, that can be dangerous watch out for method overloads they will likely be ambiguous to 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.


}
梦明 2025-01-03 01:10:24

如果没有动态,您将这样做:

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

现在这是相当幼稚的,因为它不寻找特定的方法。最好的办法是获取该名称的所有方法,并过滤到具有正确数量参数的方法,然后过滤到具有可从给定参数类型分配的类型的方法,然后选择向上转换最少的方法。

Without dynamic, you would do this:

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

Now this is fairly naive in that it isn't looking for a specific method. Best bet is to instead get all methods of that name and filter down to the ones that have the right number parameters then filter those down to the ones that have types that are assignable from the given parameter's types and then select the one with the least upcasts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文