IronPython — раскрытие методов с использованием отражения

#c# #reflection #ironpython

#c# #отражение #ironpython

Вопрос:

Я использую IronPython и знаю, как предоставлять методы из моего класса в область действия скрипта:

 m_scope.SetVariable("log", new Action<string>(Log));

public void Log(string a)
{
    Console.WriteLine(a);
}
 

Однако вместо SetVariable того, чтобы каждый раз вызывать, я хочу ускорить процесс с помощью отражения. Итак, я создал атрибут с именем ScriptMethodAttribute :

 public sealed class ScriptMethodAttribute : Attribute
{
    public string Name { get; private set; }

    public ScriptMethodAttribute(string name)
    {
        Name = name;
    }
}
 

Таким образом, я могу определить методы внутри моего класса для использования скриптом, например:

 [ScriptMethod("log")]
public void Log(string a)
{
    Console.WriteLine(a);
}
 

Теперь я хочу вызвать setVariable для каждого метода, который использует этот атрибут, чтобы ускорить процесс. Однако, похоже, это не работает.

Это служебный метод, который возвращает список Tuple<ScriptMethodAttribute, MethodInfo .

 public static IEnumerable<Tuple<TAttribute, MethodInfo>> FindMethodsByAttribute<TAttribute>()
        where TAttribute : Attribute
{
    return (from method in AppDomain.CurrentDomain.GetAssemblies()
                    .Where(assembly => !assembly.GlobalAssemblyCache)
                    .SelectMany(assembly => assembly.GetTypes())
                    .SelectMany(type => type.GetMethods())
                let attribute = Attribute.GetCustomAttribute(method, typeof(TAttribute), false) as TAttribute
                where attribute != null
                select new Tuple<TAttribute, MethodInfo>(attribute, method));
}
 

Это находится в конструкторе класса моего скрипта:

 foreach (var a in Reflector.FindMethodsByAttribute<ScriptMethodAttribute>())
{
    Action action = (Action)Delegate.CreateDelegate(typeof(Action), this, a.Item2);

    m_scope.SetVariable(a.Item1.Name, action);
}
 

Я получаю следующее исключение:

 System.ArgumentException: Cannot bind to the target method because its signature or security transparency is            not compatible with that of the delegate type.
 

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

Ответ №1:

Для обработки методов с одним параметром вы можете использовать этот код:

 var parameters = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();

var delegateType = typeof(Action<>).MakeGenericType(parameters);

var action = methodInfo.CreateDelegate(delegateType, this);

// Now you can call m_scope.SetVariable with action
 

Все становится сложнее, если вы хотите обрабатывать методы с любым количеством параметров, потому что вам нужно добавить некоторое ветвление в зависимости от количества элементов в parameters массиве. Если есть два элемента, вам нужно использовать Action<,> вместо Action<> , если вам нужно использовать три Action<,,> , и так далее.

Не очень элегантным, но быстрым способом было бы предварительно выделить массив для каждого типа действий:

 private Type[] DelegateTypes = new[]
{
    typeof (Action),
    typeof (Action<>),
    typeof (Action<,>),
    typeof (Action<,,>),
    typeof (Action<,,,>),
    typeof (Action<,,,,>),
    typeof (Action<,,,,,>),
    typeof (Action<,,,,,,>),
    typeof (Action<,,,,,,,>),
    typeof (Action<,,,,,,,,>),
    typeof (Action<,,,,,,,,,>),
    typeof (Action<,,,,,,,,,,>),
    typeof (Action<,,,,,,,,,,,>),
    typeof (Action<,,,,,,,,,,,,>),
    typeof (Action<,,,,,,,,,,,,,>),
    typeof (Action<,,,,,,,,,,,,,,>),
    typeof (Action<,,,,,,,,,,,,,,,>)
};
 

Оттуда это просто вопрос доступа к правильному индексу массива, в зависимости от вашего количества параметров:

 Type delegateType;

if (parameters.Length == 0)
    delegateType = DelegateTypes[0];
else
    delegateType = DelegateTypes[parameters.Length].MakeGenericType(parameters);
 

Комментарии:

1. А что, если мой метод вообще не требует никаких параметров? Тогда как бы я получил тип? Кроме того, есть ли способ получить тип на основе количества параметров, чтобы я мог расширить его до любого количества параметров, которое я хочу, и не ограничивать себя одним?

2. Хорошо, я понял. Это то, о чем я думал. Определенно не элегантно, но очень умно.

3. Когда я пытаюсь добавить действие, которое не принимает параметров, я получаю исключение InvalidOperationException, в котором говорится: System. Действие не является определением GenericTypeDefinition . MakeGenericType может вызываться только для типа, для которого Type . IsGenericTypeDefinition имеет значение true.

4. @GilbertWilliams О, действительно, вам нужен особый случай, когда есть 0 параметров. Отредактировано.

5. Большое вам спасибо! Работает безупречно. Я попытаюсь найти гораздо более простой способ сделать это.