Dnes jsem narazil na další záludnost .NET Frameworku - ve skutečnosti to není záludnost, je to zcela logické, ale rozhodně ne samozřejmé. Pokud používáte generický objekt Dictionary, což je jakýsi slovník klíč - hodnota, občas potřebujete projít všechny jeho položky a něco s nimi provést (např. uložit je do souboru). Není nic jednoduššího než cyklus For Each:
Structure CustomData
Dim Id As Integer, Name As String, Description As String, Verze As Integer
End Structure
Dim dic As New Dictionary(Of String, CustomData)
Public Sub ProjitSlovnik()
For Each c As CustomData In dic.Values
MsgBox(c.Name & " " & c.Description)
Next
End Sub
Navíc si při deklaraci zvolíte datové typy klíče i dat a už v době psaní kódu vám Visual Studio doplňuje možnosti, protože je datový typ známý v době kompilace. Takovému způsobu říkáme strong-typing (silné typování).
Funguje to naprosto perfektně, je to rychlé a pohodlné. Až do chvíle, kdy potřebujete v cyklu změnit obsah dat - pokud jde o strukturu, máme problém. Tyto typy se totiž nepředávají referencí (adresou), ale hodnotou. Pokud tedy změníme uvnitř cyklu data, změna se neprojeví. Slovník totiž vrátí kopii dat a ne odkaz na původní položku. Je to stejné jako při předávání argumentů procedury přes ByRef a ByVal.
Public Sub ZmenitData()
Dim d As New CustomData()
d.Name = "test"
dic.Add("klic", d)
For Each c As CustomData In dic.Values
c.Name = "nazdar lidi"
Next
MsgBox(dic("klic").Name)
End Sub
Možná vás napadne, že to můžeme obejít. Třeba si vytvoříme proměnnou, do ní nejdříve vytáhneme data ze slovníku, změníme je a pak proměnnnou vrátíme zpět do slovníku. Ale to také nepůjde - skončíme s chybovou hláškou "Kolekce byla upravena. Operace výčtu pravděpodobně nebude spuštěna." (pokud máte anglický .NET framework, tak "Collection was modified; enumeration operation may not execute.").
Pokud máme obyčenjou kolekci, případně List a jemu podobné, můžeme procházet pomocí indexů - For i As Integer = 0 To seznam.Count - 1. U slovníku to však nejde - máme jen kolekci hodnot a klíčů. Ta se však ale nesmí změnit.
Jak tedy z toho ven? Ze struktury udělat třídu a do slovníku ukládat objekty této třídy. Ty se totiž vždy předávají referencí, takže je můžeme krásně měnit, změna se projeví a vše je v pořádku.
Class CustomData
Public Id As Integer, Name As String, Description As String, Verze As Integer
End Class
Její členy musíme změnit na Public nebo z nich udělat vlastnosti (což bych doporučil spíš, máme totiž kontrolu nad přiřazováním hodnot a máme prostředek přiřazení zamezit, pokud je to nutné).
A ještě jeden tip na závěr - pokud napíšete ve Visual Studiu slovo Property a stisknete tabulátor, automaticky se vloží Code snippet s celou konstrukcí vlastnosti. Doplníte pouze název lokální proměnné, datový typ a název vlastnosti. Snadněji to již opravdu nejde.