[英]Dictionary value c#
如果我們在.Net中循環訪問字典,則無法更改。 但是為什么字典中的值是只讀的。 任何人有任何想法嗎? 為什么.Net團隊決定在遍歷字典時不更改值。 我能理解密鑰是否不能更改,但為什么要值? 另外,如果您正在使用LINQ並以Ienumerable的形式取回密鑰,則可以更改該值嗎? 延遲加載有作用嗎?
為什么BCL團隊決定在循環字典時不允許更改值 。 我知道密鑰不能更改,但是為什么不允許更改值呢?
我不能確切地為創建字典的團隊說些什么,但是我可以做出一些有根據的猜測 。
首先,經常有人說,好的程序對輸出的正確性要求嚴格,但是卻寬容了它們接受的內容。 我認為這不是一個好的設計原則。 這是一個糟糕的設計原則,因為它允許錯誤的調用者 依賴 未定義和不受支持的行為。 因此,這產生了向后兼容的負擔。 (我們肯定在Web瀏覽器世界中看到了這一點,在該世界中,每個瀏覽器供應商都被迫與其他所有瀏覽器接受的錯誤HTML兼容。)
組件定義合同-他們說接受什么信息作為輸入,支持什么操作以及產生什么結果。 當您接受合同中未包含的輸入內容或支持合同中不包含的操作時,您正在做的實際上是簽訂新合同,該合同沒有記錄,不受支持並且將來可能會被破壞版。 您基本上是在建造定時炸彈,當定時炸彈熄滅時,要么客戶(可能甚至不知道他們在做不受支持的事情)崩潰,要么組件提供商最終不得不永遠支持他們沒有做過的合同實際上沒有注冊。
因此, 嚴格執行合同是一個好的設計原則。 集合上的IEnumerable合同“在迭代時修改集合是非法的”。 該合同的實施者可以選擇說“好吧,我碰巧知道某些修改是安全的,所以我會允許這些修改”,嘿,突然之間,您不再執行該合同。 您正在執行另一種無證的合同,人們會依賴該合同。
最好只是執行合同,即使那是不必要的。 這樣一來,將來您就可以自由地依賴已記錄在案的合同,而不必擔心某些呼叫者違反了該合同並放棄了該合同,並希望它能夠永遠永遠如此。
設計一個允許在迭代過程中發生值突變的字典會很容易。 這樣做會阻止組件提供程序永遠關閉該功能,並且不需要提供這些功能,因此在有人嘗試時給出錯誤比允許調用者違反合同更好。
第二個猜測:字典類型未密封,因此可以擴展。 如果在枚舉過程中更改了值,則第三方可能會擴展字典,從而違反其不變式。 例如,假設某人以一種可以按value枚舉的方式擴展字典。
當您編寫一個接受Dictionary並對其執行操作的方法時,您會假定該操作將對所有詞典 ,甚至第三方擴展都適用。 打算進行擴展的組件的設計人員需要比平時更加小心,以確保對象執行其合同,因為未知的第三方可能依賴於該合同的執行。 因為可能有一個字典不支持在迭代過程中更改值,所以基類也不應該支持它。 否則將違反派生類對基類的替代性。
另外,如果您正在使用LINQ並以IEnumerable的形式獲取密鑰,那么可以更改該值嗎?
規則是字典在迭代時不得更改。 是否使用LINQ進行迭代都無關緊要; 迭代就是迭代。
延遲加載有作用嗎?
當然。 請記住,定義LINQ查詢不會對任何內容進行迭代。 查詢表達式的結果是查詢對象。 只有當您遍歷該對象時,實際迭代才會發生在集合上。 當你說:
var bobs = from item in items where item.Name == "Bob" select item;
這里沒有迭代發生。 直到你說
foreach(var bob in bobs) ...
發生在項目上的迭代。
KeyValuePair是一個結構,可變結構是邪惡的,因此已將其設置為只讀。
要更改某些值,可以遍歷KeyValuePairs並存儲要進行的所有更新。 完成迭代后,您可以遍歷更新列表並應用它們。 這是一個如何做的示例(不使用LINQ):
Dictionary<string, string> dict = new Dictionary<string,string>();
dict["foo"] = "bar";
dict["baz"] = "qux";
List<KeyValuePair<string, string>> updates = new List<KeyValuePair<string,string>>();
foreach (KeyValuePair<string, string> kvp in dict)
{
if (kvp.Key.Contains("o"))
updates.Add(new KeyValuePair<string, string>(kvp.Key, kvp.Value + "!"));
}
foreach (KeyValuePair<string, string> kvp in updates)
{
dict[kvp.Key] = kvp.Value;
}
我不確定為什么,但是您可以通過間接引用值來解決此限制。 例如,您可以創建一個僅引用另一個這樣的類:
class StrongRef<ObjectType>
{
public StrongRef(ObjectType actualObject)
{
this.actualObject = actualObject;
}
public ObjectType ActualObject
{
get { return this.actualObject; }
set { this.actualObject = value; }
}
private ObjectType actualObject;
}
然后,您可以更改間接參考,而不必更改二分法中的值。 (我聽說過,Blue Peter使用以下例程用外觀相同的活體動物代替死狗)。
public void ReplaceDeadDogs()
{
foreach (KeyValuePair<string, StrongRef<Dog>> namedDog in dogsByName)
{
string name = namedDog.Key;
StrongRef dogRef = namedDog.Value;
// update the indirect reference
dogRef.ActualObject = PurchaseNewDog();
}
}
其他替代方法是在迭代時將必要的更改記錄在第二個字典中,然后再應用這些更改(通過迭代第二個數字),或者在迭代並構建完一個完整的替換字典后再使用此字典而不是原始字典。
從此Microsoft支持回復 (基於Hashtable
但同樣適用)
這里的關鍵是枚舉器旨在提供整個集合的快照,但是出於性能原因,它們不會將整個集合復制到另一個臨時數組或類似的東西。 相反,他們使用實時集合並在檢測到有人更改集合時拋出異常。 是的,它使這種類型的代碼更難編寫...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.