Оператор fixed (Справочник по C#)fixed Statement (C# Reference)

Оператор fixed не позволяет сборщику мусора переносить перемещаемую переменную.The fixed statement prevents the garbage collector from relocating a movable variable. Оператор fixed допускается только в небезопасном контексте.The fixed statement is only permitted in an unsafe context. Можно также использовать ключевое слово fixed для создания буферов фиксированного размера.You can also use the fixed keyword to create fixed size buffers.

Оператор fixed задает указатель на управляемую переменную и "закрепляет" эту переменную во время выполнения оператора.The fixed statement sets a pointer to a managed variable and "pins" that variable during the execution of the statement. Указатели на перемещаемые управляемые переменные полезны только в контексте fixed.Pointers to movable managed variables are useful only in a fixed context. Без контекста fixed при сборке мусора эти переменные могут переноситься непредсказуемым образом.Without a fixed context, garbage collection could relocate the variables unpredictably. Компилятор C# позволяет присвоить указатель только управляемой переменной в операторе fixed.The C# compiler only lets you assign a pointer to a managed variable in a fixed statement.

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;
    }
}

Вы можете инициализировать указатель, используя массив, строку, буфер фиксированного размера или адрес переменной.You can initialize a pointer by using an array, a string, a fixed-size buffer, or the address of a variable. Следующий пример иллюстрирует использование переменных адресов, массивов и строк:The following example illustrates the use of variable addresses, arrays, and strings:

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]) { /*...*/ } 

Начиная с C# 7.3, оператор fixed работает с дополнительными типами, помимо массивов, строк, буферов фиксированного размера и неуправляемых переменных.Starting with C# 7.3, the fixed statement operates on additional types beyond arrays, strings, fixed size buffers, or unmanaged variables. Любой тип, реализующий метод GetPinnableReference, можно зафиксировать.Any type that implements a method named GetPinnableReference can be pinned. GetPinnableReference должен вернуть переменную ref неуправляемого типа.The GetPinnableReference must return a ref variable of an unmanaged type. Типы .NET System.Span<T> и System.ReadOnlySpan<T>, представленные в .NET Core 2.0, используют этот шаблон и могут быть зафиксированы.The .NET types System.Span<T> and System.ReadOnlySpan<T> introduced in .NET Core 2.0 make use of this pattern and can be pinned. Это показано в следующем примере:This is shown in the following example:

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);
    }
}

При создании типов, которые должны участвовать в этом шаблоне, перейдите по ссылке Span<T>.GetPinnableReference() для примера реализации шаблона.If you are creating types that should participate in this pattern, see Span<T>.GetPinnableReference() for an example of implementing the pattern.

В одном операторе можно инициализировать несколько указателей одного типа.Multiple pointers can be initialized in one statement if they are all the same type:

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

Чтобы инициализировать указатели разных типов, просто вложите операторы fixed, как показано в приведенном ниже примере.To initialize pointers of different types, simply nest fixed statements, as shown in the following example.

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

После выполнения кода в операторе закрепление всех переменных отменяется, и они могут пройти сборку мусора.After the code in the statement is executed, any pinned variables are unpinned and subject to garbage collection. Таким образом, не следует использовать указатели на эти переменные за пределами оператора fixed.Therefore, do not point to those variables outside the fixed statement. Переменные, объявленные в операторе fixed, относятся к этому оператору, что упрощает выполнение следующего выражения.The variables declared in the fixed statement are scoped to that statement, making this easier:

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

Указатели, инициализированные в операторах fixed, являются переменными только для чтения.Pointers initialized in fixed statements are readonly variables. Чтобы изменить значение указателя, необходимо объявить переменную второго указателя и изменить ее.If you want to modify the pointer value, you must declare a second pointer variable, and modify that. Переменную, объявленную в операторе fixed, изменить невозможно.The variable declared in the fixed statement cannot be modified:

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

Вы можете выделить память в стеке, где на нее не будет распространяться сборка мусора, поэтому ее не нужно будет закреплять.You can allocate memory on the stack, where it is not subject to garbage collection and therefore does not need to be pinned. Для этого воспользуйтесь оператором stackalloc.To do that use the stackalloc operator.

Спецификация языка C#C# language specification

Подробные сведения см. в разделе The fixed statement (Оператор fixed) спецификации языка C#.For more information, see The fixed statement section of the C# language specification.

См. такжеSee also