[英]Changing a struct inside another struct in a foreach loop
打印以下代碼(調用MyMethod時):
0
0
0
1
我希望它能打印出來:
0
0
1
1
為什么是這樣?
碼:
private struct MyStruct
{
public MyInnerStruct innerStruct;
}
private struct MyInnerStruct
{
public int counter;
public void AddOne()
{
++counter;
}
}
public static void MyMethod()
{
MyStruct[] myStructs = new MyStruct[] { new MyStruct() };
foreach (var myStruct in myStructs)
{
MyStruct myStructCopy = myStruct;
Console.WriteLine(myStruct.innerStruct.counter);
Console.WriteLine(myStructCopy.innerStruct.counter);
myStruct.innerStruct.AddOne();
myStructCopy.innerStruct.AddOne();
Console.WriteLine(myStruct.innerStruct.counter);
Console.WriteLine(myStructCopy.innerStruct.counter);
}
}
您看到此行為的原因必須是使用迭代變量。 迭代變量是只讀的,因為在C#中你無法修改它們(C#lang spec section 8.8.4詳細說明了這一點
迭代變量對應於只讀局部變量,其范圍擴展到嵌入語句
使用只讀的可變結構是一種意外行為的途徑。 您不是直接使用變量,而是實際使用變量的副本。 因此,在myStruct
的情況下,副本會增加,而不是實際值。 這就是原始值保持不變的原因。
Eric在這個主題上做了一篇相當深入的文章,你可以在這里訪問
你應該總是擁有不可變結構的另一個原因。
迭代變量是只讀的,通常禁止直接修改只讀結構,或者通過ref
這樣的結構或其任何部分來修改任何會修改它的代碼。 不幸的是,有一個問題:盡管許多結構方法和屬性不會改變底層結構,但是在結構上調用方法或屬性需要通過ref
傳遞它,無論方法或屬性是否會改變結構實例。 這給C#的設計者留下了五個選擇:
選擇#1會有點惱人,特別是關於通過屬性暴露其狀態的結構。 從性能的角度來看,選擇#2或#4會很好,但是意外寫this
結構例程可能會導致意外行為。 微軟選擇了#3(除了添加可選屬性外,選擇#5將是相同的)。 在許多方面,這是最糟糕的選擇(不寫this
會比沒有復制操作的方法運行得慢,編寫this
方法無論是否有效都無法正常工作,並且結構中唯一被破壞的方法寫一個只讀變量將是一個代碼已被破壞的結構的實例。 盡管如此,這種選擇現在已經很成熟,而且它就是這樣。
順便提一下,即使是嵌入在結構類型的只讀結構或屬性中的讀取操作結構也可能比使用非只讀字段要昂貴得多,而只是避免編寫它們。 在調用myStruct.innerStruct.AddOne()
,編譯器首先制作MyStruct
的副本,然后復制myStruct.InnerStruct
,然后在其上調用AddOne()
方法。 相比之下,當調用myStructCopy.innerStruct.AddOne()
,它可以通過引用AddOne()
方法傳遞innerStruct
而不復制任何東西。
我想你會發現MyStruct.innerStruct實際上返回了struct的副本,而不是struct本身。 因此,您正在增加副本的價值......
當然我最近在某處讀了一篇關於這個的博客。
如果您更改以下內容
myStruct.innerStruct.AddOne();
Console.WriteLine(myStruct.innerStruct.counter);
至
MyInnerStruct inner = myStruct.innerStruct;
inner.AddOne();
Console.WriteLine(inner.counter);
然后你應該看到它開始工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.