struct PointS
{
public int X, Y;
public override string ToString() => $"({X},{Y})";
}
class PointC
{
public int X, Y;
public override string ToString() => $"({X},{Y})";
}
class Program
{
static void MoveFirstWrong(PointS[] arr)
{
// BUG: copies the struct, mutates the copy, never writes it back
var p = arr[0]; // value copy
p.X += 10;
p.Y += 10;
// arr[0] not updated -> no effect on array storage
}
static void MoveFirstRight(PointS[] arr)
{
// FIX: mutate a local copy and assign it back
var p = arr[0]; // value copy
p.X += 10;
p.Y += 10;
arr[0] = p; // write back
}
static void MoveFirstRef(PointC[] arr)
{
// Works directly because arr[0] is a reference to the same object
arr[0].X += 10;
arr[0].Y += 10;
}
// Optional: foreach pitfall with structs in a List<T>
static void IncrementAllWrong(List<PointS> list)
{
foreach (var item in list)
{
var tmp = item; // copy
tmp.X++; tmp.Y++; // mutate the copy only
// not assigned back -> list unchanged
}
}
static void IncrementAllRight(List<PointS> list)
{
for (int i = 0; i < list.Count; i++)
{
var tmp = list[i];
tmp.X++; tmp.Y++;
list[i] = tmp; // write back
}
}
static void Main()
{
// --- Struct array demo ---
var s = new[] { new PointS { X = 1, Y = 1 } };
Console.WriteLine($"Struct before: {s[0]}"); // (1,1)
MoveFirstWrong(s);
Console.WriteLine($"After Wrong : {s[0]}"); // still (1,1) <-- bug observed
MoveFirstRight(s);
Console.WriteLine($"After Right : {s[0]}"); // (11,11) <-- fixed
// --- Class array demo ---
var c = new[] { new PointC { X = 1, Y = 1 } };
Console.WriteLine($"nClass before : {c[0]}"); // (1,1)
MoveFirstRef(c);
Console.WriteLine($"After Ref : {c[0]}"); // (11,11) <-- same object mutated
// --- Optional List<T> pitfall ---
var list = new List<PointS> { new PointS { X = 5, Y = 5 } };
Console.WriteLine($"nList before : {list[0]}"); // (5,5)
IncrementAllWrong(list);
Console.WriteLine($"After Wrong : {list[0]}"); // (5,5) <-- unchanged
IncrementAllRight(list);
Console.WriteLine($"After Right : {list[0]}"); // (6,6) <-- changed
}
}