Existe uma maneira de obter um identificador exclusivo de uma instância?
GetHashCode()
é o mesmo para as duas referências que apontam para a mesma instância. No entanto, duas instâncias diferentes podem (facilmente) obter o mesmo código hash:
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);
}
Estou escrevendo um addin de depuração e preciso obter algum tipo de ID para uma referência que seja única durante a execução do programa.
Já consegui o ADDRESS interno da instância, que é único até que o garbage collector (GC) compacta o heap (= move os objetos = muda os endereços).
Pergunta sobre Stack Overflow A implementação padrão para Object.GetHashCode () pode estar relacionada.
Os objetos não estão sob meu controle, pois estou acessando objetos em um programa que está sendo depurado usando a API do depurador. Se eu estivesse no controle dos objetos, adicionar meus próprios identificadores exclusivos seria trivial.
Eu queria o ID exclusivo para construir um ID hashtable -> objeto, para poder pesquisar objetos já vistos. Por enquanto eu resolvi assim:
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
}
fonte
.NET 4 e posterior apenas
Boas notícias, pessoal!
A ferramenta perfeita para esse trabalho é construída em .NET 4 e é chamada
ConditionalWeakTable<TKey, TValue>
. Esta aula:fonte
ConditionalWeakTable
confia emRuntimeHelpers.GetHashCode
eobject.ReferenceEquals
para fazer seu funcionamento interno. O comportamento é o mesmo que construir umIEqualityComparer<T>
que use esses dois métodos. Se você precisa de desempenho, eu realmente sugiro fazer isso, já queConditionalWeakTable
tem um bloqueio em torno de todas as suas operações para torná-lo seguro para thread.ConditionalWeakTable
contém uma referência a cada umValue
que é tão forte quanto a referência mantida em outro lugar para o correspondenteKey
. Um objeto para o qual aConditionalWeakTable
contém a única referência existente em qualquer lugar do universo deixará de existir automaticamente quando a chave o fizer.Check-out do ObjectIDGenerator classe? Isso faz o que você está tentando fazer e o que Marc Gravell descreve.
fonte
RuntimeHelpers.GetHashCode()
pode ajudar ( MSDN ).fonte
Você pode desenvolver suas próprias coisas em um segundo. Por exemplo:
Você pode escolher o que deseja ter como ID exclusivo por conta própria, por exemplo, System.Guid.NewGuid () ou simplesmente inteiro para acesso mais rápido.
fonte
Dispose
bugs, pois evitaria qualquer tipo de descarte.Que tal este método:
Defina um campo no primeiro objeto com um novo valor. Se o mesmo campo no segundo objeto tiver o mesmo valor, provavelmente é a mesma instância. Caso contrário, saia como diferente.
Agora defina o campo no primeiro objeto com um novo valor diferente. Se o mesmo campo no segundo objeto mudou para um valor diferente, é definitivamente a mesma instância.
Não se esqueça de definir o campo no primeiro objeto de volta ao seu valor original ao sair.
Problemas?
fonte
É possível criar um identificador de objeto exclusivo no Visual Studio: Na janela de observação, clique com o botão direito do mouse na variável do objeto e escolha Criar ID do objeto no menu de contexto.
Infelizmente, esta é uma etapa manual e não acredito que o identificador possa ser acessado via código.
fonte
Você mesmo teria que atribuir tal identificador, manualmente - seja dentro da instância ou externamente.
Para registros relacionados a um banco de dados, a chave primária pode ser útil (mas você ainda pode obter duplicatas). Alternativamente, use a
Guid
ou mantenha seu próprio contador, alocando usandoInterlocked.Increment
(e torne-o grande o suficiente para que não transborde).fonte
Eu sei que isso foi respondido, mas é pelo menos útil observar que você pode usar:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
O que não dará a você um "id exclusivo" diretamente, mas combinado com WeakReferences (e um hashset?) Pode fornecer uma maneira muito fácil de rastrear várias instâncias.
fonte
As informações que dou aqui não são novas, apenas adicionei para completar.
A ideia deste código é bastante simples:
RuntimeHelpers.GetHashCode
nos fornecer uma espécie de ID exclusivoobject.ReferenceEquals
GUID
, que por definição é exclusivo.ConditionalWeakTable
.Combinado, isso fornecerá o seguinte código:
Para usá-lo, crie uma instância de
UniqueIdMapper
e use os GUIDs que retorna para os objetos.Termo aditivo
Então, há um pouco mais acontecendo aqui; deixe-me escrever um pouco sobre
ConditionalWeakTable
.ConditionalWeakTable
faz algumas coisas. O mais importante é que não se preocupe com o coletor de lixo, ou seja: os objetos que você referenciar nesta tabela serão coletados de qualquer maneira. Se você pesquisar um objeto, ele basicamente funcionará da mesma forma que o dicionário acima.Curioso, não? Afinal, quando um objeto está sendo coletado pelo CG, ele verifica se há referências ao objeto e, se houver, as coleta. Portanto, se houver um objeto do
ConditionalWeakTable
, por que o objeto referenciado será coletado?ConditionalWeakTable
usa um pequeno truque, que algumas outras estruturas .NET também usam: em vez de armazenar uma referência ao objeto, ele na verdade armazena um IntPtr. Como essa não é uma referência real, o objeto pode ser coletado.Portanto, neste ponto, há 2 problemas a serem resolvidos. Primeiro, os objetos podem ser movidos no heap, então o que usaremos como IntPtr? E em segundo lugar, como sabemos que os objetos têm uma referência ativa?
DependentHandle
- mas acredito que seja um pouco mais sofisticado.DependentHandle
.Esta última solução exige que o tempo de execução não reutilize os depósitos de lista até que eles sejam explicitamente liberados e também exige que todos os objetos sejam recuperados por uma chamada para o tempo de execução.
Se presumirmos que eles usam essa solução, também podemos resolver o segundo problema. O algoritmo Mark & Sweep mantém registro de quais objetos foram coletados; assim que for coletado, sabemos neste ponto. Depois que o objeto verifica se o objeto está lá, ele chama 'Livre', que remove o ponteiro e a entrada da lista. O objeto realmente se foi.
Uma coisa importante a se observar neste ponto é que as coisas dão terrivelmente errado se
ConditionalWeakTable
for atualizado em vários threads e não for seguro para threads. O resultado seria um vazamento de memória. É por isso que todas as chamadasConditionalWeakTable
fazem um simples 'bloqueio' que garante que isso não aconteça.Outra coisa a observar é que a limpeza das entradas deve acontecer de vez em quando. Embora os objetos reais sejam limpos pelo GC, as entradas não são. É por isso que
ConditionalWeakTable
apenas cresce em tamanho. Uma vez que atinge um certo limite (determinado pela chance de colisão no hash), ele aciona umResize
, que verifica se os objetos precisam ser limpos - se o fizerem,free
é chamado no processo de GC, removendo oIntPtr
identificador.Eu acredito que é também por isso que
DependentHandle
não é exposto diretamente - você não quer bagunçar as coisas e ter um vazamento de memória como resultado. A próxima melhor coisa para isso é umWeakReference
(que também armazena um emIntPtr
vez de um objeto) - mas infelizmente não inclui o aspecto de 'dependência'.O que resta é você brincar com a mecânica, para poder ver a dependência em ação. Certifique-se de iniciá-lo várias vezes e observar os resultados:
fonte
ConditionalWeakTable
poderia ser melhor, já que só persistiria as representações de objetos enquanto existissem referências a eles. Além disso, sugiro que umInt64
pode ser melhor do que um GUID, pois permite que os objetos recebam uma classificação persistente . Essas coisas podem ser úteis em cenários de bloqueio (por exemplo, pode-se evitar o deadlock se todo o código que precisará adquirir vários bloqueios o fizer em alguma ordem definida, mas para que isso funcione deve haver uma ordem definida).long
s; depende do seu cenário - em f.ex. sistemas distribuídos às vezes é mais útil trabalhar comGUID
s. Quanto aConditionalWeakTable
: você está certo;DependentHandle
verifica a vitalidade (NOTA: somente quando a coisa é redimensionada!), o que pode ser útil aqui. Ainda assim, se você precisar de desempenho, o bloqueio pode se tornar um problema, então, nesse caso, pode ser interessante usar isso ... para ser honesto, eu pessoalmente não gosto da implementação deConditionalWeakTable
, o que provavelmente leva ao meu preconceito de usar um simplesDictionary
- uniforme embora você esteja correto.ConditionalWeakTable
realmente funciona. O fato de que ele só permite a adição de itens me faz pensar que ele foi projetado para minimizar a sobrecarga relacionada à concorrência, mas não tenho ideia de como funciona internamente. Acho curioso que não exista umDependentHandle
invólucro simples que não use uma tabela, já que definitivamente há momentos em que é importante garantir que um objeto seja mantido vivo pelo tempo de vida de outro, mas o último objeto não tem espaço para uma referência para o primeiro.ConditionalWeakTable
não permite que as entradas que foram armazenadas na tabela sejam modificadas. Como tal, eu acho que poderia ser implementado com segurança usando barreiras de memória, mas não bloqueios. A única situação problemática seria se dois threads tentassem adicionar a mesma chave simultaneamente; isso pode ser resolvido fazendo com que o método "adicionar" execute uma barreira de memória depois que um item é adicionado e, em seguida, escaneie para garantir que exatamente um item tenha essa chave. Se vários itens tiverem a mesma chave, um deles será identificável como "primeiro", portanto, será possível eliminar os outros.Se você está escrevendo um módulo em seu próprio código para um uso específico, o método do majkinetor PODE ter funcionado. Mas existem alguns problemas.
Em primeiro lugar , o documento oficial NÃO garante que o
GetHashCode()
retorne um identificador exclusivo (consulte Object.GetHashCode Method () ):Em segundo lugar , suponha que você tenha uma quantidade muito pequena de objetos para que
GetHashCode()
funcione na maioria dos casos, este método pode ser substituído por alguns tipos.Por exemplo, você está usando alguma classe C e ela sobrescreve
GetHashCode()
para sempre retornar 0. Então, cada objeto de C obterá o mesmo código hash. Infelizmente,Dictionary
,HashTable
e alguns outros recipientes associativas fará usar este método:Portanto, essa abordagem tem grandes limitações.
E mais ainda , e se você quiser construir uma biblioteca de uso geral? Não apenas você não pode modificar o código-fonte das classes utilizadas, mas seu comportamento também é imprevisível.
Agradeço que Jon e Simon tenham postado suas respostas e postarei um exemplo de código e uma sugestão de desempenho abaixo.
Em meu teste, o
ObjectIDGenerator
lançará uma exceção para reclamar que há muitos objetos ao criar 10.000.000 objetos (10x do que no código acima) nofor
loop.Além disso, o resultado do benchmark é que a
ConditionalWeakTable
implementação é 1,8x mais rápida do que aObjectIDGenerator
implementação.fonte