Existe-t-il un moyen d'obtenir un identifiant unique d'une instance?
GetHashCode()
est le même pour les deux références pointant vers la même instance. Cependant, deux instances différentes peuvent (assez facilement) obtenir le même code de hachage:
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);
}
J'écris un addin de débogage, et j'ai besoin d'obtenir une sorte d'ID pour une référence qui est unique pendant l'exécution du programme.
J'ai déjà réussi à obtenir l'ADRESSE interne de l'instance, qui est unique jusqu'à ce que le garbage collector (GC) compacte le tas (= déplace les objets = change les adresses).
Question de débordement de pile L' implémentation par défaut pour Object.GetHashCode () peut être liée.
Les objets ne sont pas sous mon contrôle car j'accède aux objets dans un programme en cours de débogage à l'aide de l'API du débogueur. Si je contrôlais les objets, ajouter mes propres identifiants uniques serait trivial.
Je voulais l'ID unique pour créer un ID de table de hachage -> objet, pour pouvoir rechercher des objets déjà vus. Pour l'instant, je l'ai résolu comme ceci:
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
}
la source
.NET 4 et versions ultérieures uniquement
Bonnes nouvelles tout le monde!
L'outil parfait pour ce travail est intégré à .NET 4 et s'appelle
ConditionalWeakTable<TKey, TValue>
. Cette classe:la source
ConditionalWeakTable
s'appuie surRuntimeHelpers.GetHashCode
etobject.ReferenceEquals
pour faire son fonctionnement interne. Le comportement est le même que la création d'unIEqualityComparer<T>
qui utilise ces deux méthodes. Si vous avez besoin de performances, je suggère en fait de le faire, car ilConditionalWeakTable
a un verrou autour de toutes ses opérations pour le rendre sûr pour les threads.ConditionalWeakTable
détient une référence à chacunValue
qui n'est aussi forte que la référence détenue ailleurs au correspondantKey
. Un objet auquel aConditionalWeakTable
détient la seule référence existante n'importe où dans l'univers cessera automatiquement d'exister lorsque la clé le fera.Vous avez vérifié la classe ObjectIDGenerator ? Cela fait ce que vous essayez de faire et ce que décrit Marc Gravell.
la source
RuntimeHelpers.GetHashCode()
peut aider ( MSDN ).la source
Vous pouvez développer votre propre truc en une seconde. Par exemple:
Vous pouvez choisir ce que vous aimerez avoir comme identifiant unique par vous-même, par exemple, System.Guid.NewGuid () ou simplement un entier pour un accès plus rapide.
la source
Dispose
bogues, car cela empêcherait toute sorte d'élimination.Que diriez-vous de cette méthode:
Définissez un champ dans le premier objet sur une nouvelle valeur. Si le même champ dans le deuxième objet a la même valeur, c'est probablement la même instance. Sinon, quittez comme différent.
Définissez maintenant le champ du premier objet sur une nouvelle valeur différente. Si le même champ dans le deuxième objet a changé pour la valeur différente, c'est certainement la même instance.
N'oubliez pas de remettre le champ du premier objet à sa valeur d'origine à la sortie.
Problèmes?
la source
Il est possible de créer un identificateur d'objet unique dans Visual Studio: Dans la fenêtre de surveillance, cliquez avec le bouton droit sur la variable d'objet et choisissez Créer un ID d'objet dans le menu contextuel.
Malheureusement, il s'agit d'une étape manuelle et je ne pense pas que l'identifiant soit accessible via le code.
la source
Vous devrez attribuer un tel identifiant vous-même, manuellement - soit à l'intérieur de l'instance, soit en externe.
Pour les enregistrements liés à une base de données, la clé primaire peut être utile (mais vous pouvez toujours obtenir des doublons). Vous pouvez également utiliser un
Guid
ou conserver votre propre compteur, en allouant en utilisantInterlocked.Increment
(et en le rendant suffisamment grand pour qu'il ne déborde pas).la source
Je sais que cela a été répondu, mais il est au moins utile de noter que vous pouvez utiliser:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
Ce qui ne vous donnera pas directement un "identifiant unique", mais combiné avec WeakReferences (et un hashset?) Pourrait vous donner un moyen assez facile de suivre diverses instances.
la source
Les informations que je donne ici ne sont pas nouvelles, je viens de les ajouter par souci d'exhaustivité.
L'idée de ce code est assez simple:
RuntimeHelpers.GetHashCode
de nous obtenir une sorte d'identifiant uniqueobject.ReferenceEquals
GUID
, qui est par définition unique.ConditionalWeakTable
.Combiné, cela vous donnera le code suivant:
Pour l'utiliser, créez une instance de
UniqueIdMapper
et utilisez les GUID qu'il renvoie pour les objets.Addenda
Donc, il se passe un peu plus ici; laissez-moi écrire un peu plus
ConditionalWeakTable
.ConditionalWeakTable
fait plusieurs choses. Le plus important est qu'il ne se soucie pas du ramasse-miettes, c'est-à-dire que les objets que vous référencez dans ce tableau seront collectés malgré tout. Si vous recherchez un objet, il fonctionne essentiellement de la même manière que le dictionnaire ci-dessus.Curieux non? Après tout, lorsqu'un objet est collecté par le GC, il vérifie s'il existe des références à l'objet, et s'il y en a, il les collecte. Donc, s'il y a un objet de
ConditionalWeakTable
, pourquoi l'objet référencé sera-t-il alors collecté?ConditionalWeakTable
utilise une petite astuce, que d'autres structures .NET utilisent également: au lieu de stocker une référence à l'objet, elle stocke en fait un IntPtr. Parce que ce n'est pas une vraie référence, l'objet peut être collecté.Donc, à ce stade, il y a 2 problèmes à résoudre. Tout d'abord, les objets peuvent être déplacés sur le tas, alors qu'allons-nous utiliser comme IntPtr? Et deuxièmement, comment savons-nous que les objets ont une référence active?
DependentHandle
- mais je pense que c'est un peu plus sophistiqué.DependentHandle
.Cette dernière solution nécessite que le runtime ne réutilise pas les buckets de liste tant qu'ils ne sont pas explicitement libérés, et elle nécessite également que tous les objets soient récupérés par un appel au runtime.
Si nous supposons qu'ils utilisent cette solution, nous pouvons également résoudre le deuxième problème. L'algorithme Mark & Sweep garde la trace des objets qui ont été collectés; dès qu'il a été collecté, nous savons à ce stade. Une fois que l'objet vérifie si l'objet est là, il appelle «Free», ce qui supprime le pointeur et l'entrée de la liste. L'objet est vraiment parti.
Une chose importante à noter à ce stade est que les choses tournent terriblement mal si elle
ConditionalWeakTable
est mise à jour dans plusieurs threads et si elle n'est pas sûre pour les threads. Le résultat serait une fuite de mémoire. C'est pourquoi tous les appelsConditionalWeakTable
effectuent un simple «verrouillage» qui garantit que cela ne se produira pas.Une autre chose à noter est que le nettoyage des entrées doit avoir lieu de temps en temps. Alors que les objets réels seront nettoyés par le GC, les entrées ne le sont pas. C'est pourquoi
ConditionalWeakTable
ne grandit qu'en taille. Une fois qu'il atteint une certaine limite (déterminée par le risque de collision dans le hachage), il déclenche unResize
, qui vérifie si les objets doivent être nettoyés - s'ils le font,free
est appelé dans le processus GC, supprimant laIntPtr
poignée.Je crois que c'est aussi pourquoi
DependentHandle
n'est pas exposé directement - vous ne voulez pas gâcher les choses et obtenir une fuite de mémoire en conséquence. La meilleure chose suivante pour cela est aWeakReference
(qui stocke égalementIntPtr
un objet au lieu d'un objet) - mais n'inclut malheureusement pas l'aspect «dépendance».Il ne vous reste plus qu'à jouer avec les mécanismes, afin que vous puissiez voir la dépendance en action. Assurez-vous de le démarrer plusieurs fois et de regarder les résultats:
la source
ConditionalWeakTable
pourrait être mieux, car il ne ferait que conserver les représentations des objets tant que des références existaient à eux. En outre, je suggérerais qu'unInt64
pourrait être meilleur qu'un GUID, car il permettrait aux objets d'obtenir un rang persistant . De telles choses peuvent être utiles dans les scénarios de verrouillage (par exemple, on peut éviter les interblocages si un tout le code qui aura besoin d'acquérir plusieurs verrous le fait dans un ordre défini, mais pour que cela fonctionne, il doit y avoir un ordre défini).long
s; cela dépend de votre scénario - par exemple. systèmes distribués, il est parfois plus utile de travailler avecGUID
s. Quant àConditionalWeakTable
: vous avez raison;DependentHandle
vérifie la vitalité (NOTE: uniquement lorsque l'objet est redimensionné!), ce qui peut être utile ici. Pourtant, si vous avez besoin de performances, le verrouillage peut devenir un problème là-bas, donc dans ce cas, il pourrait être intéressant de l'utiliser ... pour être honnête, je n'aime pas personnellement la mise en œuvre deConditionalWeakTable
, ce qui conduit probablement à mon parti pris d'utiliser un simpleDictionary
- même si vous avez raison.ConditionalWeakTable
fonctionne réellement. Le fait qu'il autorise uniquement l'ajout d'éléments me fait penser qu'il est conçu pour minimiser les frais généraux liés à la concurrence, mais je n'ai aucune idée de son fonctionnement en interne. Je trouve curieux qu'il n'y ait pas de simpleDependentHandle
wrapper qui n'utilise pas de table, car il y a certainement des moments où il est important de s'assurer qu'un objet est maintenu en vie pour la durée de vie d'un autre, mais ce dernier objet n'a pas de place pour une référence au premier.ConditionalWeakTable
n'autorise pas la modification des entrées qui ont été stockées dans la table. En tant que tel, je pense qu'il pourrait être implémenté en toute sécurité en utilisant des barrières de mémoire mais pas des verrous. La seule situation problématique serait si deux threads essayaient d'ajouter la même clé simultanément; cela pourrait être résolu en demandant à la méthode «ajouter» d'effectuer une barrière de mémoire après l'ajout d'un élément, puis en effectuant une analyse pour s'assurer qu'exactement un élément possède cette clé. Si plusieurs éléments ont la même clé, l'un d'entre eux sera identifiable comme "premier", il sera donc possible d'éliminer les autres.Si vous écrivez un module dans votre propre code pour un usage spécifique, la méthode de majkinetor POURRAIT avoir fonctionné. Mais il y a quelques problèmes.
Premièrement , le document officiel ne garantit PAS que le
GetHashCode()
retourne un identifiant unique (voir Object.GetHashCode Method () ):Deuxièmement , supposons que vous ayez une très petite quantité d'objets pour que
GetHashCode()
cela fonctionne dans la plupart des cas, cette méthode peut être remplacée par certains types.Par exemple, vous utilisez une classe C et elle remplace
GetHashCode()
toujours 0. Ensuite, chaque objet de C obtiendra le même code de hachage. Malheureusement,Dictionary
,HashTable
et d'autres conteneurs associatifs feront utiliser cette méthode:Donc, cette approche a de grandes limites.
Et même plus , que se passe-t-il si vous souhaitez créer une bibliothèque à usage général? Non seulement vous ne pouvez pas modifier le code source des classes utilisées, mais leur comportement est également imprévisible.
J'apprécie que Jon et Simon aient publié leurs réponses, et je publierai un exemple de code et une suggestion sur les performances ci-dessous.
Dans mon test, le
ObjectIDGenerator
lancera une exception pour se plaindre qu'il y a trop d'objets lors de la création de 10 000 000 objets (10x que dans le code ci-dessus) dans lafor
boucle.En outre, le résultat de référence est que la
ConditionalWeakTable
mise en œuvre est 1,8 fois plus rapide que laObjectIDGenerator
mise en œuvre.la source