如何在c#中指定插件函数的默认值

本文关键字:插件 函数 默认值 | 更新日期: 2023-09-27 18:15:08

我正在尝试实现一个简单的插件系统,它将允许人们写以下内容:

[Plugin("A plugin function")]
public static int PluginFunction(int a, int b)
{
    return a + b;
}

,然后将包含此函数的DLL放到一个文件夹中,应用程序将对其进行扫描,并在运行时显示为可用函数。这一切都很好,到目前为止一切都很好,PluginAttribute类是你所期望的,只是一个函数的描述字符串。

然而,我希望允许插件作者为参数指定默认值。这是OK的值在编译时是恒定的,然后通过反射推断,但我想有一种方法来指定默认值为更复杂的类型,将在运行时创建。有人实现过类似的东西吗?主要目标是使实现插件功能变得简单-我试图避免复杂的脚手架,但如果我想要这个功能,我接受我漂亮的简单系统不会削减它。我也很高兴在应用程序代码中有一些复杂性,这使得系统在插件作者看来很简单。

谢谢,查理。


更新:

我将使用这里建议的组合,最接近的是Peter O.提出的-这是一个版本:

[Plugin("A plugin function")]
[Defaults(typeof(AdderDefaults))]
public static int Adder(int a, int b)
{
    return a + b;
}
public static class AdderDefaults
{
    public static int a { get { return 1; } }
    public static int b { get { return 2; } }
}
[Plugin("Another plugin function")]
[Defaults(typeof(TexturizerDefaults))]
public static Bitmap Texturize(Bitmap source, Point offset)
{
    return result;
}
public static class TexturizerDefaults
{
    // no default for source parameter
    public static Point offset { get { return new Point(16, 16); } }
}

允许跳过参数并通过名称指定参数。没有编译时检查,但没关系-在运行时检查这些是可以接受的。

如何在c#中指定插件函数的默认值

也许您可以创建一个引用类型的属性包含插件的默认值。例子:

[PluginDefaults(typeof(MyPluginDefaults))]

类MyPluginDefaults可能看起来像:

public class MyPluginDefaults {
     int Parameter1 { // First parameter
         get { return 0; } // default value for 'a'
     }
     int Parameter2 { // Second parameter
         get { return 4; } // default value for 'b'
     }
     // Additional parameters would be called Parameter3, Parameter4, and so on.
}

有很多方法可以做到这一点,最简单的是使用一个简单的约定:

[Plugin("A plugin function")]
public static int PluginFunction(int a, int b)
{
    return a + b;
}
public static object[] PluginFunctionDefaultArguments()
{
    return new [] { 0, 0 };
}

每次找到一个标记为PluginAttribute的函数时,搜索一个具有与DefaultArguments后缀相同的名称,没有参数且返回类型为object[]的函数。然后调用它并将值存储在某个地方。您还应该支持使用专用的c#/VB语法指定的默认值(可以在参数的DefaultValue成员中找到)

一种方法是为每个类设置属性Defaults。它返回一个可以查询默认值的对象,例如:

object[] pluginFunctionDefaults = FooPlugin.Defaults["PluginFunction"];

(显然,您的应用程序中不会有这样的代码。)

默认值的声明可以像这样:

class FooPlugin
{
    static FooPlugin()
    {
        var bar = new Bar();
        Defaults = new DefaultValues()
            .Add(() => PluginFunction(42, 13))
            .Add(() => AnotherFunction(bar));
    }
    public static DefaultValues Defaults { get; private set; }
    // actual methods of the class
}

使用这样的表达式意味着在编译时检查默认值的类型。DefaultValues类解析表达式并存储参数。它可以看起来像这样:

class DefaultValues
{
    private readonly Dictionary<string, object[]> m_expressions =
        new Dictionary<string, object[]>();
    public DefaultValues Add<T>(Expression<Func<T>> func)
    {
        var methodCall = ((MethodCallExpression)func.Body);
        var name = methodCall.Method.Name;
        var arguments =
            methodCall.Arguments
                .Select(Evaluate)
                .ToArray();
        m_expressions.Add(name, arguments);
        return this;
    }
    private static object Evaluate(Expression expression)
    {
        return Expression.Lambda<Func<object>>(
            Expression.Convert(expression, typeof(object)))
            .Compile()();
    }
    public object[] this[string methodName]
    {
        get { return m_expressions[methodName]; }
    }
}