Программно меняющиеся файлы кода - PullRequest
1 голос
/ 05 января 2011

Я меняю наши веб-сервисы на асинхронную модель.И для этого мне нужно поменять более сотни методов.

Выполнение вручную - это один (непривлекательный) вариант.Нет ли способа программно проанализировать и изменить несколько файлов функций / кодов?

Пример:

[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 & end.

Ответы [ 6 ]

5 голосов
/ 05 января 2011

Вы должны быть в состоянии использовать метод CSharpCodeProvider.Parse для генерации экземпляра CodeCompileUnit, который является объектно-ориентированным представлением вашего кода.С этим вы можете перейти к своим методам, изменить аргументы, добавить новые методы и прочее, и когда вы закончите, вы можете сохранить код обратно в текстовый файл.Вы генерируете код, вызывая CodeDomProvider.GenerateCodeFromCompileUnit, передавая измененный CodeCompileUnit

Предоставляет доступ к экземплярам генератора кода C # и компилятора кода.

2 голосов
/ 05 января 2011

Вы можете использовать генератор парсеров, например ANTLR .Написание грамматики ANTLR для подмножества C #, которое только анализирует объявления классов и методов и просто игнорирует код метода, не должно быть трудным.Или вы можете использовать одну из грамматик C # с сайта ANTLR.

ANTLR имеет функцию под названием «переписать грамматики» (например, посмотрите этот вопрос ), которая очень близка к тому, что вы хотитеделать.

Но лично я бы не поместил сгенерированные методы в один файл с реальными методами.Если вы обнаружите ошибку в сгенерированном коде и захотите сгенерировать ее заново, анализатор становится более сложным, потому что он должен распознавать методы, сгенерированные им ранее.И соблазн редактировать сгенерированные методы очень велик.Кроме того, кажется, что это нарушает принцип единственной ответственности, но это может быть вопросом вкуса.

Я бы поместил сгенерированные методы в отдельный файл (производный класс или частичное объявление класса).Преимущество этого в том, что вам не нужен синтаксический анализатор: если не сгенерированный файл класса компилируется (возможно, с абстрактными или частичными объявлениями методов в нем), вы можете скомпилировать его и просто использовать хорошо известное отражениемеханизмы, чтобы получить всю информацию, которую вы хотите.Все, что вам нужно, это шаблонная структура, такая как StringTemplate или T4 для генерации кода.

2 голосов
/ 05 января 2011

Попробуйте найти решение от Resharper, а если нет, замените текст на Regex.

1 голос
/ 05 января 2011

Приведенный ниже код выполняет замену, но сильно зависит от форматирования входного исходного файла.

Допущения

  • 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 .

1 голос
/ 05 января 2011

Почему бы просто не написать один класс-обертку, который будет получать объекты существующих классов (или делегатов) при создании и вызывать нужные методы асинхронно?Методы существующих классов все еще могут быть синхронными.

0 голосов
/ 05 января 2011

Я на самом деле не слишком знаком с .NET Framework, но это определенно можно сделать с помощью регулярных выражений.

...