2017-09-25 1 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; 

    } 


} 

시험 방법 : invoke 메소드를 추가

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

감사합니다! 그것은 현재 솔루션보다 훨씬 좋아 보인다! 또 다른 질문 :이 코드가 다른 코드 (예 : 루프에서 여기에 내보내지는 함수를 호출하는 스크립트)에서 여러 번 실행되는 경우 성능 (리플렉션 사용)이 문제가됩니까? –

+0

리플렉션은 일반적으로 성능 저하라는 나쁜 평판을 가지고 있습니다. 개인적으로 생각해 보면 매우 과장되어 있거나 .NET 초기 단계의 빈약 한 구현 잔여 물입니다. 그러나 성능에 관심이 있다면 생성자의 사전에 methodinfo 멤버를 구문 분석하고 저장하여 대신 사용할 수 있습니다. – Micael

관련 문제