【问题标题】:IronPython - Exposing methods using reflectionIronPython - 使用反射公开方法
【发布时间】:2016-10-16 07:51:52
【问题描述】:

我正在使用 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&lt;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 类中获取它们。

【问题讨论】:

    标签: c# reflection ironpython


    【解决方案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&lt;,&gt; 而不是Action&lt;&gt;,如果有三个则需要使用Action&lt;,,&gt;,以此类推。

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

    【讨论】:

    • 如果我的方法根本不需要参数怎么办?那么我将如何获得类型?另外,有没有办法根据参数计数来获取类型,这样我就可以将它扩展到我想要的任何参数计数,而不是限制自己?
    • 好的,我明白了。这就是我想做的事情。绝对不优雅但非常聪明。
    • 当我尝试添加一个不带参数的操作时,我得到一个 InvalidOperationNException,它说:System.Action 不是 GenericTypeDefinition。 MakeGenericType 只能在 Type.IsGenericTypeDefinition 为 true 的类型上调用。
    • @GilbertWilliams 哦,确实,当参数为 0 时,您需要一个特殊情况。已编辑。
    • 非常感谢!完美运行。我会尝试找出一个更简单的方法来做到这一点。
    猜你喜欢
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多