Gibt es eine Möglichkeit, eine eindeutige Kennung einer Instanz zu erhalten?
GetHashCode()
ist für die beiden Referenzen, die auf dieselbe Instanz verweisen, gleich. Zwei verschiedene Instanzen können jedoch (ziemlich leicht) denselben Hash-Code erhalten:
Hashtable hashCodesSeen = new Hashtable();
LinkedList<object> l = new LinkedList<object>();
int n = 0;
while (true)
{
object o = new object();
// Remember objects so that they don't get collected.
// This does not make any difference though :(
l.AddFirst(o);
int hashCode = o.GetHashCode();
n++;
if (hashCodesSeen.ContainsKey(hashCode))
{
// Same hashCode seen twice for DIFFERENT objects (n is as low as 5322).
Console.WriteLine("Hashcode seen twice: " + n + " (" + hashCode + ")");
break;
}
hashCodesSeen.Add(hashCode, null);
}
Ich schreibe ein Debugging-Add-In und benötige eine ID für eine Referenz, die während der Programmausführung eindeutig ist.
Ich habe es bereits geschafft, eine interne ADRESSE der Instanz zu erhalten, die eindeutig ist, bis der Garbage Collector (GC) den Heap komprimiert (= verschiebt die Objekte = ändert die Adressen).
Frage zum Stapelüberlauf Die Standardimplementierung für Object.GetHashCode () ist möglicherweise verwandt.
Die Objekte sind nicht unter meiner Kontrolle, da ich über die Debugger-API auf Objekte in einem Programm zugreife, das debuggt wird. Wenn ich die Kontrolle über die Objekte hätte, wäre es trivial, meine eigenen eindeutigen Bezeichner hinzuzufügen.
Ich wollte die eindeutige ID zum Erstellen einer Hashtabellen-ID -> Objekt, um bereits gesehene Objekte nachschlagen zu können. Im Moment habe ich es so gelöst:
Build a hashtable: 'hashCode' -> (list of objects with hash code == 'hashCode')
Find if object seen(o) {
candidates = hashtable[o.GetHashCode()] // Objects with the same hashCode.
If no candidates, the object is new
If some candidates, compare their addresses to o.Address
If no address is equal (the hash code was just a coincidence) -> o is new
If some address equal, o already seen
}
quelle
Nur .NET 4 und höher
Gute Neuigkeiten alle zusammen!
Das perfekte Tool für diesen Job ist in .NET 4 erstellt und heißt
ConditionalWeakTable<TKey, TValue>
. Diese Klasse:quelle
ConditionalWeakTable
halber : verlässt sich aufRuntimeHelpers.GetHashCode
undobject.ReferenceEquals
tut sein Innenleben. Das Verhalten ist dasselbe wie beim Erstellen einesIEqualityComparer<T>
, das diese beiden Methoden verwendet. Wenn Sie Leistung benötigen, empfehle ich dies, daConditionalWeakTable
alle Vorgänge gesperrt sind, um die Thread-Sicherheit zu gewährleisten.ConditionalWeakTable
enthält einen Verweis auf jeden,Value
der nur so stark ist wie der Verweis auf den entsprechendenKey
. Ein Objekt, auf das aConditionalWeakTable
die einzige noch vorhandene Referenz im gesamten Universum enthält, hört automatisch auf zu existieren, wenn der Schlüssel dies tut.Die ObjectIDGenerator- Klasse ausgecheckt ? Dies macht das, was Sie versuchen und was Marc Gravell beschreibt.
quelle
RuntimeHelpers.GetHashCode()
kann helfen ( MSDN ).quelle
Sie können Ihr eigenes Ding in einer Sekunde entwickeln. Zum Beispiel:
Sie können selbst auswählen, was Sie als eindeutige ID haben möchten, z. B. System.Guid.NewGuid () oder einfach eine Ganzzahl für den schnellsten Zugriff.
quelle
Dispose
Fehler benötigen , da dies jede Art von Entsorgung verhindern würde.Wie wäre es mit dieser Methode:
Setzen Sie ein Feld im ersten Objekt auf einen neuen Wert. Wenn dasselbe Feld im zweiten Objekt denselben Wert hat, ist es wahrscheinlich dieselbe Instanz. Andernfalls beenden Sie als anders.
Setzen Sie nun das Feld im ersten Objekt auf einen anderen neuen Wert. Wenn dasselbe Feld im zweiten Objekt in einen anderen Wert geändert wurde, handelt es sich definitiv um dieselbe Instanz.
Vergessen Sie nicht, das Feld im ersten Objekt beim Beenden auf den ursprünglichen Wert zurückzusetzen.
Probleme?
quelle
In Visual Studio ist es möglich, eine eindeutige Objektkennung zu erstellen: Klicken Sie im Überwachungsfenster mit der rechten Maustaste auf die Objektvariable und wählen Sie im Kontextmenü die Option Objekt-ID erstellen.
Leider ist dies ein manueller Schritt, und ich glaube nicht, dass auf die Kennung über Code zugegriffen werden kann.
quelle
Sie müssten eine solche Kennung selbst manuell zuweisen - entweder innerhalb der Instanz oder extern.
Für Datensätze, die sich auf eine Datenbank beziehen, kann der Primärschlüssel hilfreich sein (Sie können jedoch weiterhin Duplikate erhalten). Alternativ können Sie entweder einen verwenden
Guid
oder Ihren eigenen Zähler behalten, indem Sie ihn zuweisenInterlocked.Increment
(und ihn so groß machen, dass er wahrscheinlich nicht überläuft).quelle
Ich weiß, dass dies beantwortet wurde, aber es ist zumindest nützlich zu beachten, dass Sie Folgendes verwenden können:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
Was Ihnen nicht direkt eine "eindeutige ID" gibt, sondern in Kombination mit WeakReferences (und einem Hashset?) Eine ziemlich einfache Möglichkeit bietet, verschiedene Instanzen zu verfolgen.
quelle
Die Informationen, die ich hier gebe, sind nicht neu. Der Vollständigkeit halber habe ich sie nur hinzugefügt.
Die Idee dieses Codes ist ganz einfach:
RuntimeHelpers.GetHashCode
, nämlich eine Art eindeutige ID zu erhaltenobject.ReferenceEquals
GUID
, die per Definition eindeutig ist.ConditionalWeakTable
.In Kombination erhalten Sie den folgenden Code:
Um es zu verwenden, erstellen Sie eine Instanz von
UniqueIdMapper
und verwenden Sie die zurückgegebenen GUIDs für die Objekte.Nachtrag
Hier ist also noch ein bisschen mehr los. Lass mich ein bisschen darüber schreiben
ConditionalWeakTable
.ConditionalWeakTable
macht ein paar Dinge. Das Wichtigste ist, dass der Garbage Collector keine Rolle spielt, dh, die Objekte, auf die Sie in dieser Tabelle verweisen, werden unabhängig davon erfasst. Wenn Sie ein Objekt nachschlagen, funktioniert es im Wesentlichen genauso wie das obige Wörterbuch.Neugierig nein? Wenn ein Objekt vom GC erfasst wird, prüft es schließlich, ob Verweise auf das Objekt vorhanden sind, und erfasst diese, falls vorhanden. Wenn es also ein Objekt aus dem gibt
ConditionalWeakTable
, warum wird das referenzierte Objekt dann gesammelt?ConditionalWeakTable
verwendet einen kleinen Trick, den auch einige andere .NET-Strukturen verwenden: Anstatt einen Verweis auf das Objekt zu speichern, wird tatsächlich ein IntPtr gespeichert. Da dies keine echte Referenz ist, kann das Objekt gesammelt werden.An dieser Stelle sind also zwei Probleme zu lösen. Erstens können Objekte auf dem Heap verschoben werden. Was werden wir also als IntPtr verwenden? Und zweitens, woher wissen wir, dass Objekte eine aktive Referenz haben?
DependentHandle
- aber ich glaube, es ist etwas ausgefeilter.DependentHandle
.Diese letzte Lösung erfordert, dass die Laufzeit die Listen-Buckets erst wiederverwendet, wenn sie explizit freigegeben wurden, und dass alle Objekte durch einen Aufruf der Laufzeit abgerufen werden müssen.
Wenn wir davon ausgehen, dass sie diese Lösung verwenden, können wir auch das zweite Problem angehen. Der Mark & Sweep-Algorithmus verfolgt, welche Objekte gesammelt wurden. Sobald es gesammelt wurde, wissen wir an dieser Stelle. Sobald das Objekt prüft, ob das Objekt vorhanden ist, ruft es 'Free' auf, wodurch der Zeiger und der Listeneintrag entfernt werden. Das Objekt ist wirklich weg.
Eine wichtige Sache, die an dieser Stelle zu beachten ist, ist, dass Dinge schrecklich schief gehen, wenn sie
ConditionalWeakTable
in mehreren Threads aktualisiert werden und wenn sie nicht threadsicher sind. Das Ergebnis wäre ein Speicherverlust. Aus diesem Grund führen alle eingehenden AnrufeConditionalWeakTable
eine einfache Sperre durch, die sicherstellt, dass dies nicht geschieht.Eine andere Sache zu beachten ist, dass das Bereinigen von Einträgen von Zeit zu Zeit erfolgen muss. Während die tatsächlichen Objekte vom GC bereinigt werden, sind dies die Einträge nicht. Deshalb
ConditionalWeakTable
wächst nur die Größe. Sobald es ein bestimmtes Limit erreicht (bestimmt durch die Kollisionswahrscheinlichkeit im Hash), löst es ein ausResize
, das prüft, ob Objekte bereinigt werden müssen - wenn dies der Fallfree
ist , wird es im GC-Prozess aufgerufen und dasIntPtr
Handle entfernt.Ich glaube, dies ist auch der Grund, warum
DependentHandle
nicht direkt belichtet wird - Sie möchten sich nicht mit Dingen anlegen und dadurch ein Speicherleck bekommen. Das nächstbeste dafür ist aWeakReference
(das auchIntPtr
ein Objekt anstelle eines Objekts speichert ) - enthält aber leider nicht den Aspekt 'Abhängigkeit'.Was bleibt, ist, dass Sie mit der Mechanik herumspielen, damit Sie die Abhängigkeit in Aktion sehen können. Starten Sie es mehrmals und sehen Sie sich die Ergebnisse an:
quelle
ConditionalWeakTable
könnte besser sein, da es die Darstellungen für Objekte nur beibehalten würde, wenn Verweise auf sie vorhanden wären. Außerdem würde ich vorschlagen, dass eineInt64
möglicherweise besser als eine GUID ist, da Objekte einen dauerhaften Rang erhalten könnten . Solche Dinge können in Sperrszenarien nützlich sein (z. B. kann man einen Deadlock vermeiden, wenn ein Code, der mehrere Sperren erhalten muss, dies in einer definierten Reihenfolge tut, aber damit dies funktioniert, muss es eine geben eine definierte Reihenfolge geben).long
s; es hängt von Ihrem Szenario ab - in f.ex. Verteilte Systeme Es ist manchmal nützlicher, mitGUID
s zu arbeiten . WasConditionalWeakTable
: du hast recht;DependentHandle
prüft auf Lebendigkeit (HINWEIS: nur wenn die Größe des Dings geändert wird!), was hier nützlich sein kann. Wenn Sie jedoch Leistung benötigen, kann das Sperren dort zu einem Problem werden. In diesem Fall könnte es interessant sein, dies zu verwenden. Um ehrlich zu sein, mag ich die Implementierung von persönlich nichtConditionalWeakTable
, was wahrscheinlich zu meiner Tendenz führt, ein einfachesDictionary
- sogar zu verwenden obwohl du richtig bist.ConditionalWeakTable
tatsächlich funktioniert. Die Tatsache, dass nur Elemente hinzugefügt werden können, lässt mich denken, dass es so konzipiert ist, dass der Aufwand für die Parallelität minimiert wird, aber ich habe keine Ahnung, wie es intern funktioniert. Ich finde es merkwürdig, dass es keinen einfachenDependentHandle
Wrapper gibt, der keine Tabelle verwendet, da es definitiv Zeiten gibt, in denen es wichtig ist, sicherzustellen, dass ein Objekt für die Lebensdauer eines anderen am Leben bleibt, aber das letztere Objekt keinen Platz für eine Referenz hat zum ersten.ConditionalWeakTable
erlaubt nicht, dass Einträge, die in der Tabelle gespeichert wurden, geändert werden. Als solches würde ich denken, dass es sicher unter Verwendung von Speicherbarrieren, aber nicht von Sperren implementiert werden könnte. Die einzige problematische Situation wäre, wenn zwei Threads gleichzeitig versuchen würden, denselben Schlüssel hinzuzufügen. Dies könnte behoben werden, indem die "add" -Methode nach dem Hinzufügen eines Elements eine Speicherbarriere durchführt und dann scannt, um sicherzustellen, dass genau ein Element diesen Schlüssel hat. Wenn mehrere Elemente denselben Schlüssel haben, kann eines als "erstes" identifiziert werden, sodass die anderen Elemente entfernt werden können.Wenn Sie ein Modul in Ihrem eigenen Code für eine bestimmte Verwendung schreiben, hat die Methode von Majkinetor möglicherweise funktioniert. Es gibt jedoch einige Probleme.
Erstens garantiert das offizielle Dokument NICHT , dass das Dokument
GetHashCode()
eine eindeutige Kennung zurückgibt (siehe Object.GetHashCode-Methode () ):Zweitens wird davon ausgegangen, dass Sie nur eine sehr kleine Anzahl von Objekten haben, sodass
GetHashCode()
diese Methode in den meisten Fällen von einigen Typen überschrieben werden kann.Zum Beispiel verwenden Sie eine Klasse C und diese überschreibt,
GetHashCode()
um immer 0 zurückzugeben. Dann erhält jedes Objekt von C den gleichen Hash-Code. LeiderDictionary
,HashTable
und einige andere assoziative Container wird diese Methode machen verwenden:Dieser Ansatz weist also große Einschränkungen auf.
Und noch mehr , was ist, wenn Sie eine Allzweckbibliothek erstellen möchten? Sie können nicht nur den Quellcode der verwendeten Klassen nicht ändern, sondern ihr Verhalten ist auch nicht vorhersehbar.
Ich schätze das Jon und Simon ihre Antworten veröffentlicht haben, und ich werde unten ein Codebeispiel und einen Vorschlag zur Leistung veröffentlichen.
In meinem Test wird der
ObjectIDGenerator
eine Ausnahme auslösen, um sich darüber zu beschweren, dass beim Erstellen von 10.000.000 Objekten (10x als im obigen Code) in derfor
Schleife zu viele Objekte vorhanden sind .Das Benchmark-Ergebnis ist außerdem, dass die
ConditionalWeakTable
Implementierung 1,8-mal schneller ist als dieObjectIDGenerator
Implementierung.quelle