我在查看BitConverter类的源代码时,突然发现大量使用 fixed关键字.

Reference Source

如下代码,将Int16转化为两个字节【C#中,是低字节在前的】

public static readonly bool IsLittleEndian = true;
// Converts a short into an array of bytes with length
        // two.
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe static byte[] GetBytes(short value)
        {
            Contract.Ensures(Contract.Result<byte[]>() != null);
            Contract.Ensures(Contract.Result<byte[]>().Length == 2);
 
            byte[] bytes = new byte[2];
            fixed(byte* b = bytes)
                *((short*)b) = value;
            return bytes;
        }

将4个字节转化为一个Int32数

// Converts an array of bytes into an int.  
        [System.Security.SecuritySafeCritical]  // auto-generated
        public static unsafe int ToInt32 (byte[] value, int startIndex) {
            if( value == null)  {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
            }
        
            if ((uint) startIndex >= value.Length) {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
            }
        
            if (startIndex > value.Length -4) {
                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
            }
            Contract.EndContractBlock();
 
            fixed( byte * pbyte = &value[startIndex]) {
                if( startIndex % 4 == 0) { // data is aligned 
                    return *((int *) pbyte);
                }
                else {
                    if( IsLittleEndian) { 
                        return (*pbyte) | (*(pbyte + 1) << 8)  | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
                    }
                    else {
                        return (*pbyte << 24) | (*(pbyte + 1) << 16)  | (*(pbyte + 2) << 8) | (*(pbyte + 3));                        
                    }
                }
            }
        }

下面对fixed关键字进行说明 以及使用场景

参考微软官方文档:

fixed 语句 -
C# 参考 | Microsoft Docs

fixed 语句可防止垃圾回收器重新定位可移动的变量。 fixed 语句仅允许存在于fixed上下文中。 还可以使用 fixed 关键字创建fixed。一般fixed关键字都用在unsafe【不安全】的环境中

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

使用fixed关键字 可以提升运算的性能,

  不能使用 fixed 语句来获取已固定的表达式的地址,因此fixed不能获取结构struct的地址

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

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

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

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

使用VisualStudio2022新建控制台应用程序FixedDemo,选择.net 5.0

右键FixedDemo项目,选择属性,勾选“允许使用unsafe关键字编译的代码”。

 源程序如下:

using System;

namespace FixedDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"测试fixed关键字,将数组的一部分转化为Span");
            FixedSpanExample();
            Console.WriteLine($"测试使用指针修改存储的值");
            ModifyFixedStorage();
            Console.WriteLine();
        }

        /// <summary>
        /// fixed关键字示例
        /// </summary>
        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)
            {
                // 计算数字之和 1,4,6,4,1
                int sum = 0;
                for (int i = 0; i < RowFive.Length; i++)
                {
                    sum += *(ptrToRow + i);
                }
                Console.WriteLine(sum);
            }
        }

        /// <summary>
        /// 使用fixed指针修改内存的值,不能使用fixed关键字获取结构的地址
        /// </summary>
        unsafe private static void ModifyFixedStorage()
        {
            // Variable pt is a managed variable, subject to garbage collection.
            Coordinate coordinate = new Coordinate()
            {
                X = 100,
                Y = 200
            };
            Console.WriteLine($"初始值:X={coordinate.X},Y={coordinate.Y}");

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

            fixed (int* p = &coordinate.X, q = &coordinate.Y)
            {
                *p = 12345;
                *q = 67890;
            }
            Console.WriteLine($"修改后的值:X={coordinate.X},Y={coordinate.Y}");
        }
    }

    /// <summary>
    /// 坐标,fixed关键字不能获取已固定的表达式的地址
    /// 因此fixed不能获取结构struct的地址
    /// </summary>
    class Coordinate 
    {
        public int X;
        public int Y;
        public int Z;
    }
}

程序运行如图:

 

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐