Refactoring C# with PowerShell

imageVisual Studio 2005 and 2008 have built-in support for refactoring code, including renaming namespaces, classes, variables, and more. Add-ins like Resharper also have support for refactoring by renaming. These tools work great, and have good integration into the Visual Studio IDE, for example being able to preview each change and exclude false positive matches. I know many people swear by Resharper in particular (for more features than the refactoring alone).

Unfortunately all of these tools are restricted in one normally insignificant way: they only operate on the currently loaded project(s). For most software this really is insignificant. The entire application can be loaded into the IDE all at once. Sometimes, though, it is a problem–usually when there’s another problem at play: code "bloat" (via Jeff).

If you’ve ever changed a namespace and wanted to simply push the change into every code file in an entire directory branch, PowerShell comes to our rescue with it’s search-and-replace capabilities. I wrote the following "Move-Namespace" function to do just that.

Invoke the function like so: "Move-Namespace *.cs "[current namespace]" "[current class name]" "[new namespace]".

After it completes, any explicit reference will be changed, and any implicit references (using traditional "using [namespace];" statements) will have a new "using [new namespace];" line inserted after the last existing using statement.

Let’s hope you don’t have a code base that requires this function. :) But if you do, I hope it helps!

Note that this function relies on my "Replace-String" function, found here.

function Move-Namespace(
    $includes = $(throw 'Specify a file filter to use'),
    $oldNamespace = $(throw 'The namespace to replace - i.e. MyCompany.Product'),
    $className = $(throw 'The class name to search for - i.e. MyClass'),
    $newNamespace = $(throw 'The new namespace to add - i.e. MyCompany.Product.Feature'))
{
    # Look for cases of $className
    # or $oldNamespace.$className
    # and replace as necessary

    # First check for assumed "using" statements, and add the using statement
    $results = @{}
    $files = get-childitem -r -i $includes | select-string "(?<!$oldNamespace\.)\b$className\b" -list |% { $_.Path }
    select-string "^using.*;" -path $files | group-object Path | select-object Name, @{Expression={ ($_.Group | measure-object -property LineNumber -max).Maximum }; Name="LastUsingStatement" } |% { $results[$_.Name] = $_.LastUsingStatement }
    foreach ($key in $results.keys)
    {
      $processFile = $true
      $lastUsingIndex = $results[$key]
      $fileContents = get-content $key
      # look for existing using statement and cancel processing this file
      for ($i = 0; $i -lt $lastUsingIndex; $i++)
      {
        if ($fileContents[$i] -match "using $newNamespace;")
        {
          # this file already has the correct using statement, stop processing
          Write-Warning "File $key already has the required using statement, ignoring"
          $processFile = $false
          break;
        }
      }

      if ($processFile)
      {
        $newContents = $fileContents[0..($lastUsingIndex-1)] + "using $newNamespace;" + $fileContents[$lastUsingIndex..$fileContents.Length]
        set-content -path $key -value $newContents
        Write-Host "Successfully updated file $key" -foregroundcolor Green
      }
    }

    # Next check for explicit references
    replace-string "\b$oldNamespace\.$className\b" "$newNamespace.$className" $includes
}

This entry was posted on Friday, January 4th, 2008 at 2:06 pm and is filed under .net, powershell, programming. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

4 Responses to “Refactoring C# with PowerShell”

  1. Keith Hill Says:

    Heheh, I done similar source code mods like this. One addition that you might consider is using Write-Progress to keep you updated on the progress of the script since this type of operation could take a while depending on the size of your codebase.

  2. aaron Says:

    You know, I could’ve sworn I had that in there – I think I might’ve updated the script for our use after I wrote the blog post. I’ll update it if that’s the case (Monday, of course – now is the time for Guitar Hero 3 :) )

  3. » Daily Bits - January 6, 2008 Alvin Ashcraft’s Daily Geek Bits: Daily links plus random ramblings about development, gadgets and raising rugrats. Says:

    [...] 6, 2008 Posted in January 6th, 2008 by Alvin Ashcraft in Daily Links, Development, microsoft Refactoring C# with PowerShell (via Jason [...]

  4. Reflective Perspective - Chris Alcock » The Morning Brew #5 Says:

    [...] Refactoring C# with PowerShell – Aaron Lerch details an interesting use of Powershell – Makes DOS batch files look very antiquated. [...]