Замена всех использований метода (введение косвенного обращения)

Я вообще не очень люблю инструменты рефакторинга. Не нужно вдаваться в подробности. Тем не менее, я иногда пробую новые версии. Вот что я пытался сделать, оценивая resharper 4.5:

Мне нужно было заменить все использования метода методом-оболочкой (который должен быть создан), но я не мог. Я обычно плохо замечаю очевидную особенность, так ли это? Если у resharper нет этой функции, знаете ли вы такие инструменты?

Редактировать 2: Образец был улучшен, чтобы включить вызовы методов экземпляра. Редактировать: вот простой случай, чтобы играть.

    static void Main(string[] args)
    {
        while(true)
        {
            if (Console.ReadKey().Key == ConsoleKey.Escape)
            {
                Thread.Sleep(10);
                if (Quiting()) break;
            }
            Console.Beep(250, 50);
        }
    }

    static bool Quiting()
    {
        if (Console.In.Peek() > 0)
        {
            Console.Beep(250, 150);
            return false;
        }
        return true;
    }

Мне нужно что-то вроде: (Edit2: добавлен образец экземпляра)

    private static StringBuilder _builder = new StringBuilder();

    static void Main(string[] args)
    {
        while(true)
        {
            var key = Console.ReadKey();
            if (key.Key == ConsoleKey.Escape)
            {
                Thread.Sleep(10);
                if (Quiting()) break;
            }
            _builder.Append(" (").Append(key.KeyChar).Append(") ");
            Beep(250, 50);
        }
    }

    static bool Quiting()
    {
        if (Console.In.Peek() > 0)
        {
            Beep(250, 150);
            _builder.Append('@');
            return false;
        }
        return true;
    }

    static void Beep(int frequency, int duration)
    {
        // finally cursor ends up here
        Console.Beep(250, 50);
    }

Вызовы Console.Beep переработаны. Далее давайте рефакторим StringBuilder.Append(char) :

class Program
{
    private static StringBuilder _builder = new StringBuilder();

    static void Main(string[] args)
    {
        while(true)
        {
            var key = Console.ReadKey();
            if (key.Key == ConsoleKey.Escape)
            {
                Thread.Sleep(10);
                if (Quiting()) break;
            }
            _builder.Append(" (").AppendUpper(key.KeyChar).Append(") ");
            Beep(250, 50);
        }
    }

    static bool Quiting()
    {
        if (Console.In.Peek() > 0)
        {
            Beep(250, 150);
            _builder.AppendUpper('n');
            return false;
        }
        return true;
    }

    static void Beep(int frequency, int duration)
    {
        // finally cursor ends up here
        Console.Beep(250, 50);
    }
}

static class StringBuilderExtensions
{
    public static StringBuilder AppendUpper(this StringBuilder builder, char c)
    {
        return builder.Append(char.ToUpper(c));
    }
}

Выбор из вариантов использования и, возможно, исключение общих параметров (таких как 250 выше) или общих параметров экземпляра для статики без расширения сделают эту функцию более полезной. Надеюсь, это проясняет вопрос.


person orcun    schedule 13.05.2009    source источник


Ответы (4)


Он не включен ни в один рефакторинг .NET IIRC, инструмент, который имеет такой рефакторинг, - это Eclipse, но не для .NET/C#.

person Frans Bouma    schedule 13.05.2009
comment
Это как минимум подтверждает, что я не один. Спасибо. - person orcun; 13.05.2009
comment
Можете ли вы указать мне пример того, что Eclipse делает для этого? Мне было бы интересно посмотреть, как он обрабатывает требование изменить все ссылки с одного типа объекта на другой. - person Martin Harris; 13.05.2009
comment
В Eclipse это называется «ввести косвенность». В Eclipse в основном каждый рефакторинг реализуется в отдельном фрагменте кода. (что не обязательно, см. основную диссертацию Jeroen v/d Bos по этому вопросу: homepages.cwi.nl/~paulk/thesesMasterSoftwareEngineering/2008/JeroenVanDenBos.pdf) - person Frans Bouma; 13.05.2009
comment
Я как раз думал о его названии. Introduce Indirection определенно подводит итог. Удивлен, что эта функция существует с 2006 года и не имеет аналогов. Еще раз спасибо за информацию. - person orcun; 13.05.2009
comment
Интересно, кто голосует против вашего ответа. Ответы не всегда должны быть прямыми и полными. Надеюсь, кто-то не пометит и ваш комментарий. - person orcun; 13.05.2009
comment
@Франц: Спасибо за ссылку. Это выглядит полезным, но, к сожалению, я должен согласиться и сказать, что это не похоже ни на что, что я видел в любом варианте рефакторинга Visual Studio. - person Martin Harris; 13.05.2009

В ReSharper нет единого рефакторинга. Я мог бы сделать это следующим образом:

  1. Выберите содержимое метода, который нужно обернуть, и используйте метод извлечения, чтобы создать из содержимого новый закрытый метод.
  2. Первоначальный метод теперь представляет собой тривиальную обертку вокруг «самого себя». Переименуйте его, если хотите, или манипулируйте им по своему усмотрению (сделайте его статичным, переместите в другой класс, окружите с помощью try/catch и т. д.).

РЕДАКТИРОВАНИЕ: Судя по вашему редактированию, у вас возникла дополнительная проблема. Console.Beep не только не в том же классе, он даже не в вашем классе.

Но если вы не возражаете против небольшого поиска и замены, вы можете поместить его в свой собственный класс, а затем продолжить рефакторинг:

namespace Temporary {
    public class Console {
        public static void Beep(int x, int y) {System.Console.Beep(x,y);}
    }
}

Затем выполните «Заменить в файлах», чтобы заменить Console.Beep на Temporary.Console.Beep, и действуйте, как указано выше.

person John Saunders    schedule 13.05.2009
comment
Это должно работать только для статических функций и требует осторожного процесса. Спасибо хоть. - person orcun; 13.05.2009
comment
Если вопрос заключался в том, как это сделать, а не в том, есть ли такая функция, мой друг предложил, чтобы методы расширения работали так же, как вы описали. И конечно оба способа не всегда работают, нужно немного удачи, которой мне не хватает. - person orcun; 13.05.2009
comment
@orca: я не вижу, чтобы это ограничивалось статическими методами. Перемещение извлеченного метода в другой класс потребовало бы, чтобы он стал статическим, но метод-оболочку не нужно было бы изменять. Кроме того, осторожный процесс на самом деле не нужен. Любой сбой, скорее всего, приведет к поломке сборки, поэтому выполняйте сборку до регистрации. Если вы используете анализ всего решения, ReSharper сообщит вам, если вы что-то сломали. Это предполагает, что весь затронутый код находится в одном и том же решении, по крайней мере временно. - person John Saunders; 13.05.2009
comment
Предположим, что у нас есть более одной консоли и такие вызовы, как console1.Beep(...) и console2.Beep(...). Поскольку Beep не является статичным, найти его использование сложнее. Не говоря уже о перегрузках и прочем. Даже статический случай имеет проблемы. Теперь предположим, что имеется также Console.Beep(int, int, int), это может быть громоздко. Или даже опасный случай, когда существует Virtual.Console.Beep(int,int), который даже не нарушит сборку. Или аналогичные причины, по которым функция поиска использований/ссылок существует в первую очередь. - person orcun; 13.05.2009
comment
Не имеет значения. Он по-прежнему работает, и команда Find Usages в ReSharper все равно найдет все варианты использования. В случае экземпляра вам даже не нужен поиск и замена. Это было только необходимо, потому что Console.Beep == System.Console.Beep, которое вы не можете изменить. - person John Saunders; 13.05.2009
comment
Это имеет значение. Поиск использований/ссылок не помогает с заменой. Подскажите пожалуйста, что я могу изменить в корпусе экземпляра? Экземпляр не означает, что я контролирую его код. Пожалуйста, ознакомьтесь с принятым ответом и его комментариями. Тем временем я постараюсь улучшить пример кода. - person orcun; 14.05.2009

Предполагая, что метод-оболочка будет в том же классе, вы можете переименовать текущий метод в имя нового метода-оболочки (ctrl+R+R в Resharper). Это также переименует все вызовы этого метода в решении. Затем вручную переименуйте исходный метод обратно (не используйте Resharper, иначе все вызовы также будут переименованы) и добавьте метод-оболочку.


Основываясь на вашем редактировании, я думаю, вам не повезет получить желаемую функциональность из любой надстройки Visual Studio, которую я видел (помимо простого поиска и замены, которые, я думаю, помогут вам) .

В зависимости от того, сколько времени и усилий вы готовы посвятить этому, я полагаю, что можно использовать DXCore и напишите плагин, который будет выполнять такой рефакторинг.

person Martin Harris    schedule 13.05.2009
comment
Вот как я обычно это делал. Работает хорошо! (если в одном классе конечно) - person Svish; 13.05.2009
comment
В моем случае не только обертка будет в другом классе, она будет в другой сборке. Как вы упомянули, это рефакторинг метода и переименование его использования, две разные вещи. Мне нужно реорганизовать его использование. Тем не менее, спасибо, я должен был предвидеть это. - person orcun; 13.05.2009

В Resharper есть функция Поиск и замена шаблоном. Он может искать и заменять шаблоны и выражения.

Это приведет к реорганизации всех вызовов Console.Beep() в ваш собственный метод. Он заменяет использование только в том случае, если 250 является первым параметром:

Заменить вызов Console.Beep()

Однако это заменит использование Console.Beep() в вашем собственном методе Beep. Вам придется вручную заменить это использование.

person Craig Curtis    schedule 21.01.2014