fixed (Instrucción, Referencia de C#)

La instrucción fixed evita que el recolector de elementos no utilizados reubique una variable móvil. La instrucción fixed solo se permite en un contexto de unsafe. También puede usar la palabra clave fixed para crear búferes de tamaño fijo.

La instrucción fixed establece un puntero a una variable administrada y "ancla" esa variable durante su ejecución. Los punteros a variables administradas móviles solo son útiles en un contexto fixed. Sin un contexto fixed, la recolección de elementos no utilizados podría reubicar las variables de forma impredecible. El compilador de C# solo permite asignar un puntero a una variable administrada en una instrucción fixed.

class Point
{
    public int x;
    public int y;
}

unsafe private static void ModifyFixedStorage()
{
    // Variable pt is a managed variable, subject to garbage collection.
    Point pt = new Point();

    // Using fixed allows the address of pt members to be taken,
    // and "pins" pt so that it is not relocated.

    fixed (int* p = &pt.x)
    {
        *p = 1;
    }
}

Puede inicializar un puntero mediante una matriz, una cadena, un búfer de tamaño fijo o la dirección de una variable. En el ejemplo siguiente se muestra el uso de direcciones, matrices y cadenas de variable:

Point point = new Point();
double[] arr = { 0, 1.5, 2.3, 3.4, 4.0, 5.9 };
string str = "Hello World";

// The following two assignments are equivalent. Each assigns the address
// of the first element in array arr to pointer p.

// You can initialize a pointer by using an array.
fixed (double* p = arr) { /*...*/ }

// You can initialize a pointer by using the address of a variable.
fixed (double* p = &arr[0]) { /*...*/ }

// The following assignment initializes p by using a string.
fixed (char* p = str) { /*...*/ }

// The following assignment is not valid, because str[0] is a char,
// which is a value, not a variable.
//fixed (char* p = &str[0]) { /*...*/ }

A partir de C# 7.3, la instrucción fixed funciona en tipos adicionales más allá de matrices, cadenas, búferes de tamaño fijo o variables no administradas. Cualquier tipo que implemente un método denominado GetPinnableReference se puede anclar. GetPinnableReference debe devolver una variable ref a un tipo no administrado. Los tipos de .NET System.Span<T> y System.ReadOnlySpan<T> presentados en .NET Core 2.0 usan este patrón y se pueden anclar. Esto se muestra en el ejemplo siguiente:

unsafe private static void FixedSpanExample()
{
    int[] PascalsTriangle = {
                  1,
                1,  1,
              1,  2,  1,
            1,  3,  3,  1,
          1,  4,  6,  4,  1,
        1,  5,  10, 10, 5,  1
    };

    Span<int> RowFive = new Span<int>(PascalsTriangle, 10, 5);

    fixed (int* ptrToRow = RowFive)
    {
        // Sum the numbers 1,4,6,4,1
        var sum = 0;
        for (int i = 0; i < RowFive.Length; i++)
        {
            sum += *(ptrToRow + i);
        }
        Console.WriteLine(sum);
    }
}

Si crea tipos que deben participar en este patrón, consulte Span<T>.GetPinnableReference() para ver un ejemplo de implementación del patrón.

Es posible inicializar varios punteros en una sola instrucción si todos son del mismo tipo:

fixed (byte* ps = srcarray, pd = dstarray) {...}

Para inicializar punteros de tipos diferentes, simplemente anide instrucciones fixed, como se muestra en el ejemplo siguiente.

fixed (int* p1 = &point.x)
{
    fixed (double* p2 = &arr[5])
    {
        // Do something with p1 and p2.
    }
}

Después de ejecutar el código de la instrucción, las variables ancladas se desanclan y quedan sujetas a la recolección de elementos no utilizados. Por lo tanto, no debe apuntar a esas variables fuera de la instrucción fixed. Las variables declaradas en la instrucción fixed se limitan a dicha instrucción, lo que simplifica esta tarea:

fixed (byte* ps = srcarray, pd = dstarray)
{
   ...
}
// ps and pd are no longer in scope here.

Los punteros inicializados en instrucciones fixed son variables de solo lectura. Si quiere modificar el valor del puntero, debe declarar una segunda variable de puntero y modificarla. La variable declarada en la instrucción fixed no se puede modificar:

fixed (byte* ps = srcarray, pd = dstarray)
{
    byte* pSourceCopy = ps;
    pSourceCopy++; // point to the next element.
    ps++; // invalid: cannot modify ps, as it is declared in the fixed statement.
}

Puede asignar memoria en la pila, donde no está sujeta a la recolección de elementos no utilizados y, por tanto, no necesita anclarse. Para ello, use una expresión stackalloc.

Especificación del lenguaje C#

Para más información, consulte la sección La declaración fixed de Especificación del lenguaje C#.

Vea también