如何通过名称直接调用方法?

时间:2017-09-25 00:38:43

标签: c# design-patterns

我正在项目中实现类似解释器的功能。目标是允许此库的用户调用Invoke(command, param1, param2, param3...)之类的东西来调用不同的命令。每个命令都是该类的方法。

我目前的实施方式如下:

class MyTest: IInvokable {
    public void Command1(string pa)
    {
        throw new NotImplementedException();
    }
    public int Command2(string pa, int a)
    {
        throw new NotImplementedException();
    }
    public string Command3()
    {
        throw new NotImplementedException();
    }
    public CommandResult Invoke(string cmd, params object[] p)
    {
        switch(cmd)
        {
            case "Command1":
            case "Command1Alias":
                return new CommandResult(this.Command1(p[0].ToString()));
                break;
            case "Command2":
                *** omitted ***
        }
    }
}

巨人switch-case对我来说真的很傻。我看了Command Pattern,但不知道它是否适用于此。有什么建议让代码更好吗?

1 个答案:

答案 0 :(得分:1)

首先,定义两个属性来标识允许调用的方法,以及设置方法别名的能力:

public class CommandAttribute : Attribute
{
}

[System.AttributeUsage(validOn: System.AttributeTargets.Method, AllowMultiple = true)]
public class CommandAliasAttribute : Attribute
{
    public CommandAliasAttribute(string alias)
    {
        Alias = alias;
    }

    public string Alias { get;}
}

现在我们可以使用它来标记可调用的方法:

public class Test
{
    [Command]
    [CommandAlias("Method1Alias")]
    public void Method1()
    {
        System.Console.WriteLine("Method1");
    }

    [Command]
    [CommandAlias("Method2Alias")]
    public void Method2()
    {
        System.Console.WriteLine("Method2");
    }

    public void NonInvokableMethod()
    {
        System.Console.WriteLine("NonInvokableMethod");
    }

}

最后,让我们添加调用方法:

public class Test
{
    [Command]
    [CommandAlias("Method1Alias")]
    public void Method1()
    {
        System.Console.WriteLine("Method1");
    }

    [Command]
    [CommandAlias("Method2Alias")]
    public void Method2()
    {
        System.Console.WriteLine("Method2");
    }

    public void NonInvokableMethod()
    {
        System.Console.WriteLine("NonInvokableMethod");
    }

    public object Invoke(string cmd)
    {
        var type = GetType();

        var methodinfo = type.GetMethods().SingleOrDefault(x =>
            x.GetCustomAttribute(typeof(CommandAttribute)) != null //Only allow methods with the Command attribute
            &&
            (
                x.Name == cmd //Match method name
                || x.GetCustomAttributes(typeof(CommandAliasAttribute)) //Match alias
                    .Select(attr => attr as CommandAliasAttribute) //type cast to CommandAlias
                    .Any(attr => attr.Alias == cmd)
            ));

            if (methodinfo == null)
                throw new InvalidOperationException($"No method named or aliased \"{cmd}\" was found.");

            var ret = methodinfo.Invoke(this, new object[0]);

            return ret;

    }


}

测试方法:

void Main()
{
    var test = new Test();

    test.Invoke("Method1");
    test.Invoke("Method1Alias");

    try
    {
        test.Invoke("MethodX");
    }
    catch (Exception e)
    {
        System.Console.WriteLine(e.Message);
    }

    try
    {
        test.Invoke("NonInvokableMethod");
    }
    catch (Exception e)
    {
        System.Console.WriteLine(e.Message);
    }

}

此示例不包括参数的使用,但我认为您可以弄清楚如何调整invoke方法以支持它。请记住,例如,如果您希望从命令提示符调用方法,则需要将参数从string转换为各自的参数类型。否则,在调用方法时会出现异常。