2011-01-05 5 views
1

저는 우리의 웹 서비스를 비동기 모델로 변경하고 있습니다. 그리고 그것을 위해 나는 100 가지 이상의 방법을 바꾸어야합니다.프로그래밍 방식으로 변경되는 코드 파일

수동으로 수행하는 것은 매력이없는 옵션입니다. 프로그래밍 방식으로 & 여러 함수/코드 파일을 구문 분석 할 수있는 방법이 있습니까?

예 :

[Webmethod] 
public void MyWebservice (string parameter1, string parameter2, string parameter3) 
{ 
    //Logic here 
} 

그리고로 변경 :

public void InternalMyWebservice (string parameter1, string parameter2, string parameter3, AsyncCallback callback) 
{ 
    //Logic here 
} 

[Webmethod] 
public void BeginMyWebservice (string parameter1, string parameter2, string parameter3, AsyncCallback callback, object asyncState) 
{ 
    //Queue InternalMyWebservice in a threadpool 
} 

public void EndMyWebservice(IAsyncResult asyncResult) 
{ 
    //Set return values 
} 

그것은 기본적으로 내가 각 웹 서비스에 대해해야 할 같은 일입니다. 이름을 "InternalX"로 변경하고 매개 변수를 추가하고 begin & 끝 방법을 만듭니다.

답변

2

Resharper가 해결책을 찾으십시오. Regex로 대체 할 텍스트가 없으면 시도하십시오.

+0

어떻게 이것을 재사용 할 수 있습니까? – Carra

+0

현재 메서드를 가져 와서 이름을 변경하고 새 이름으로 복제하는 템플릿을 만듭니다. –

+0

이렇게하면 최소한의 작업으로 훨씬 쉽게 작업을 수행 할 수 있습니다. 각 기능에 대한 webmethod 이름 만 입력하면됩니다. – Carra

0

실제로 .NET 프레임 워크에 익숙하지 않지만 정규식을 사용하여 확실히 수행 할 수 있습니다.

+0

문제가 커지면 regex에 달라 붙지 않아야합니다. 그들은 이것을 위해 일할 수도 있지만 일반적인 경우에는 그런 일을하기에는 너무 잘하지 않습니다. – delnan

+0

필자는 그렇게하지 않을 것입니다. 함수 선언 구문은 매우 복잡합니다. 매개 변수, 매개 변수의 특성, 중첩 된 제네릭 형식 등의 주석을 잊어 버린 경우 정규식은 경고 또는 오류없이 메서드를 무시합니다. C#은 정규 언어가 아니며 (이 하위 집합도 아님) 구문 분석을 사용합니다. 당신은 그것을 분석하고 싶다. – Niki

+0

문제가 있습니까? 정규식을 사용하십시오. 이제 두 가지 문제가 있습니다 :) – Carra

1

작성시 기존 클래스의 객체 (또는 델리게이트)를 가져오고 필요한 메소드를 비동기 적으로 호출하는 래퍼 클래스를 작성하지 않는 이유는 무엇입니까? 기존 클래스의 메소드는 여전히 동기식 일 수 있습니다.

5

CSharpCodeProvider.Parse 메서드를 사용하여 코드의 객체 지향 표현 인 CodeCompileUnit 인스턴스를 생성 할 수 있어야합니다. 이를 통해 메소드를 상세하게 분석하고, 인수를 변경하고, 새로운 메소드 및 항목을 추가 할 수 있으며, 작업이 끝나면 코드를 텍스트 파일로 다시 저장할 수 있습니다. 당신은 수정 된 CodeCompileUnit

Provides access to instances of the C# code generator and code compiler.

+0

+1은'CSharpCodeProvider.Parse'를 제안했지만 CodeDOM을 사용하여 코드를 생성하는 것은 거대한 피타입니다 – Niki

+0

어렵다는 것을 알지 못합니다. 그리고 이것은 매우 어렵습니다. 그래서 이것은 관리 할 수 ​​있어야합니다 –

+0

어려운 말은하지 않았지만 엉덩이에 고통이 있다고 말했습니다 .--) C# 코드 한 줄을 생성하는 데 일반적으로 3-10 줄의 CodeDom 코드가 필요합니다. 템플릿 엔진을 사용하는 경우 한 줄을 작성해야합니다. 생성하려는 줄. – Niki

2

당신은 ANTLR 같은 파서 생성기를 사용할 수 있습니다 통과 CodeDomProvider.GenerateCodeFromCompileUnit를 호출하여 코드를 생성합니다. 클래스 및 메서드 선언을 구문 분석하고 메서드 코드를 무시하는 C# 하위 집합에 대한 ANTLR 문법을 작성하는 것은 어렵지 않습니다. 또는 ANTLR 사이트의 C# 문법 중 하나를 사용할 수 있습니다.

ANTLR에는 원하는 작업에 매우 근접한 "다시 쓰기 문법"(예 : this question보기)이라는 기능이 있습니다.

하지만 개인적으로 나는 실제 메서드를 사용하여 하나의 파일에 생성 된 메서드를 넣지 않을 것입니다. 생성 된 코드에서 버그를 발견하여 다시 생성하려는 경우 파서는 이전에 생성 한 메소드를 인식해야하기 때문에 더 복잡해집니다. 그리고 생성 된 방법을 편집하려는 유혹은 매우 높습니다. 또한, 그것은 단일 책임 원리를 위반하는 것 같지만 맛의 문제 일 수 있습니다.

나는 생성 된 메서드를 별도의 파일 (파생 클래스 또는 부분 클래스 선언)에 넣습니다. 이것은 파서가 필요하지 않은 장점이 있습니다. 생성되지 않은 클래스 파일이 컴파일 될 경우 (아마도 추상 또는 부분 메소드 선언이 포함 된 경우)이를 컴파일하고 단순히 잘 알려진 리플렉션 메커니즘을 사용하여 원하는 정보. 코드를 생성하려면 StringTemplate 또는 T4과 같은 템플릿 프레임 워크 만 있으면됩니다.

1

아래 코드는 대체 작업을 수행하지만 입력 소스 파일의 형식에 크게 의존합니다.

가정

  • WEBMETHOD 프리픽스 '공공 무효'
  • 파라미터
  • 개폐 브라켓 ({})의 각 행에있는 동일 행에있는 함께 새로운 라인에서 시작 .


코드를 최적화하고 하드 코딩을 제거 할 수 있습니다.


class CodeChanger 
{ 
    private Dictionary webMethodDictionary; 

    public CodeChanger() 
    { 
     webMethodDictionary = new Dictionary(); 
    } 

    public void ChangeCode(string oldFilePath, string newFilePath) 
    { 
     StringBuilder newFileContents = new StringBuilder(); 
     StringBuilder webserviceMethodContents = new StringBuilder(); 
     Encoding iso88591Encoding = Encoding.GetEncoding("ISO-8859-1"); 
     string readLine; 
     using (StreamReader streamReader = new StreamReader(oldFilePath, iso88591Encoding)) 
     { 
      while ((readLine = streamReader.ReadLine()) != null) 
      { 
       if (!string.IsNullOrEmpty(readLine.Trim())) 
       { 
        if (string.Equals(readLine, "[Webmethod]")) 
        { 
         // Read the next line - method signature 
         if ((readLine = streamReader.ReadLine()) != null) 
         { 
          readLine = readLine.Trim(); 
          if (readLine.StartsWith("public void")) 
          { 
           string methodName = readLine.Split(new char[] { ' ' })[2]; 
           Webmethod webMethod = new Webmethod(methodName); 
           webMethodDictionary.Add(methodName, webMethod); 

           // Process parameters 
           ProcessParameters(readLine, methodName, webMethod); 

           // Process Body 
           if ((readLine = streamReader.ReadLine()) != null) 
           { 
            StringBuilder methodBody = new StringBuilder(); 
            readLine = readLine.Trim(); 
            if (string.Equals(readLine, "{")) 
            { 
             int bracketCounter = 1; 
             while ((readLine = streamReader.ReadLine()) != null) 
             { 
              if (string.Equals(readLine.Trim(), "}")) 
              { 
               bracketCounter--; 
              } 
              else if (string.Equals(readLine.Trim(), "{")) 
              { 
               bracketCounter++; 
              } 

              if (bracketCounter != 0) 
              { 
               methodBody.AppendLine(readLine); 
              } 
              else 
              { 
               break; 
              } 
             } 

             webMethod.AddBody(methodBody.ToString()); 
            } 
           } 

           newFileContents.AppendLine(GenerateNewWebmethods(webMethod)); 
          } 
         } 
        } 
        else 
        { 
         newFileContents.AppendLine(readLine); 
        } 
       } 
       else 
       { 
        newFileContents.AppendLine(); 
       } 
      } 
     } 

     using (StreamWriter writer = new StreamWriter(newFilePath, false, iso88591Encoding)) 
     { 
      writer.Write(newFileContents.ToString()); 
     } 
    } 

    private static void ProcessParameters(string readLine, string methodName, Webmethod webMethod) 
    { 
     int positionOpenBrackets = string.Concat("public void ", methodName, " ").Length; 
     string parametersString = readLine.Substring(positionOpenBrackets).Trim(); 
     parametersString = parametersString.TrimStart(new char[] { '(' }); 
     parametersString = parametersString.TrimEnd(new char[] { ')' }); 

     string[] parameters = parametersString.Split(new char[] { ',' }); 
     foreach (string parameter in parameters) 
     { 
      string[] splitParameters = parameter.Trim().Split(new char[] { ' ' }); 
      webMethod.AddParameter(splitParameters[0].Trim(), splitParameters[1].Trim()); 
     } 
    } 

    private string GenerateNewWebmethods(Webmethod webmethod) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 

     stringBuilder.AppendLine(GenerateInternal(webmethod)); 
     stringBuilder.AppendLine(GenerateBegin(webmethod)); 
     stringBuilder.Append(GenerateEnd(webmethod)); 

     return stringBuilder.ToString(); 
    } 

    private string GenerateInternal(Webmethod webmethod) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 

     string parametersString = GenerateParameterString(webmethod); 

     stringBuilder.AppendLine(string.Format("public void Internal{0} ({1}, AsyncCallback callback)", 
      webmethod.Name, parametersString.Trim().TrimEnd(','))); 
     stringBuilder.AppendLine("{"); 
     stringBuilder.Append(webmethod.Body); 
     stringBuilder.AppendLine("}"); 

     return stringBuilder.ToString(); 
    } 

    private string GenerateEnd(Webmethod webmethod) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 

     stringBuilder.AppendLine(string.Format("public void End{0} (IAsyncResult asyncResult)", webmethod.Name)); 
     stringBuilder.AppendLine("{"); 
     stringBuilder.AppendLine("//Set return values"); 
     stringBuilder.Append("}"); 

     return stringBuilder.ToString(); 
    } 

    private string GenerateBegin(Webmethod webmethod) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 
     stringBuilder.AppendLine("[Webmethod]"); 
     string parametersString = GenerateParameterString(webmethod); 

     stringBuilder.AppendLine(string.Format("public void Begin{0} ({1}, AsyncCallback callback, object asyncState)", 
      webmethod.Name, parametersString.Trim().TrimEnd(','))); 
     stringBuilder.AppendLine("{"); 
     stringBuilder.AppendLine("//Queue InternalMyWebservice in a threadpool"); 
     stringBuilder.AppendLine("}"); 

     return stringBuilder.ToString(); 
    } 

    private static string GenerateParameterString(Webmethod webmethod) 
    { 
     StringBuilder parametersStringBuilder = new StringBuilder(); 
     foreach (MethodParameter parameter in webmethod.Parameters) 
     { 
      string parameterString = string.Concat(parameter.Type, " ", parameter.Name, ", "); 
      parametersStringBuilder.Append(parameterString); 
     } 

     return parametersStringBuilder.ToString(); 
    } 
} 

class Webmethod 
{ 
    public IList Parameters { get; private set; } 
    public string Name { get; private set; } 
    public string Body { get; private set; } 


    public Webmethod(string name) 
    { 
     Parameters = new List(); 
     Name = name; 
    } 

    public void AddParameter(string paramType, string paramName) 
    { 
     MethodParameter methodParameter = new MethodParameter 
              { 
               Type = paramType, 
               Name = paramName 
              }; 

     Parameters.Add(methodParameter); 
    } 

    public void AddBody(string body) 
    { 
     Body = body; 
    } 
} 

class MethodParameter 
{ 
    public string Type { get; set; } 
    public string Name { get; set; } 
} 

사용은


CodeChanger cc = new CodeChanger(); 
cc.ChangeCode(@"D:\1.cs", @"D:\3.cs"); 

이것은 또한 System.CodeDom 방식에 맞게 수정할 수 있습니다.

+0

이 파서의 종류는 매우 약합니다. 속성의 '[Webmethod()]'라고 쓰면 어떨까요? 아니면'[SomeAttribute, WebMethod]'? 속성과 함수 사이에 XML 주석이 있다면 어떨까요? 반환 유형에 공백이 포함 된 경우 (예 : '')? 함수의 주석에 '{'또는 '}'문자가 포함 된 경우 어떻게됩니까? 같은 줄에 하나 이상의 괄호가있는 경우 (예 :'var data = new [] {1,2,3}')? 이 방법으로는 C#을 파싱 할 수 없습니다. 깨끗한 파서를 작성하거나 파서 - 생성기를 사용하여 수행하십시오. – Niki

+0

@nikie, 네 말이 맞아. 코드는 가정과 약간의 차이가 있습니다. 나는 코드 생성기 (System.CodeDom) 클래스에 대해 몰랐다. 클래스를 생성하는 훨씬 더 좋은 방법을 제공한다. 이 코드는 출발점으로 사용할 수 있습니다. –

+0

코드에 대해 감사드립니다. 하지만 표준 파서 중 하나를 가지고 갈 것입니다. 더 안전해야합니다. – Carra

관련 문제