C # очень динамичный вызов - PullRequest
       1

C # очень динамичный вызов

3 голосов
/ 12 января 2012

Я хочу написать это на C #:

SomeUnknownType x;

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

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

Получите какой-нибудь объект, о котором я ничего не знаю, имя метода и список параметров, и просто вызовите метод. SuperDuperInvoke2 предполагает, что параметры могут быть преобразованы из строки.

Полагаю, что-то подобное возможно с помощью динамического фреймворка ... Я просто не могу найти, как ...

Я знаю, что могу сделать это с помощью Reflection, но это уродливо и раздражает ...


Я немного объясню.

Я хочу использовать это для тестирования интеграции бизнес-сервера. На сервере есть много различных компонентов, которые могут обрабатывать запросы, все они загружены в контейнер IoC. Мне нужно предоставить некоторые из этих компонентов, в основном для тестирования, поэтому я хочу просто получить имя компонента, какой метод мне следует вызывать с какими параметрами и просто вызвать его.

Ответы [ 4 ]

4 голосов
/ 12 января 2012

Я знаю, что вы написали, что вам не нравится 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 по типу.

1 голос
/ 13 января 2012

При использовании ключевого слова dynamic вам необходимо знать имя метода во время компиляции, однако то, во что он в действительности компилируется, - это вызовы DLR API со строковой константой для имени метода.Конечно, это можно назвать самим, но тут сложнее то, что производительность dlr зависит от статических сайтов кэширования, созданных вместе с этими вызовами API.

Фреймворк с открытым исходным кодом ImpromptuInterface (находится в Nuget) обертывает API DLR с помощью некоторых статических методов вызова .Он хэширует сайты кеширования, поэтому он не так быстр, как динамическое ключевое слово, но по крайней мере в 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 попытается сопоставить метод, основанный на типах аргументов, он будет учитывать неявные преобразования, но только статически определять их (TryConvert на DynamicObjectне сработает), поэтому вам понадобится прокси, который имеет статически определенные неявные преобразования во все ваши ожидаемые типы, что может быть опасно, если вы перегрузите методы, они, вероятно, будут неоднозначными для 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.


}
1 голос
/ 12 января 2012

Вы говорите, что не любите использовать отражение, однако, поскольку вы упоминаете, что вы знаете только имя метода как строку, есть только один способ: отражение.Это не так сложно, как вы думаете.Вот один из способов:

РЕДАКТИРОВАТЬ: код обновлен.Теперь он работает с перегруженными членами и любым количеством параметров.При условии, что вы знаете либо тип параметров, либо параметры должным образом инициализированы для их соответствующих типов, это будет работать.

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), и у вас есть только строковое значение, которое нужно преобразовать в любой из них, вам потребуется дополнительная информация о вашем методе, потому что нет способа узнать заранее, какой из этих методовВы должны позвонить, если все значения параметров являются строковыми.

0 голосов
/ 12 января 2012

Без динамики вы бы сделали это:

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

Теперь это довольно наивно в том смысле, что он не ищет определенного метода. Лучше всего вместо этого получить все методы с таким именем и отфильтровать их до тех, которые имеют правильные числовые параметры, затем отфильтровать их до тех, которые имеют типы, которые можно назначить из типов данного параметра, и затем выбрать метод с наименьшим количеством повышений. .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...