2017-09-25 12 views
0

私はインタプリタのような機能をプロジェクトに実装しています。目的は、このライブラリのユーザがInvoke(command, param1, param2, param3...)のようなものを呼び出して、別のコマンドを呼び出せるようにすることです。各コマンドはクラスのメソッドです。メソッドをelegently名前で呼び出す方法?

私の現在の実装では、同様である。

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を見ましたが、ここで動作するかどうかわかりません。コードを改善するための提案はありますか?

+0

gotosを使用してください。 C#にはそれらがあります。 – nicomp

+0

私は、アトリビュートを使用してリビジョンを使用してそのアトリビュートを持つすべてのメソッドを見つけ、次にInvokeでそれを辞書から調べることをお勧めします –

+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); 
    } 

} 

この例ではパラメータの使用は含まれていませんが、私はあなたがそれをサポートするために呼び出しメソッドを調整する方法を理解できると思います。たとえば、コマンドプロンプトからメソッドを呼び出す場合は、文字列からそれぞれのパラメータ型にパラメータを型変換する必要があります。それ以外の場合は、メソッドを呼び出すときに例外が発生します。

+0

ありがとうございました!それは私の現在の解決策よりもはるかに良いようです!もう1つの質問:このコードが別のコード(たとえばループ内でエクスポートされた関数を呼び出すスクリプトなど)で何度も実行されている場合、パフォーマンス(リフレクションを使用)は問題になりますか? –

+0

Reflectionは、通常、パフォーマンスが悪いという評判が悪いです。個人的には非常に誇張されていると思いますが、.NETの初期段階では実装の貧弱さが残っています。しかし、パフォーマンスに懸念がある場合は、コンストラクタ内の辞書にmethodinfoメンバを解析して格納し、その代わりに使用することができます。 – Micael

関連する問題