Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meaning of "this" for a struct (C#)

Tags:

c#

According to MSDN (Section 11.3.6 of the C# spec):

Within an instance constructor of a struct, this corresponds to an out parameter of the struct type, and within an instance function member of a struct, this corresponds to a ref parameter of the struct type. In both cases, this is classified as a variable, and it is possible to modify the entire struct for which the function member was invoked by assigning to this or by passing this as a ref or out parameter.

I don't get it. How is this different for a struct than for a class? Code examples are appreciated

like image 886
Sean Avatar asked Sep 09 '09 14:09

Sean


3 Answers

Eric Lippert had a fabulous post on mutating readonly structs a while back that will really help clarify the issue for you. There's even a code example, and a quiz!

The salient point is that structs obey value semantics and classes do not and so this must mean something different for the two. this is readonly for a class, but not for a struct. The following code is legal

struct Point {
    public int x;
    public int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void DoGoTime() {
        GoTime(ref this);
    }

    public static void GoTime(ref Point p) {
        p.x = 100;
        p.y = 100;
    }
}

but is not if "struct" is replaced by "class."

like image 92
jason Avatar answered Oct 24 '22 01:10

jason


When you're dealing with structs, you're dealing with value types.

In a class, "this" is a reference to the current instance. This lets you mutate the class instance by setting properties/fields on the class.

However, if you're in a struct, things act differently. When you're in a struct's method, "this" lets you mutate the struct. However, if you're using this in a method, you're almost always dealing with a copy of the "original" struct.

For example:

struct Test
{
    int i;
    void Mutate() {
         this.i += 1;
    }
}

When you use this:

void MutateTest(Test instance)
{
    instance.Mutate();
}

{
    Test test = new Test();
    test.i = 3;
    Console.WriteLine(test.i); // Writes 3
    test.Mutate(); // test.i is now 4
    Console.WriteLine(test.i); // Writes 4
    MutateTest(test); // MutateTest works on a copy.. "this" is only part of the copy itself
    Console.WriteLine(test.i); // Writes 4 still
}

Now, the stranger part - this is valid, and what that quote was saying:

struct Test
{
    public Test(int value)
    {
       this.i = value;
    }
    int i;

    void Mutate(int newValue) {
         this = new Test(newValue); // This wouldn't work with classes
    }
}


///
{
    Test test = new Test();
    test.i = 3;
    Console.WriteLine(test.i); // Writes 3
    test.Mutate(4); 
    Console.WriteLine(test.i); // Writes 4
like image 40
Reed Copsey Avatar answered Oct 24 '22 03:10

Reed Copsey


Jason's answer and Eric's post show one aspect of this which is interesting... but there's another which is even more alarming:

You can reassign this within a method, even if the type is otherwise immutable.

To demonstrate it, we'll use a struct which is stored in a non-readonly variable, but which contains a readonly field:

using System;

public struct LooksImmutable
{
    private readonly int value;
    public int Value { get { return value; } }

    public LooksImmutable(int value)
    {
        this.value = value;
    }

    public void GoCrazy()
    {
        this = new LooksImmutable(value + 1);
    }
}

public class Test
{
    static void Main()
    {
        LooksImmutable x = new LooksImmutable(5);
        Console.WriteLine(x.Value);
        x.GoCrazy();
        Console.WriteLine(x.Value);
    }
}
like image 40
Jon Skeet Avatar answered Oct 24 '22 03:10

Jon Skeet