IronPython - 使用反射公开方法

时间:2016-10-16 07:51:53

标签: c# reflection 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.

我猜它是因为我必须在Action构造函数中包含所需的类型,但我不知道如何从MethodInfo类中获取它们。

1 个答案:

答案 0 :(得分:0)

要使用一个参数处理方法,可以使用以下代码:

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<,,>,依此类推。

一种不那么优雅但快速的方法是使用每种类型的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);