2011-01-05 70 views
1

我將我們的webservices更改爲異步模型。爲此,我必須改變上百種方法。以編程方式更改代碼文件

手動操作是一個(不吸引人)選項。有沒有辦法以編程方式解析&更改多個函數/代碼文件?

例子:

[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 
} 

它基本上是我對每個web服務做同樣的事情。將名稱更改爲「InternalX」,添加一個參數並創建開始&結束方法。

回答

2

嘗試通過Resharper找到解決方案,如果不通過正則表達式替換一些文本。

+0

resharper如何幫助我完成此任務? – Carra 2011-01-05 10:46:14

+0

創建一個獲取當前方法的模板,更改它的名稱並用您的新名稱複製它 – 2011-01-05 10:54:50

+0

這確實可以使事情變得更容易,只需最少的工作。我只需填寫每個函數的webmethod名稱。 – Carra 2011-01-05 12:30:49

0

我其實不太熟悉.NET框架,但這肯定可以用正則表達式完成。

+0

如果問題應該變大,只要確保不要拘泥於正則表達式。他們可能會爲此工作,但在一般情況下,他們對於這樣的事情做得並不好。 – delnan 2011-01-05 10:51:14

+0

我不會那樣做:函數聲明的語法很複雜。如果你忘記了一些邊界情況(例如參數之間的註釋,參數的屬性,嵌套泛型類型),你的正則表達式將簡單地忽略一個方法,沒有警告或錯誤 - C#不是一個常規語言(甚至不是這個子集),使用解析if你想解析它。 – Niki 2011-01-05 12:01:30

+0

有問題嗎?使用正則表達式。現在你有兩個問題:) – Carra 2011-01-05 13:09:10

1

爲什麼不寫一個包裝類,它將在構造中獲取現有類(或委託)的對象,並異步調用所需的方法?現有類的方法仍可能是同步的。

5

您應該可以使用CSharpCodeProvider.Parse方法生成CodeCompileUnit實例,該實例是代碼的面向對象表示形式。有了這個,你可以深入到你的方法,改變參數,添加新的方法和東西,當你完成後,你可以將代碼保存迴文本文件。您可以通過調用CodeDomProvider.GenerateCodeFromCompileUnit經過修改CodeCompileUnit

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

+0

+1建議'CSharpCodeProvider.Parse',但使用CodeDOM生成代碼是一個巨大的PITA – Niki 2011-01-05 12:19:44

+0

我不覺得這很困難,這是一個相當小的任務,所以它應該是可管理的 – 2011-01-05 12:31:27

+0

我沒有說這很困難,我說這是一個痛苦的屁股;-)生成一行C#代碼通常需要3-10行CodeDom代碼。如果您使用模板引擎,則必須編寫一行:要生成的行。 – Niki 2011-01-05 12:58:15

2

你可以使用一個解析器生成像ANTLR生成代碼。爲C#的一個子集寫一個ANTLR語法,它只解析類和方法聲明,並簡單地忽略方法代碼不應該很難。或者你可以使用ANTLR站點的C#語法之一。

ANTLR有一個名爲「rewrite grammars」的功能(例如看看this question),它非常接近你想要做的事情。

但個人而言,我不會將生成的方法與實際方法放在一個文件中。如果您在生成的代碼中發現錯誤並想重新生成它們,解析器會變得更加複雜,因爲它必須識別先前生成的方法。編輯生成的方法的誘惑力非常高。此外,它似乎違反了單一責任原則,但這可能是一個品味問題。

我會把生成的方法放在一個單獨的文件(派生類或部分類聲明)中。這有一個好處,你不需要需要一個解析器:如果非生成的類文件編譯(可能包含抽象或部分方法聲明),你可以編譯它,並簡單地使用衆所周知的反射機制來獲取所有你想要的信息。您所需要的只是一個模板框架,如StringTemplateT4來生成代碼。

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註釋會怎麼樣?如果返回類型包含空格(例如'Dictionary ')?如果函數中的註釋包含字符'{'或'}'會怎麼樣?如果同一行中的括號不止一個(例如,'var data = new [] {1,2,3}')?你不能這樣解析C#。編寫一個乾淨的解析器或使用解析器生成器爲你做。 – Niki 2011-01-05 12:51:35

+0

@nikie,你是對的。該代碼將在與假設略有偏差時打破。我不知道代碼生成器(System.CodeDom)類,它提供了一個更好的方法來生成類。這段代碼可以作爲一個起點。 – 2011-01-05 12:56:23

+0

非常感謝代碼。但是我會選擇其中一個標準解析器。應該更安全。 – Carra 2011-01-05 13:09:49