以编程方式更改代码文件

时间:2011-01-05 10:37:09

标签: c# .net code-generation

我正在将我们的web服务更改为异步模型。为此,我必须改变一百多种方法。

手动执行此操作是一种(不具吸引力)选项。有没有办法以编程方式解析&更改多个功能/代码文件?

示例:

[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”,添加参数并创建开始&结束方法。

6 个答案:

答案 0 :(得分:5)

您应该能够使用CSharpCodeProvider.Parse方法生成CodeCompileUnit实例,该实例是代码的面向对象表示。有了它,你可以深入到你的方法,更改参数,添加新的方法和东西,当你完成后,你可以将代码保存回文本文件。您可以通过调用CodeDomProvider.GenerateCodeFromCompileUnit传递已修改的CodeCompileUnit

来生成代码

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

答案 1 :(得分:2)

尝试通过Resharper找到解决方案,如果没有用Regex替换一些文本。

答案 2 :(得分:2)

您可以使用ANTLR之类的解析器生成器。为C#的子集编写ANTLR语法,只解析类和方法声明,而忽略方法代码不应该很难。或者您可以使用ANTLR站点中的一个C#语法。

ANTLR有一个名为“重写语法”的功能(例如,看this question),它非常接近您想要做的事情。

但就个人而言,我不会将生成的方法与实际方法放在一个文件中。如果您在生成的代码中发现错误并想要重新生成它们,则解析器会变得更加复杂,因为它必须识别它先前生成的方法。编辑生成的方法的诱惑非常高。此外,它似乎违反了单一责任原则,但这可能是一个品味问题。

我会将生成的方法放在一个单独的文件中(派生类或部分类声明)。这样做的好处是你不需要解析器:如果非生成的类文件编译(可能使用抽象或部分方法声明),你可以编译它并简单地使用众所周知的反射机制,以获取您想要的所有信息。您只需要一个模板框架,如StringTemplateT4来生成代码。

答案 3 :(得分:1)

为什么不编写一个包装类,它将在构造时获取现有类(或委托)的对象并异步调用所需的方法?现有类的方法仍然可以是同步的。

答案 4 :(得分:1)

以下代码进行替换,但在很大程度上取决于输入源文件的格式。

假设

  • Webmethod从一个前缀为'public void'的新行开始
  • 参数在同一行
  • 打开和关闭括号({})位于不同的行上。


可以优化代码并删除硬编码。


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方法。

答案 5 :(得分:0)

我实际上对.NET框架并不太熟悉,但这肯定可以用正则表达式完成。