Frage

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.Allerdings können zwei verschiedene Instanzen (ganz einfach) 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 Art ID für eine Referenz, die während der Ausführung des Programms eindeutig ist.

Ich habe es bereits geschafft, die interne ADRESSE der Instanz abzurufen, die einzigartig ist, bis der Garbage Collector (GC) den Heap komprimiert (= die Objekte verschiebt = die Adressen ändert).

Frage zum Stapelüberlauf Standardimplementierung für Object.GetHashCode() könnte zusammenhängen.

Die Objekte unterliegen nicht meiner Kontrolle, da ich mithilfe der Debugger-API auf Objekte in einem Programm zugreife, das debuggt wird.Wenn ich die Kontrolle über die Objekte hätte, wäre das Hinzufügen meiner eigenen eindeutigen Identifikatoren trivial.

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
}
War es hilfreich?

Lösung

Der Verweis ist die eindeutige Kennung für das Objekt. Ich weiß nicht von irgendeiner Weise dies wie ein String in etwas umzuwandeln usw. Der Wert der Referenz wird während der Verdichtung ändern (wie Sie gesehen haben), aber jeder vorherigen Wert A wird auf den Wert B geändert werden, um so weit als sicher Code betroffen ist, es ist immer noch eine eindeutige Kennung.

Wenn die beteiligten Objekte unter Ihrer Kontrolle sind, können Sie eine Abbildung mit schwache Referenzen (der Garbage collection zu vermeiden verhindert) von einem Verweis auf eine ID Ihrer Wahl (GUID, integer, was auch immer). Das würde fügen Sie eine bestimmte Menge an Overhead und die Komplexität, aber.

Andere Tipps

.NET 4 und später nur

Eine gute Nachricht, jeder!

Das perfekte Werkzeug für diesen Job in .NET gebaut 4 und es heißt ConditionalWeakTable<TKey, TValue> . Diese Klasse:

  • kann verwendet werden, um beliebige Daten mit einem verwalteten Objektinstanz wie ein Wörterbuch zu verknüpfen (obwohl es ist kein Wörterbuch)
  • hängt nicht von Speicheradressen, ist so immun gegen die GC Verdichten des Heap
  • keine Objekte am Leben zu erhalten, nur weil sie als Schlüssel in die Tabelle eingetragen, so kann es, ohne dass jedes Objekt in Ihrem Prozess für immer lebt
  • verwendet werden
  • verwendet Referenz Gleichheit Objektidentität zu bestimmen; Moveover, Klasse Autoren können dieses Verhalten nicht ändern, so dass es verwendet werden kann, konsequent auf Objekte jeglicher Art
  • kann im laufenden Betrieb aufgefüllt werden, so ist es nicht erforderlich, dass Sie Code in Objektkonstruktoren
  • injizieren

die ObjectIDGenerator Klasse? Das tut, was Sie versuchen zu tun, und was Marc GRA beschreibt.

  

Der ObjectIDGenerator verfolgt zuvor identifizierten Objekte. Wenn Sie für die ID eines Objekts fragen, ob die ObjectIDGenerator kennt die bestehende ID zurückzukehren, oder zu generieren und eine neue ID erinnern.

     

Die IDs sind einzigartig für das Leben der ObjectIDGenerator Instanz. Im Allgemeinen dauert ein ObjectIDGenerator Leben, solange die Formatter, die es erstellt. Objekt-IDs haben nur innerhalb eines bestimmten serialisierten Streams Bedeutung und werden für die Verfolgung verwendet, die innerhalb des serialisierten Objekts Graph Verweise auf andere Objekte haben.

     

eine Hash-Tabelle verwendet, behält der ObjectIDGenerator die ID zugewiesen ist, an dem Objekt. Die Objektreferenzen, die eindeutig jedes Objekt identifizieren, werden Adressen in der Laufzeit Garbage Collected Heap. Objektreferenzwerte können während der Serialisierung ändern, aber die Tabelle wird automatisch aktualisiert, so dass die Informationen korrekt sind.

     

Objekt-IDs sind 64-Bit-Zahlen. Zuordnung geht von einem, so Null ist nie eine gültige Objekt-ID. Ein Formatierer kann einen Nullwert wählen, eine Objektreferenz, dessen Wert darzustellen ist ein NULL-Verweis (Nothing in Visual Basic).

RuntimeHelpers.GetHashCode() kann helfen ( MSDN ).

Sie können Ihre eigene Sache, in einem zweiten entwickeln. Zum Beispiel:

   class Program
    {
        static void Main(string[] args)
        {
            var a = new object();
            var b = new object();
            Console.WriteLine("", a.GetId(), b.GetId());
        }
    }

    public static class MyExtensions
    {
        //this dictionary should use weak key references
        static Dictionary<object, int> d = new Dictionary<object,int>();
        static int gid = 0;

        public static int GetId(this object o)
        {
            if (d.ContainsKey(o)) return d[o];
            return d[o] = gid++;
        }
    }   

Sie können wählen, was Sie als eindeutige ID auf eigene Faust, zum Beispiel haben mögen, System.Guid.NewGuid () oder einfach integer für schnellsten Zugriff.

Wie über diese Methode:

Stellen Sie ein Feld in dem ersten Objekt auf einen neuen Wert. Wenn das gleiche Feld in dem zweiten Objekt den gleichen Wert hat, ist es wahrscheinlich die gleiche Instanz. Ansonsten verlassen als andere.

Sie nun das Feld in dem ersten Objekt zu einem anderen neuen Wert gesetzt. Wenn das gleiche Feld in dem zweiten Objekt hat auf den anderen Wert geändert, es ist auf jeden Fall die gleiche Instanz.

Vergessen Sie nicht, Feld in dem ersten Objekt zu setzen zurück, um es an der Ausfahrt ursprünglicher Wert.

Probleme?

Es ist möglich, eine eindeutige Objektkennung in Visual Studio zu machen. In dem Uhrenfenster mit dem rechten Maustaste auf die Objektvariable, und wählen Sie Objekt mit ID aus dem Kontextmenü

Leider ist dies ein manueller Schritt, und ich glaube nicht, die Kennung kann über Code zugegriffen werden.

würden Sie haben sich eine solche Kennung zuweisen, manuell -. Entweder innerhalb der Instanz, oder extern

zu einer Datenbank verknüpften Datensätze kann der Primärschlüssel nützlich sein (aber man kann immer noch Duplikate erhalten). Alternativ kann entweder eine Guid verwenden oder eigene Zähler halten, Aufteilung Interlocked.Increment mit (und macht es groß genug, dass es nicht wahrscheinlich überlaufen ist).

Ich weiß, dass diese beantwortet wurde, aber es ist zumindest sinnvoll zu beachten, dass Sie verwenden können:

http://msdn.microsoft.com/en- us / library / system.object.referenceequals.aspx

Was dir nicht eine „eindeutige ID“ direkt geben, aber mit WeakReferences kombiniert (und einem Hashset?) Könnten Sie eine ziemlich einfache Möglichkeit, verschiedene Instanzen zu verfolgen.

Die Informationen, die ich hier gebe, sind nicht neu, ich habe sie nur der Vollständigkeit halber hinzugefügt.

Die Idee dieses Codes ist ganz einfach:

  • Objekte benötigen eine eindeutige ID, die standardmäßig nicht vorhanden ist.Stattdessen müssen wir uns auf das Nächstbeste verlassen, nämlich RuntimeHelpers.GetHashCode um uns eine Art eindeutige ID zu verschaffen
  • Um die Einzigartigkeit zu überprüfen, müssen wir Folgendes verwenden object.ReferenceEquals
  • Wir möchten jedoch immer noch eine eindeutige ID haben, also habe ich eine hinzugefügt GUID, was per Definition eindeutig ist.
  • Da es mir nicht gefällt, alles abzusperren, wenn ich es nicht muss, nutze ich es nicht ConditionalWeakTable.

Zusammengenommen ergibt das den folgenden Code:

public class UniqueIdMapper
{
    private class ObjectEqualityComparer : IEqualityComparer<object>
    {
        public bool Equals(object x, object y)
        {
            return object.ReferenceEquals(x, y);
        }

        public int GetHashCode(object obj)
        {
            return RuntimeHelpers.GetHashCode(obj);
        }
    }

    private Dictionary<object, Guid> dict = new Dictionary<object, Guid>(new ObjectEqualityComparer());
    public Guid GetUniqueId(object o)
    {
        Guid id;
        if (!dict.TryGetValue(o, out id))
        {
            id = Guid.NewGuid();
            dict.Add(o, id);
        }
        return id;
    }
}

Um es zu verwenden, erstellen Sie eine Instanz von UniqueIdMapper und verwenden Sie die GUIDs, die für die Objekte zurückgegeben werden.


Nachtrag

Hier ist also noch ein bisschen mehr los;Lassen Sie mich etwas darüber schreiben ConditionalWeakTable.

ConditionalWeakTable macht ein paar Dinge.Das Wichtigste ist, dass es sich nicht um den Garbage Collector kümmert, das heißt:Die Objekte, auf die Sie in dieser Tabelle verweisen, werden unabhängig davon erfasst.Wenn Sie ein Objekt nachschlagen, funktioniert es im Grunde genauso wie das obige Wörterbuch.

Neugierig, nein?Wenn ein Objekt vom GC gesammelt wird, prüft er schließlich, ob Verweise auf das Objekt vorhanden sind, und wenn ja, sammelt er diese.Wenn es also ein Objekt aus dem gibt ConditionalWeakTable, warum wird das referenzierte Objekt dann gesammelt?

ConditionalWeakTable nutzt einen kleinen Trick, den auch einige andere .NET-Strukturen nutzen:Anstatt einen Verweis auf das Objekt zu speichern, speichert es tatsächlich einen IntPtr.Da es sich dabei nicht um eine echte Referenz handelt, kann das Objekt eingesammelt werden.

An diesem Punkt müssen also zwei Probleme gelöst werden.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?

  • Das Objekt kann auf dem Heap angeheftet und sein tatsächlicher Zeiger gespeichert werden.Wenn der GC auf das zu entfernende Objekt trifft, löst es die Fixierung und sammelt es ein.Das würde jedoch bedeuten, dass wir eine angeheftete Ressource erhalten, was keine gute Idee ist, wenn Sie viele Objekte haben (aufgrund von Problemen mit der Speicherfragmentierung).So funktioniert es wahrscheinlich nicht.
  • Wenn der GC ein Objekt verschiebt, ruft er zurück, wodurch die Referenzen aktualisiert werden können.Den externen Aufrufen nach zu urteilen, könnte es so implementiert sein DependentHandle - aber ich glaube, es ist etwas anspruchsvoller.
  • Es wird nicht der Zeiger auf das Objekt selbst, sondern ein Zeiger in der Liste aller Objekte aus dem GC gespeichert.Der IntPtr ist entweder ein Index oder ein Zeiger in dieser Liste.Die Liste ändert sich nur, wenn ein Objekt die Generation wechselt. An diesem Punkt kann ein einfacher Rückruf die Zeiger aktualisieren.Wenn Sie sich erinnern, wie Mark & ​​Sweep funktioniert, ist dies sinnvoller.Es gibt kein Fixieren und das Entfernen erfolgt wie zuvor.Ich glaube, dass es so funktioniert DependentHandle.

Diese letzte Lösung erfordert, dass die Laufzeit die Listen-Buckets nicht wiederverwendet, bis sie explizit freigegeben werden, und erfordert außerdem, dass alle Objekte durch einen Aufruf der Laufzeit abgerufen werden.

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 eingesammelt wurde, wissen wir zu diesem Zeitpunkt Bescheid.Sobald das Objekt prüft, ob das Objekt vorhanden ist, ruft es „Frei“ auf, wodurch der Zeiger und der Listeneintrag entfernt werden.Das Objekt ist wirklich weg.

An dieser Stelle ist es wichtig zu beachten, dass die Dinge furchtbar schief gehen, wenn ConditionalWeakTable in mehreren Threads aktualisiert wird und wenn es nicht threadsicher ist.Die Folge wäre ein Speicherverlust.Aus diesem Grund rufen alle an ConditionalWeakTable Führen Sie eine einfache „Sperre“ durch, um sicherzustellen, dass dies nicht geschieht.

Beachten Sie außerdem, dass die Bereinigung von Einträgen hin und wieder erfolgen muss.Während die eigentlichen Objekte vom GC bereinigt werden, ist dies bei den Einträgen nicht der Fall.Deshalb ConditionalWeakTable wächst nur an Größe.Sobald es einen bestimmten Grenzwert erreicht (bestimmt durch die Kollisionswahrscheinlichkeit im Hash), löst es einen aus Resize, das prüft, ob Objekte bereinigt werden müssen – wenn ja, free wird im GC-Prozess aufgerufen und entfernt die IntPtr handhaben.

Ich glaube, das ist auch der Grund DependentHandle wird nicht direkt offengelegt – Sie möchten nicht mit Dingen herumspielen und dadurch einen Speicherverlust erleiden.Das nächstbeste dafür ist a WeakReference (in dem auch eine gespeichert wird IntPtr anstelle eines Objekts) - enthält aber leider nicht den Aspekt der Abhängigkeit.

Was bleibt, ist, dass Sie mit der Mechanik herumspielen, damit Sie die Abhängigkeit in Aktion sehen können.Starten Sie es unbedingt mehrmals und beobachten Sie die Ergebnisse:

class DependentObject
{
    public class MyKey : IDisposable
    {
        public MyKey(bool iskey)
        {
            this.iskey = iskey;
        }

        private bool disposed = false;
        private bool iskey;

        public void Dispose()
        {
            if (!disposed)
            {
                disposed = true;
                Console.WriteLine("Cleanup {0}", iskey);
            }
        }

        ~MyKey()
        {
            Dispose();
        }
    }

    static void Main(string[] args)
    {
        var dep = new MyKey(true); // also try passing this to cwt.Add

        ConditionalWeakTable<MyKey, MyKey> cwt = new ConditionalWeakTable<MyKey, MyKey>();
        cwt.Add(new MyKey(true), dep); // try doing this 5 times f.ex.

        GC.Collect(GC.MaxGeneration);
        GC.WaitForFullGCComplete();

        Console.WriteLine("Wait");
        Console.ReadLine(); // Put a breakpoint here and inspect cwt to see that the IntPtr is still there
    }

Wenn Sie ein Modul in Ihrem eigenen Code für eine bestimmte Nutzung schreiben, majkinetor Methode MACHT gearbeitet haben. Aber es gibt einige Probleme.

Erste , das offizielle Dokument hat nicht Garantie, dass die GetHashCode() eine eindeutige Kennung zurückgibt (siehe Object.GetHashCode-Methode () ):

  

Sie sollten nicht davon ausgehen, dass gleichen Hash-Codes Objekt Gleichheit bedeuten.

Zweite , vorausgesetzt, dass Sie eine sehr kleine Menge von Objekten, so dass GetHashCode() in den meisten Fällen funktionieren wird, kann diese Methode von einigen Arten außer Kraft gesetzt werden.
Zum Beispiel verwenden Sie eine Klasse C und es überschreibt GetHashCode() immer 0 zurück jedes Objekt von C Dann wird den gleichen Hash-Code erhalten. Leider Dictionary, HashTable und einige andere assoziative Container wird die Verwendung dieser Methode machen:

  

Ein Hash-Code ist ein numerischer Wert, der verwendet wird, einzusetzen und ein Objekt in einer Hash-basierten Sammlung wie der Dictionary Klasse, die Hashtable Klasse oder einen Typ aus der Klasse abgeleitet Dictionary zu identifizieren. Die GetHashCode-Methode stellt diesen Hash-Code für Algorithmen, die die schnelle Kontrolle von Objektgleichheit müssen.

So hat dieser Ansatz große Einschränkungen.

und noch , was ist, wenn Sie einen Mehrzweckbibliothek aufbauen wollen? Nicht nur, dass Sie nicht in der Lage, den Quellcode der verwendeten Klassen zu ändern, aber ihr Verhalten ist auch unberechenbar.

Ich schätze, dass Jon und

scroll top