C# Structs "this = ...."
It replaces the value, basically. It effectively copies all the fields from the right side to the left... except it works even if the fields are readonly. And yes, it does look distinctly weird, and it's somewhat scary.
Example:
using System;
class Test
{
static void Main()
{
Point point = new Point(10, 20);
point.ReplaceWith(new Point(2, 3));
Console.WriteLine(point); // (2, 3)
}
}
struct Point
{
private readonly int x;
private readonly int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public void ReplaceWith(Point other)
{
this = other;
}
public override string ToString()
{
return string.Format("({0}, {1})", x, y);
}
}
For more information, read section 7.6.7 of the C# 4 spec, which includes:
[text about its use in a struct constructor]
When
this
is used in a primary-expression within an instance method or instance accessor of a struct, it is classified as a variable. The type of the variable is the instance type of the struct within which the usage occurs.
If the method or accessor is not an iterator, the
this
variable represents the struct for which the method or accessor was invoked, and behaves exactly the same as aref
parameter of the struct type.[text about iterators]
If s1
and s2
are structs of type Foo
, with fields f1
, f2
, and f3
, the statement s1 = s2
is semantically equivalent to
s1.f1 = s2.f1;
s1.f2 = s2.f2;
s1.f3 = s2.f3;
except that one should make no assumption about the order of the assignment operations (or even the relative ordering of reads and writes; the generated code might, for example, read all three fields into registers, and then write all three fields). All fields will be copied, independent of whether they are public or private, mutable or so-called immutable. No property getters or setters will be called; neither the source nor destination struct will receive any notice that the fields of the structs are being duplicated or overwritten.
A statement this = new Foo(whatever);
is in C#(*) equivalent to
Foo temp;
call Foo's constructor (out temp, whatever);
this.f1 = temp.f1;
this.f2 = temp.f2;
this.f3 = temp.f3;
(*) Struct constructor semantics in vb.net are different
As above, the field assignments are done without regard for whether the fields are public or private, and without regard for whether they are supposedly immutable.
One reason I believe (contrary to the view of some other people) that structs should often expose mutable fields is that syntax like:
// Assume myKVP is a field of type KeyValuePair<Wizzle, int>
rr = new KeyValuePair<Wizzle, int>(myKVP.Key, myKVP.Value + 1);
makes it appear as though myKVP
will refer to a different instance after the assignment from what it held before, when what is actually happening is:
// Assumes backing fields are named _Key and _Value
// Note that C# won't allow one to write private fields directly, but the
// act of copying one struct instance to another copies all the fields,
// public and private, from the source instance to the destination.
KeyValuePair<Wizzle, int> temp;
temp._Key = myKVP.Key; // Constructor has access to backing fields
temp._Value = myKVP.Value+1;
myKVP._Key = temp._Key; // Struct assignment copies all fields, public and private
myKVP.Value = temp.Value;
In other words, the statement does not make myKVP
report to a different instance; instead, it creates a new instance, mutates the old instance by overwriting its fields with those of the new instance, and then discards the new instance. If some code was evaluating myKVP.ToString()
while the above assignment took place, the mutation would affect the instance of myMVP
that was being printed.
Structs can have useful semantics, but so-called "immutable" structs aren't. Non-trivial structs (those for which it's possible to create a value different from the default) are mutable if and only if they are kept in mutable storage locations, regardless of any semantics imposed by the type. Self-mutating structs, i.e. structs which mutate this
in any methods except in constructors and property setters, can have some unexpected behaviors because compilers have no way of forbidding the invocation of other methods which will mutate this
on immutable struct instances. Public exposure of struct fields, however, does not pose any such danger. Since all fields of a mutable instance of a non-trivial struct are inherently mutable, regardless of any attempts the struct might make to allow mutation, and all fields of an immutable instance of a struct are immutable even if they're exposed, a struct which endeavors to make its fields "immutable" is really telling a lie. A lie which may at times be useful (e.g. if field contents are supposed to obey certain invariants) but which shouldn't be told without some real cause.