.NET identificateur d'objet unique
Est-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 cas 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);
}
Je suis en train d'écrire un débogage addin, et j'ai besoin d'obtenir une sorte de ID pour une référence qui est unique au cours de l'exécution du programme.
J'ai déjà réussi à obtenir l'ADRESSE interne de l'instance, ce qui est unique jusqu'à ce que le garbage collector (GC) compacte le tas (= déplace les objets = changements d'adresses).
Pile Overflow question L'implémentation par défaut de l'Objet.GetHashCode() pourrait être liée.
Les objets ne sont pas sous mon contrôle que je suis l'accès aux objets dans un programme en cours de débogage à l'aide de l'API de débogueur. Si j'étais en possession des objets, en ajoutant mes propres identifiants uniques serait trivial.
Je voulais l'ID unique pour la construction d'une table de hachage ID -> objet, pour être en mesure de recherche déjà vu des objets. 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
}
Vous devez vous connecter pour publier un commentaire.
La référence est l'identifiant unique de l'objet. Je ne sais pas de toute façon de convertir ce en quoi que ce soit comme une chaîne de caractères etc. La valeur de la référence va changer au cours de la compaction du sol (comme vous l'avez vu), mais à chaque valeur précédente sera changé à la valeur de B, donc autant que coffre-fort à code, il est encore un ID unique.
Si les objets impliqués sont sous votre contrôle, vous pouvez créer une cartographie à l'aide de références faibles (pour éviter de prévention de la collecte des ordures) à partir d'une référence à un ID de votre choix (GUID, entier, peu importe). Que serait ajouter un certain montant de frais généraux et de la complexité, cependant.
ceq
IL. Je soupçonne ce genre de subtils problème n'est pas ce que Slipp parlait bien. Personnellement, je tiens à garder au moins le plus simple conceptuelle modèle, même si intelligente choses se passe derrière les coulisses 🙂.NET 4 et plus tard seulement
Bonne nouvelle, tout le monde!
L'outil parfait pour ce travail est construit en .NET 4 et il est appelé
ConditionalWeakTable<TKey, TValue>
. Cette classe:ConditionalWeakTable
s'appuie surRuntimeHelpers.GetHashCode
etobject.ReferenceEquals
à faire de 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, en fait, je suggère pour ce faire, depuisConditionalWeakTable
a un verrou autour de l'ensemble de ses opérations à faire thread-safe.ConditionalWeakTable
contient une référence à chaqueValue
qui est aussi forte que la référence a tenu d'ailleurs à l'correspondantKey
. Un objet dont l'unConditionalWeakTable
détient la seule existante de référence de n'importe où dans l'univers cessera automatiquement d'exister lorsque la clé n'est.Vérifié les ObjectIDGenerator classe? Ce n'est ce que tu essaies de faire, et ce que Marc Gravel décrit.
RuntimeHelpers.GetHashCode()
peut aider (MSDN).Vous pouvez développer votre propre chose en une seconde. Par exemple:
Vous pouvez choisir ce que vous souhaitez avoir comme IDENTIFIANT unique de votre propre, par exemple, du Système.Guid.NewGuid() ou tout simplement en entier pour un accès plus rapide.
Dispose
bugs, parce que cela permettrait d'éviter tout type de disposition.Comment de cette méthode:
Définir un champ dans le premier objet à une nouvelle valeur. Si le même champ dans la deuxième objet a la même valeur, c'est probablement la même instance. Sinon, la sortie comme différents.
Maintenant définir le champ dans le premier objet à une autre nouvelle valeur. Si le même champ dans la deuxième objet a changé à la valeur différente, c'est certainement la même instance.
N'oubliez pas de définir le champ dans le premier objet revenir à la valeur initiale à la sortie.
Problèmes?
Il est possible de faire un unique identificateur d'objet dans Visual Studio: Dans la fenêtre, cliquez-droit sur la variable d'objet et choisissez Faire l'Objet d'ID dans le menu contextuel.
Malheureusement, c'est une étape manuelle, et je ne crois pas que l'identifiant peut être consulté via le code.
Vous devez attribuer un tel identificateur de vous-même, manuellement, soit à l'intérieur de l'instance ou à l'extérieur.
Pour les dossiers liés à une base de données, la clé primaire peut être utile (mais vous pouvez toujours retrouver avec des doublons). Sinon, vous pouvez soit utiliser un
Guid
, ou de garder votre propre compteur, l'allocation de l'aideInterlocked.Increment
(et de le rendre assez grande qu'il n'est pas susceptible de déborder).Je sais que cette question a été répondu, mais c'est au moins utile de noter que vous pouvez utiliser:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
Qui ne vous donnera pas un "identifiant unique" directement, mais combiné avec WeakReferences (et un hashset?) pourrais vous donner d'une manière assez facile le suivi des diverses instances.
Les informations que je donne ici n'est pas nouveau, je viens d'ajouter ce pour des raisons d'exhaustivité.
L'idée de ce code est assez simple:
RuntimeHelpers.GetHashCode
pour nous une sorte d'IDENTIFIANT uniqueobject.ReferenceEquals
GUID
, qui est par définition unique.ConditionalWeakTable
.Combiné, qui vous donnera le code suivant:
Pour l'utiliser, créez une instance de la
UniqueIdMapper
et utiliser le GUID est qu'elle renvoie pour les objets.Additif
Donc, il y a un peu plus de choses ici; laissez-moi écrire un peu vers le bas sur
ConditionalWeakTable
.ConditionalWeakTable
fait un certain nombre de choses. La chose la plus importante est qu'il se complique pas de soins sur le garbage collector, c'est: les objets que vous avez de référence dans ce tableau seront recueillies indépendamment. Si vous recherche un objet, il fonctionne de la même manière que le dictionnaire ci-dessus.Curieux non? Après tout, quand un objet est en train d'être recueillies par le GC, il vérifie si il y a des références à l'objet, et si il y a, il les collectionne. Donc si il y a un objet à partir de la
ConditionalWeakTable
, pourquoi l'objet référencé être collectées, puis?ConditionalWeakTable
utilise une petite astuce que certains autres .NET des structures aussi utiliser: au lieu de stocker une référence à l'objet, il stocke en fait un IntPtr. Parce que ce n'est pas une véritable référence, l'objet peut être recueilli.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, de quoi allons-nous utiliser comme IntPtr? Et deuxièmement, comment savons-nous que les objets ont un actif de référence?
DependentHandle
- mais je crois que c'est un peu plus sophistiqué.DependentHandle
.Cette dernière solution nécessite que l'exécution n'a pas ré-utiliser la liste des seaux jusqu'à ce qu'ils sont explicitement libéré, et il exige aussi que tous les objets sont récupérés par un appel à l'exécution.
Si nous supposons qu'ils utilisent cette solution, nous pouvons également aborder le deuxième problème. La Marque & algorithme de Balayage garde la trace de ces objets ont été recueillis; dès qu'il a été recueilli, nous savons à ce stade. Une fois l'objet de vérifier si l'objet est là, qu'il appelle "Libre", ce qui supprime le pointeur et l'entrée de la liste. L'objet est vraiment passé.
Une chose importante à noter à ce point, c'est que les choses tournent mal si
ConditionalWeakTable
est mis à jour dans plusieurs threads et si elle n'est pas thread-safe. Le résultat serait une fuite de mémoire. C'est pourquoi tous les appels enConditionalWeakTable
faire un simple "lock" qui assure que cela n'arrive pas.Une autre chose à noter est que le nettoyage des entrées de a à se produire de temps en temps. Alors que les objets réels seront nettoyés par le GC, les entrées ne sont pas. C'est pourquoi
ConditionalWeakTable
ne pousse que dans la taille. Une fois qu'il atteint une certaine limite (déterminé par la collision de chance dans la table de hachage), il déclenche uneResize
, qui vérifie si les objets doivent être nettoyés -- s'ils le font,free
est appelé dans la GC processus de retrait de laIntPtr
poignée.Je crois que c'est aussi pourquoi
DependentHandle
n'est pas exposé directement - vous ne voulez pas salir avec les choses et une fuite de mémoire comme un résultat. La prochaine meilleure chose est que uneWeakReference
(qui stocke également uneIntPtr
au lieu d'un objet), mais, malheureusement, ne comprennent pas la "dépendance" aspect.Ce qui reste est pour vous de jouet autour de la mécanique, de sorte que vous pouvez voir la dépendance dans l'action. Assurez-vous de commencer plusieurs fois et de regarder les résultats:
ConditionalWeakTable
peut-être mieux, car il ne serait que de persister les représentations des objets alors que les références existé pour eux. Aussi, je suggère qu'uneInt64
peut-être mieux qu'un GUID, car cela permettrait d'objets pour bénéficier d'un persistants rang. De telles choses peuvent être utiles dans le verrouillage de scénarios (par exemple, on peut éviter un blocage si un seul code, qui ont besoin d'acquérir de multiples verrous fait dans un ordre défini, mais pour que cela fonctionne, il doit y est un ordre défini).long
s; il dépend de votre scénario - dans f.ex. systèmes distribués, il est parfois plus utile de travailler avecGUID
s. Comme pourConditionalWeakTable
: vous avez raison;DependentHandle
vérifie la vitalité (NOTE: uniquement lorsque la chose redimensionne!), ce qui peut être utile ici. Encore, si vous avez besoin de performances de verrouillage peut devenir un problème il y a, donc, dans ce cas, il peut être intéressant d'utiliser cette... pour être honnête, personnellement, je n'aime pas la mise en œuvre deConditionalWeakTable
, ce qui conduit sans doute à mon biais de l'aide d'un simpleDictionary
- même si vous êtes correct.ConditionalWeakTable
fonctionne réellement. Le fait qu'elle permet uniquement d'éléments pour être ajoutés me fait penser qu'il est conçu pour minimiser la simultanéité des frais généraux liés, mais je n'ai aucune idée de comment ça fonctionne en interne. Je trouve curieux qu'il n'y a pas de simpleDependentHandle
wrapper qui permet de ne pas utiliser une table, car il ya certainement des moments où il est important de s'assurer qu'un objet est maintenue pour la durée de vie de l'autre, mais cet objet n'a pas de place pour une référence à la première.ConditionalWeakTable
ne permet pas les entrées qui ont été enregistrées dans la table à modifier. En tant que tel, je pense qu'il pourrait être mis en œuvre en toute sécurité à l'aide de barrières de mémoire, mais pas de serrures. Le seul problème serait le cas si deux threads essayé d'ajouter la même clé, en même temps, qui pourrait être résolu par l' "ajouter" méthode effectuer une barrière de mémoire après un article est ajouté, puis la numérisation afin de s'assurer qu'un élément a de cette clé. Si plusieurs éléments ont la même clé, l'un d'entre eux va être identifiables en tant que "première", de sorte qu'il sera possible d'éliminer les autres.ConcurrentDictionary
ou utilisé une valeur de hachage avec une liste chaînée de seaux et d'ajouter de nouvelles entrées à l'aide deInterlocked.Exchange
(pour n'en citer que deux possibilités). Seules les opérations de redimensionnement sur la liste de seau a besoin d'une serrure. Le point que je n'aime pas leConditionalWeakTable
a beaucoup à voir avec leur hachage de la mise en œuvre, même si je peux comprendre pourquoi ils ont fait ça comme ça. Encore, nous sommes à la dérive... est-ce à vous donner quelques indications sur votre question sur la façonConditionalWeakTable
œuvres?free
appels de la ephemeron poignées; le GC prend soin de l'objet de la collecte des ordures (généralement avant les poignées sont libérés puisque cela ne se produit qu'au cours de la redimensionner des appels). Je pense que les poignées sont en "épinglé" (en fait, je pense qu'ils sont des pointeurs dans certains exécution de table comme le GC); les objets eux-mêmes n'ont pas à être épinglé. La gratuit des appels eux-mêmes peuvent être trouvés dansDependentHandle
btw et sont appelées lors de la redimensionner phase.Si vous écrivez un module dans votre propre code pour un usage spécifique, majkinetor de la méthode POURRAIT ont travaillé. Mais il y a quelques problèmes.
Première, le document officiel ne PAS garantir que
GetHashCode()
retourne un identifiant unique (voir Objet.Méthode GetHashCode ()):Deuxième, supposons que vous disposez d'une très petite quantité d'objets, de sorte que
GetHashCode()
fonctionne dans la plupart des cas, cette méthode peut être remplacée par certains types.Par exemple, vous utilisez un peu de classe C et il remplace
GetHashCode()
retourne toujours 0. Ensuite, chaque objet de C obtiendrez le même code de hachage.Malheureusement,
Dictionary
,HashTable
et quelques autres conteneurs associatifs fera usage de cette méthode:Donc, cette approche a de grandes limites.
Et encore plus, si vous voulez construire une bibliothèque d'intérêt général?
Non seulement vous serez en mesure de modifier le code source des classes utilisées, mais leur comportement est imprévisible.
J'apprécie que Jon et Simon ont posté leurs réponses, et je vais poster un exemple de code et une suggestion sur la performance ci-dessous.
Dans mon test, le
ObjectIDGenerator
lèvera une exception pour se plaindre qu'il y a trop d'objets lors de la création de 10 000 000 d'objets (10x plus que dans le code ci-dessus) dans lefor
boucle.Aussi, le résultat du benchmark est que le
ConditionalWeakTable
mise en œuvre est de 1,8 x plus rapide que laObjectIDGenerator
mise en œuvre.