C# – Byte Array als Key in einem Dictionary / HashSet
Heute stand ich vor dem Problem, dass ich ein Byte Array als Index in einem Dictionary in .NET verwenden wollte. Das hat natürlich nicht funktioniert, da der Hash-Code von zwei inhaltsgleichen Byte-Arrays ungleich ist und dadurch der Vergleich beim Zugriff fehl schlägt. Wenn ich also überprüfen möchte, ob ein Key bereits in der Datenstruktur vorhanden ist, wird es immer fehl schlagen, da das erzeugte Objekt mit in den Hashwert eingeht.
Das folgende kleine Beispiel zeigt das Problem:
|
1 2 3 4 |
byte[] arr1 = new byte[] { 1, 2, 3 }; byte[] arr2 = new byte[] { 1, 2, 3 }; Console.WriteLine(string.Format("Hash Array 1 = '{0}', Hash Array 2 = '{1}'", arr1.GetHashCode(), arr2.GetHashCode())); |
Man erhält als Ergebnis "Hash Array 1 = '37121646', Hash Array 2 = '45592480'”. Fügt man also das erste Array z.B. einem Dictionary hinzu, wird mit bei einer Abfrage mit ContainsKey(...) ein false erhalten.
Glücklicherweise gibt es seit .NET 3.5 die Möglichkeit, diesen Datentypen im Konstruktor einen IEqualityComparer<T> mitzugeben, der dann den Vergleich übernimmt. Eine Klasse, die dieses Interface implementiert, muss die Methoden Equals(...) und GetHashCode(...) zur Verfügung stellen. Für meinen Anwendungsfall reicht die folgende Implementierung:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
internal class ByteArrayComparer : IEqualityComparer<byte[]> { public bool Equals(byte[] x, byte[] y) { if (x == null || y == null) { return x == y; } return x.SequenceEqual(y); } public int GetHashCode(byte[] obj) { return BitConverter.ToString(obj).GetHashCode(); } } |
Wichtig ist hier speziell die GetHashCode(...) Methode. Hier wird der Inhalt des Arrays mit dem BitConverter in einen Hex-String konvertiert und von diesem String der HashCode zurückgegeben. Der Hash wird dann also über den Inhalt gebildet und ist dann bei beiden Arrays gleich. Unsere neue Implementierung wird dem Konstruktor dann mit übergeben und dann in der entsprechenden Datenstruktur verwendet. Das folgende Beispiel zeigt die Anwendung und ein "Vorher / Nachher" Vergleich:
|
1 2 3 4 5 6 7 8 9 10 |
byte[] arr1 = new byte[] { 1, 2, 3 }; byte[] arr2 = new byte[] { 1, 2, 3 }; //ohne IEqualityComparer HashSet<byte[]> test = new HashSet<byte[]>() { arr1 }; Console.WriteLine(string.Format("Ohne IEqualityComparer - Contains = {0}", test.Contains(arr2))); //mit IEqualityComparer test = new HashSet<byte[]>(new ByteArrayComparer()) { arr1 }; Console.WriteLine(string.Format("Mit IEqualityComparer - Contains = {0}", test.Contains(arr2))); |
Startet man das Programm, erhält man folgende Ausgabe "Ohne IEqualityComparer - Contains = False. Mit IEqualityComparer - Contains = True". Problem gelöst
.
HP Touchpad im aktuellen Milka Werbespot
War das da nicht gerade? Doch, oder? Gibt’s doch nicht! – So in etwa sah meine Reaktion aus, als ich in einer Werbepause den aktuellen Werbespot der Firma Milka zu ihrem Produkt “Cake & Choc” gesehen habe. Da wird doch tatsächlich das von HP verschmähte HP Touchpad in der Werbung gezeigt. Ich bin ja immer noch froh, dass ich das Ding habe und benutze es – nach wie vor – täglich. Schade, dass es von HP so voreilig aufgegeben wurde…
Hier der Werbespot – zu sehen ist das Touchpad ab der 4. Sekunde:
DES / 3DES Verschlüsselungsbibliothek für Arduino
Für ein kleines Projekt auf dem Arduino Leonardo habe ich vor kurzem eine Crypto-Bibliothek gesucht, mit der ich DES und 3DES Nachrichten verschlüsseln und entschlüsseln kann. Leider bin ich nicht fündig geworden, bzw. zumindest nicht als Bibliothek für die Entwicklungsumgebung des Arduinos. Bei “Das Labor” gibt es aber eine hervorragende Crypto-Bibliothek für Atmel AVR Mikrocontroller – nichts anderes ist ja auch ein Arduino – also habe ich mir den DES / 3DES Teil geschnappt und ihn auf den Arduino portiert. Das Ergebnis habe ich, ganz der GPL folgend, auf GitHub verfügbar gemacht. Solltet ihr also in einem Sketch bei euch auch DES / 3DES Verschlüsselung benötigen, dann könnt ihr die Bibliothek verwenden. Wenn ihr Änderungen habt, dann nichts wie her damit (entweder als Patch oder als Pull-Request per GitHub).