Tuesday, January 18, 2011

Not as easy as it looks, Part Two

Holy goodness, did you guys ever find a lot of additional ways in which an "eliminate variable" refactoring can go wrong. Just a few of your observations: (again, in every case, "x" is eliminated.)
Any situation in which x is being treated as a variable rather than a value will pose a problem. Some obvious ones, like if x is on the left side of an assignment, or the target of a ++, or used as a "ref" or "out" parameter are straightforward. But there are some situations in which it is a bit surprising where x is being used as a variable. Suppose S is a mutable struct with field F:

S x = new S();
x.F = 123;

Here we cannot simply say (new S()).F = 123 because new S() is not a variable. The mutable field requires a storage location to mutate but we don't have a storage location here.
A number of people pointed out that the C# rules about how simple names may be used potentially pose problems:

int y = 2;
void M()
{
 Action x = () => { Console.WriteLine(y); };  // refers to this.y
 {
   string y = "hello";
   x();
 }

The two inconsistent uses of the simple name y are legal here because the declaration spaces in which both are first used do not overlap. But the refactoring causes them to overlap; this needs to be refactored to

void M()
{
 {
   string y = "hello";
   ((Action)(()=>{Console.WriteLine(this.y);})))();
 }

It gets even worse if the inconsistent simple names cannot be fully qualified:

Action x = () => {int z = 123;};
{
 string z = "hello";
 x();
}

Now one of the local zeds must be renamed if the refactoring is to succeed.

Read more: Fabulous Adventures In Coding