Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutability of value types

Tags:

c#

c#-4.0

Consider the TranslateAllCoords static function:

static class CoordinateTransformation
{
  public static void TranslateAllCoords(ref int x, ref int y, ref int z, 
                                        int amount)
  {
    x+=amount;
    y+=amount;
    z+=amount;
  }
}

Then, later in code, you have:

int x=0, y=0, z=0;
...
CoordinateTransformation.TranslateAllCoords(ref x, ref y, ref z, 5);
...

But, by calling TranslateAllCoords you are in effect modifying value types (i.e., the integer coords) and generally values types should be immutable. Are some rules broken here or is this a perfectly valid construct that gets around the "value types should be immutable" construct by modifying only built in value types?

like image 557
Michael Goldshteyn Avatar asked Nov 12 '10 16:11

Michael Goldshteyn


2 Answers

The values are immutable. The variables that contain the value types are mutable. Variables vary, that's why they're called "variables".

The design guidance that value types should be immutable is essentially saying that you should not try to change only part of a variable. When you say

struct Point { public int X; public int Y; public int Z; }
...
Point p = new Point();
p.X = 123;

then what you are saying is "mutate only part of the variable p". That is confusing. The variable p should logically represent a point, and a point is a value. If you want to vary p, then logically vary the whole thing by assigning a new point to it. Don't mutate one point into another.

But even if we made point immutable:

struct Point { public int X { get; private set; } ... etc }

then a variable of that type can still vary!

Point p = new Point(123, 456, 789);
p = new Point(100, 200, 300);

But now it is clear that the entire variable is changing to a new point, rather than us trying to mutate a particular portion of the variable.

With an immutable value type you can then do your translation more sensibly:

static Point Translate(Point p, int offset) 
{
    return new Point(p.X + offset, p.Y + offset, p.Z + offset);
}
...
Point p = new Point(100, 200, 300);
p = Translate(p, 5);

See, again, p mutates, but it mutates all at once, not in little bits at a time.

like image 72
Eric Lippert Avatar answered Sep 28 '22 08:09

Eric Lippert


No rules are broken there. You're simply creating a new integer value and reassigning the variable containing them.

like image 35
bradenb Avatar answered Sep 28 '22 08:09

bradenb