views:

343

answers:

3

I am generally not very fond of refactoring tools. No need to get into details. Still, I occasionally try out new versions. Here is what I was trying to do while evaluating resharper 4.5 :

I needed to replace all usages of a method with a wrapper method (to be created) but I could not. I usually suck at noticing an obvious feature, is this the case? If resharper does not have this feature, do you know such tools?

Edit 2: Sample has been improved to include instance method calls. Edit: Here is a simple case to play.

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

What I need is something like: (Edit2: added an instance sample)

    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 calls are refactored. Next lets refactor 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));
    }
}

Selecting from usages and maybe omitting common parameters (such as 250 above) or common instance parameters for non-extension statics shall make this feature more valuable. Hopefully, this clears up the question.

+4  A: 

It's not included in any .NET refactoring IIRC, the tool which has such a refactoring is Eclipse, but not for .NET/C#

Frans Bouma
This at least confirms that I am not alone. Thanks.
orca
Can you point me to an example of what Eclipse does to perform this? I'd be interested to see how it handles the requirement to change all references from one object type to another.
Martin Harris
It's called 'introduce indirection' in Eclipse. In Eclipse, it's mainly the case that each refactoring is implemented in a separate piece of code. (which doesn't have to be the case, see Jeroen v/d Bos' master thesis on this:homepages.cwi.nl/~paulk/thesesMasterSoftwareEngineering/2008/JeroenVanDenBos.pdf )
Frans Bouma
I was just thinking about its naming. "Introduce Indirection" definitely sums it up. Surprised that this feature is out there since 2006 and there is no equivalent. Thanks again for the info.
orca
Wondering who is voting down your answer. Answers do not always have to be direct and complete. Hope someone does not flag your comment too.
orca
@Frans: Thanks for the link. It does look useful, but unfortunately I have to agree and say that it is unlike anything I've seen in any Visual Studio refactoring option.
Martin Harris
+3  A: 

Assuming the wrapper method will be in the same class you can rename the current method to the name of the new wrapper method (ctrl+R+R in Resharper). This will rename all calls to this method in the solution as well. Then rename the original method back by hand (don't use Resharper or all the calls will get renamed back too) and add the wrapper method.


Based on your edit I think you will be out of luck to get the functionality that you want from any Visual Studio add-in that I've seen (beyond the simple find and replace which will get you some of the way there I guess).

Depending on how much time and effort you are willing to devote to this I'd imagine it's possible to use the DXCore framework and write a plugin that will do this kind of refactoring.

Martin Harris
This is how I have usually done it. Works well! (If in same class of course)
Svish
In my case, not only the wrapper will be in a different class, it will be in a different assembly. As you mentioned, this is refactoring a method and renaming its usages, two separate things. I need to refactor its usages.Still, thanks, I should have seen it coming.
orca
+5  A: 

ReSharper doesn't have this as a single refactoring. I might do it as follows:

  1. Select the contents of the method to be wrapped, and use Extract Method to create a new private method from the contents.
  2. The original method is now a trivial wrapper around "itself". Rename it if you like, or manipulate it as you like (make it static, move to a different class, surround with try/catch, whatever).


EDIT: Based on your edit, it seems you have an additional problem. Not only is Console.Beep not in the same class, it's not even in your class.

But if you don't mind a little search and replace, then you can put it into your own class, then proceed with the refactoring:

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

Then do a "Replace in Files" to replace Console.Beep with Temporary.Console.Beep, and proceed as above.

John Saunders
This should only work for static functions and requires a cautious process. Thanks though.
orca
If the question was how to do it instead of is there such feature, my friend suggested that extension methods should work in a similar way you described. And of course both methods does not always work, you need a bit of luck, which I lack.
orca
@orca: I don't see that this would be limited to static methods. Moving the extracted method to a different class would require it to become static, but the wrapper method would not need to change. Also, "cautious process" is not really necessary. Any failure will likely have broken the build, so build before you check in. If using solution-wide analysis, ReSharper will tell you if you've broken anything. This does assume all affected code is in the same solution, at least temporarily.
John Saunders
Suppose we have more than one console and calls like console1.Beep(...) and console2.Beep(...). Since Beep is not static, it is harder to find its usages. Not to mention overloads and such.Even static case has problems. Now, suppose there is also a Console.Beep(int, int, int), this could be cumbersome. Or even a dangerous case where Virtual.Console.Beep(int,int) exists which will not even broke the build.Or similar reasons why "find usages/references" feature exist in the first place.
orca
Doesn't matter. It still works, and the "Find Usages" command in ReSharper will find all usages anyway. In the instance case, you don't even need the find and replace. That was _only_ needed because Console.Beep == System.Console.Beep, which you can't change.
John Saunders
It matters. "Find usages/references" does not help with replacement. Please tell me what I can change in the instance case? Instance does not mean that I have control over its code. Please see the accepted answer and its comments. In the mean time, I will try to improve the sample code.
orca