Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Structs "this = ...."

Tags:

c#

struct

I've just been browsing a file in reflector and seen this in a struct constructor:

this = new Binder.SyntaxNodeOrToken();

I've not seen that terminology before. Can someone explain what that this assignment means in C#. It's difficult to Google.

like image 682
Stephen Ellis Avatar asked Mar 10 '12 18:03

Stephen Ellis


2 Answers

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 a ref parameter of the struct type.

    • [text about iterators]

like image 112
Jon Skeet Avatar answered Oct 19 '22 20:10

Jon Skeet


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.

like image 40
supercat Avatar answered Oct 19 '22 21:10

supercat