fixed 语句(C# 参考)

fixed 语句可防止垃圾回收器重新定位可移动的变量。 fixed 语句仅允许存在于不安全的上下文中。 还可以使用 fixed 关键字创建固定大小的缓冲区

fixed 语句将为托管变量设置一个指针,并在该语句的执行过程中“单边锁定”该变量。 仅可在 fixed 上下文中使用指向可移动托管变量的指针。 如果没有 fixed 上下文,垃圾回收可能会不可预测地重定位变量。 C# 编译器只允许将指针分配给 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;
    }
}

可以通过使用一个数组、字符串、固定大小的缓冲区或变量的地址来初始化指针。 以下示例演示变量地址、数组和字符串的使用方式:

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 语句可在数组、字符串、固定大小缓冲区或非托管变量以外的其他类型上执行。 实施名为 GetPinnableReference 的方法的任何类型都可以被固定。 GetPinnableReference 必须返回非托管类型ref 变量。 .NET Core 2.0 中引入的 .NET 类型 System.Span<T>System.ReadOnlySpan<T> 使用此模式,并且可以固定。 下面的示例对此进行了演示:

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() 以查看有关实施此模式的示例。

如果它们都是同一类型,则可以在一个语句中初始化多个指针:

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

若要初始化不同类型的指针,只需嵌套 fixed 语句,如下面的示例中所示。

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

执行该语句中的代码之后,任何固定的变量都将被解锁并受垃圾回收的约束。 因此,请勿指向 fixed 语句之外的那些变量。 在 fixed 语句中声明的变量的作用域为该语句,使此操作更容易:

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

fixed 语句中初始化的指针为只读变量。 如果想要修改指针值,必须声明第二个指针变量,并修改它。 不能修改在 fixed 语句中声明的变量:

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

可以在堆栈上分配内存,在这种情况下,内存不受垃圾回收的约束,因此不需要固定。 为此,请使用 stackalloc 表达式

C# 语言规范

有关详细信息,请参阅 C# 语言规范中的 fixed 语句部分。

请参阅