Fehler mit Dictionary

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    Fehler mit Dictionary

    Hallo liebe Community,
    ich habe momentan ein Problem, das ich mir nicht erklären kann. Hier erstmal der Code:

    VB.NET-Quellcode

    1. _Images = New Dictionary(Of String, Bitmap)
    2. For Each Image As String In assembly.GetManifestResourceNames
    3. If Image.EndsWith(".png") Then
    4. Dim Key As String = Image.Substring(Image.IndexOf("."c) + 1)
    5. If Not _Images.ContainsKey(Key) Then _Images.Add(Key, GetEmbeddedImage(assembly, Image))
    6. End If
    7. Next

    Hier kommt immer der Fehler "Ein Element mit dem gleichen Schlüssel wurde bereits hinzugefügt." allerdings prüfe ich ja, ob der Schlüssel schon vorhanden ist.

    Ist das ein Frameworkproblem oder mache ich da irgendwas falsch?
    Was sagt denn die Variablenüberwachung, wenn Du Dir in diesem Falle das Dictionary mal ansiehst?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi
    normalerweise würde man die Abfrage sowieso Try-Catchen - zumindest, wenn man das Konzept des Dictionarys so ausweitet, dass möglichst keine redundanten Vorgänge erfolgen sollen. Zu Catchen wäre eine ArgumentException. Alternativ - zum Überschreiben, kannst du aber auch einfach den Setter der Default-Property verwenden. Der setzt aber bei jedem Auftreten den Wert neu.
    Ist der Resourcebezeichner minus der Dateierweiterung denn mehrfach vorhanden?

    Gruß
    ~blaze~
    Ein Dictionary funktioniert ja mittels einer Hashtabelle. Ein String ist ja nun ein Referenztyp, bei dem die Vergleichsoperatoren so überschrieben wurden, dass der Inhalt des Strings verglichen wird. Aber die sind für ein Dictionary ja egal. Das ruft nur GetHashCode auf. Also wenn die beiden Strings verschiedene Hashes liefern sollten, hast du das Problem gefunden.

    Skybird schrieb:

    Das sind ja Ubisoftmethoden hier !

    Weil es Referenzen auf unterschiedliche Speicherinhalte sind? Ich weiß nicht, ob die Funktion so überschrieben wurde, dass ein Hash des Inhalts des Strings erstellt wird. Das müsstest du mal prüfen.

    Skybird schrieb:

    Das sind ja Ubisoftmethoden hier !

    Es passen alle reingegebenen Werte, das ist ja das Problem. Ich hab auch noch etwas rumgespielt und eine funktionierende Einzeilerlösung gefunden:

    VB.NET-Quellcode

    1. _Images = assembly.GetManifestResourceNames.Where(Function(filename) filename.EndsWith(".png")).ToDictionary(Function(image) image.Substring(image.IndexOf("."c) + 1), Function(image) GetEmbeddedImage(assembly, image))
    bzw. kurz:

    VB.NET-Quellcode

    1. _Images = assembly.GetManifestResourceNames.Where(Function(s) s.EndsWith(".png")).ToDictionary(Function(s) s.Substring(s.IndexOf("."c) + 1), Function(s) GetEmbeddedImage(assembly, s))

    Trotztem würde es mich interessieren, dem Problem auf den Grund zu gehen. Niemand einen Ansatz?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets3646“ ()

    Dieses mal nimmst du aber eh den kompletten String als Schlüssel, wenn ich mich nicht täusche?
    @vb-checker:
    Würde prinzipiell keinen Unterschied machen, oder? Contains und Add verwenden quasi dieselben Äquivalenzvergleiche. Wenn man Equals überschreibt sollte man aber auch GetHashCode überschreiben. Btw. gilt auch, dass wenn man den =- oder <>-Operator überlädt, dass man Equals und GetHashCode überschreiben sollte.

    Gruß
    ~blaze~
    @~blaze~: Hm, stimmt. Habe es mal nachgeschaut, GetHashCode ist bei inhaltsgleichen Strings auch gleich.
    Ich kann das aber auch nicht reproduzieren. Das müsste doch genau das selbe sein, wie bei dir, oder?

    VB.NET-Quellcode

    1. Dim dic As New Dictionary(Of String, Integer)
    2. Dim a As String = ".png"
    3. dic.Add(a, a.GetHashCode)
    4. Dim b As String = ".png"
    5. If dic.ContainsKey(b) = False Then dic.Add(b, b.GetHashCode)

    Bei mir kommt kein Fehler und am Ende ist auch nur 1 Element im Dictionary. Welche Frameworkversion nutzt du denn? Ich habe es mit 4.0 Client Profile getestet. Nach einer halben Stunde rumprobieren, in den Framework-Code zu steppen beim Debuggen (was laut Codeproject nicht möglich ist, wenn Dateien von Sicherheitsupdates verändert wurden -.-), habe ich mir die mscorlib.dll im IL DASM angeschaut. ContainsKey()macht sowas:

    Quellcode

    1. public bool ContainsKey(TKey key)
    2. {
    3. return (this.FindEntry(key) >= 0);
    4. }

    FindEntry() benutzt dann GetHashCode und den IEqualityComparer.

    Übrigens habe ich diesen Tipp gefunden: blog.lab49.com/archives/840 also probiere es doch mal mit TryGetValue()

    Skybird schrieb:

    Das sind ja Ubisoftmethoden hier !

    Dictionary funktioniert folgendermaßen:
    - Add: hinzufügen, Exception wird geworfen, wenn ein Schlüssel bereits vorhanden ist
    - Default Set: setzen, egal ob ein Schlüssel bereits gesetzt ist
    - Default Get: zurückgeben, Exception wird geworfen, wenn kein Wert vorhanden ist
    - TryGetValue: zurückgeben, ob der zum Schlüssel zugehörige Wert gefunden wurde und bei Erfolg im 2. Parameter zurückgeben
    - ContainsKey: zurückgeben, ob ein zum Schlüssel gehöriger Eintrag gefunden wurde
    - Remove: Eintrag ggf. entfernen, wenn gefunden. Gibt zurück, ob gelöscht wurde.
    Default Set und Get sind halt die Methoden, die aufgerufen werden, wenn man direkt auf der Instanz arbeitet:

    VB.NET-Quellcode

    1. Dim dct As IDictionary(Of TKey, TValue) = value
    2. dtc(key) = someValue
    3. Debug.WriteLine(dtc(key))

    Man sollte möglichst alle Operationen in einem Rutsch durchführen, da eben das Suchen des Eintrags ggf. lange dauert.

    Gruß
    ~blaze~