Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What advantage is there to storing "this" in a local variable in a struct method?

Tags:

c#

.net

People also ask

What is the advantage of local variable?

Advantages of using local variables: We do not have to take care of deleting unnecessary variables when the task is complete because local variables are deleted from memory automatically when their task is complete. When you use local variables, you do not have to worry that they will be changed by another task.

Why do we use struct in C#?

The struct (structure) is like a class in C# that is used to store data. However, unlike classes, a struct is a value type. Suppose we want to store the name and age of a person. We can create two variables: name and age and store value.

What is the use of struct?

Structures (also called structs) are a way to group several related variables into one place. Each variable in the structure is known as a member of the structure. Unlike an array, a structure can contain many different data types (int, float, char, etc.).

What do you mean by local variables?

A local variable is a type of variable that can be used where the scope and extent of the variable is within the method or statement block in which it is declared. It is used as an iteration variable in the foreach statement, exception variable in the specific-catch clause and resource variable in the using statement.


As you already noticed, System.Collections.Immutable.ImmutableArray<T> is a struct:

public partial struct ImmutableArray<T> : ...
{
    ...

    T IList<T>.this[int index]
    {
        get
        {
            var self = this;
            self.ThrowInvalidOperationIfNotInitialized();
            return self[index];
        }
        set { throw new NotSupportedException(); }
    }

    ...

var self = this; creates a copy of the struct referred to by this. Why should it need to do that? The source comments of this struct give an explanation of why it is necessary:

/// This type should be thread-safe. As a struct, it cannot protect its own fields
/// from being changed from one thread while its members are executing on other threads
/// because structs can change in place simply by reassigning the field containing
/// this struct. Therefore it is extremely important that
/// ** Every member should only dereference this ONCE. **
/// If a member needs to reference the array field, that counts as a dereference of this.
/// Calling other instance members (properties or methods) also counts as dereferencing this.
/// Any member that needs to use this more than once must instead
/// assign this to a local variable and use that for the rest of the code instead.
/// This effectively copies the one field in the struct to a local variable so that
/// it is insulated from other threads.

In short, if it is possible that other threads are making changes to a field of the struct or changing the struct in place (by reassigning a class member field of this struct type, for example) while the get method is being executed and thus could cause bad side effects, then it becomes necessary for the get method to first make a (local) copy of the struct before processing it.

Update: Please also read supercats answer, which explains in detail which conditions must be fulfilled so that an operation like making a local copy of a struct (i.e. var self = this;) is being thread-safe, and what could happen if those conditions are not met.


Structure instances in .NET are always mutable if the underlying storage location is mutable, and always immutable if the underlying storage location is immutable. It's possible for structure types to "pretend" to be immutable, but .NET will allow structure-type instances to be modified by anything that can write the storage locations in which they reside, and the structure types themselves have no say in the matter.

Thus, if one had a struct:

struct foo {
  String x;
  override String ToString() {
    String result = x;
    System.Threading.Thread.Sleep(2000);
    return result & "+" & x;
  }
  foo(String xx) { x = xx; }
}

and one were to invoke the following method on two threads with the same array myFoos of type foo[]:

myFoos[0] = new foo(DateTime.Now.ToString());
var st = myFoos[0].ToString();

it would be entirely possible that whichever thread started first would have its ToString() value report the time written by its constructor call and the time reported by the other thread's constructor call, rather than reporting the same string twice. For methods whose purpose is to validate a structure field and then use it, having the field change between the validation and the use would result in the method using an unvalidated field. Copying the contents of the structure's field (either by copying just the field, or by copying the whole structure) avoids that danger.

Note that for structures which contain a field of type Int64, UInt64, or Double, or which contain more than one field, it is possible that a statement like var temp=this; which occurs in one thread while another thread is overwriting the location where this had been stored, may end up copying a structure which holds an arbitrary mixture of old and new content. Only when a structure contains a single field of a reference type, or a single field of a 32-bit-or-smaller primitive, is it guaranteed that a read that occurs simultaneous with a write will yield some value that the structure actually held, and even that may have some quirks (e.g. at least in VB.NET, a statement like someField = New foo("george") may clear someField before calling the constructor).