• Empleos
  • Sobre nosotros
  • profesionales
    • Inicio
    • Empleos
    • Cursos y retos
    • Preguntas
    • Profesores
  • empresas
    • Inicio
    • Publicar vacante
    • Nuestro proceso
    • Precios
    • Pruebas Online
    • Nómina
    • Blog
    • Comercial
    • Calculadora de salario

0

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

I was browsing the .NET Core source tree today and ran across this pattern in System.Collections.Immutable.ImmutableArray<T>:

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

This pattern (storing this in a local variable) seems to be consistently applied in this file whenever this would otherwise be referenced multiple times in the same method, but not when it is only referenced once. So I started thinking about what the relative advantages might be to doing it this way; it seems to me that the advantage is likely performance-related, so I went down this route a little further... maybe I'm overlooking something else.

The CIL that gets emitted for the "store this in a local" pattern seems to look something like a ldarg.0, then ldobj UnderlyingType, then stloc.0 so that later references come from ldloc.0 instead of a bare ldarg.0 like it would be to just use this multiple times.

Maybe ldarg.0 is significantly slower than ldloc.0, but not by enough for either the C#-to-CIL translation or the JITter to look for opportunities to optimize this for us, such that it makes more sense to write this weird-looking pattern in C# code any time we would otherwise emit two ldarg.0 instructions in a struct instance method?

Update: or, you know, I could have looked at the comments at the top of that file, which explain exactly what's going on...

almost 3 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

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.

almost 3 years ago · Santiago Trujillo Denunciar

0

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).

almost 3 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Nuestro proceso Comercial
Legal
Términos y condiciones Política de privacidad
© 2025 PeakU Inc. All Rights Reserved.

Andres GPT

Recomiéndame algunas ofertas
Necesito ayuda