小心C#中的只读结构体成员
生活随笔
收集整理的這篇文章主要介紹了
小心C#中的只读结构体成员
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
示例
- 我們先來看一段結構體的代碼 (基于 VS2022 + .NET 8.0)
public struct MyStruct(int number)
{
public int Number = number;
public void SetNumber(int number) => Number = number;
}
public class Program
{
private static MyStruct myStruct = new(1);
public static void Main()
{
int before = myStruct.Number;
myStruct.SetNumber(2);
int after = myStruct.Number;
Console.WriteLine($"before: {before}");
Console.WriteLine($" after: {after}");
Console.ReadKey();
}
}
輸出如下:
before: 1
?after: 2
修改為只讀
private static readonly MyStruct myStruct = new(1);
輸出如下:
before: 1
?after: 1
- 我們看到,修改只讀結構體成員的字段失敗了,但是編譯器竟然沒有報錯
- 如果我們直接操作
myStruct.Number = 2;編譯器是會報錯的,但是加了一個方法間接的修改,編譯器就歇菜了
內部原理
我們查看反匯編代碼,可以看到,在實際操作只讀結構體成員字段的時候,會把該字段的值拷貝一份到一個新的堆棧變量上,然后再基于拷貝后的這個變量計算
17: myStruct.SetNumber(2);
mov rcx,7FF9BD68E500h
mov edx,9
call CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE (07FFA1D15B6F0h)
mov rcx,26AE19DB1D0h //rcx保存結構體Number的地址
mov rcx,qword ptr [rcx] //拷貝Number的值到rcx
mov qword ptr [rbp+70h],rcx //rcx的值賦值到臨時變量
lea rcx,[rbp+70h]
mov edx,2
call ConsoleTest_NET_8.MyStruct.SetNumber(Int32) (07FF9BD6A2BC8h)
導致的問題
- 我們先來看一段自旋鎖的代碼,基于 SpinLock
public class Program
{
private static readonly SpinLock spinLock = new(false);
public static void Main()
{
int sum = 0;
Parallel.For(0, ushort.MaxValue, i =>
{
bool lockToken = false;
try
{
spinLock.Enter(ref lockToken);
sum++;
}
finally
{
if (lockToken)
{
spinLock.Exit();
}
}
});
Console.WriteLine(sum);
Console.ReadKey();
}
}
- 我們期望的輸出是: 65535, 但實際不是,因為隱藏的只讀機制導致了字段值的拷貝, 這就造成了隱藏的 BUG
結論
-
避免把結構體成員變量設置只讀
-
在確定結構體內的字段只讀時,可以使用 readonly 直接修飾 結構體本身或者字段,比如
public readonly struct MyStruct(int number) { public readonly int Number = number; }
總結
以上是生活随笔為你收集整理的小心C#中的只读结构体成员的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦见栽竹子什么意思
- 下一篇: 梦见麻将8条是什么意思