Apakah ada cara untuk mendapatkan pengenal unik dari sebuah instance?
GetHashCode()
adalah sama untuk dua referensi yang menunjuk ke instance yang sama. Namun, dua contoh berbeda dapat (dengan mudah) mendapatkan kode hash yang sama:
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);
}
Saya sedang menulis addin debugging, dan saya perlu mendapatkan semacam ID untuk referensi yang unik selama menjalankan program.
Saya sudah berhasil mendapatkan ALAMAT internal dari instance, yang unik sampai pengumpul sampah (GC) memadatkan heap (= memindahkan objek = mengubah alamat).
Pertanyaan Stack Overflow Implementasi default untuk Object.GetHashCode () mungkin terkait.
Objek tidak di bawah kendali saya karena saya mengakses objek dalam program yang sedang di-debug menggunakan API debugger. Jika saya mengontrol objek, menambahkan pengenal unik saya sendiri akan menjadi hal yang sepele.
Saya ingin ID unik untuk membangun ID hashtable -> objek, agar dapat mencari objek yang sudah dilihat. Untuk saat ini saya menyelesaikannya seperti ini:
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
}
sumber
.NET 4 dan yang lebih baru saja
Kabar baik, semuanya!
Alat yang sempurna untuk pekerjaan ini ada di .NET 4 dan disebut
ConditionalWeakTable<TKey, TValue>
. Kelas ini:sumber
ConditionalWeakTable
mengandalkanRuntimeHelpers.GetHashCode
danobject.ReferenceEquals
melakukan pekerjaan batinnya. Perilakunya sama dengan membangunIEqualityComparer<T>
yang menggunakan dua metode ini. Jika Anda membutuhkan kinerja, saya benar-benar menyarankan untuk melakukan ini, karenaConditionalWeakTable
memiliki kunci di sekitar semua operasinya untuk membuatnya aman untuk utas.ConditionalWeakTable
memegang referensi untuk masing-masingValue
yang hanya sekuat referensi yang diadakan di tempat lain yang sesuaiKey
. Sebuah objek yangConditionalWeakTable
memiliki satu-satunya referensi yang masih ada di mana pun di alam semesta akan secara otomatis tidak ada lagi jika kuncinya ada.Memeriksa kelas ObjectIDGenerator ? Ini melakukan apa yang Anda coba lakukan, dan apa yang dijelaskan Marc Gravell.
sumber
RuntimeHelpers.GetHashCode()
mungkin membantu ( MSDN ).sumber
Anda dapat mengembangkan hal Anda sendiri dalam sekejap. Misalnya:
Anda dapat memilih apa yang ingin Anda miliki sebagai ID unik Anda sendiri, misalnya, System.Guid.NewGuid () atau hanya integer untuk akses tercepat.
sumber
Dispose
bug, karena ini akan mencegah segala jenis pembuangan.Bagaimana dengan metode ini:
Setel bidang di objek pertama ke nilai baru. Jika bidang yang sama di objek kedua memiliki nilai yang sama, itu mungkin contoh yang sama. Jika tidak, keluar sebagai berbeda.
Sekarang atur bidang di objek pertama ke nilai baru yang berbeda. Jika bidang yang sama di objek kedua telah berubah ke nilai yang berbeda, itu pasti contoh yang sama.
Jangan lupa untuk menyetel bidang di objek pertama kembali ke nilai aslinya saat keluar.
Masalah?
sumber
Dimungkinkan untuk membuat pengenal objek unik di Visual Studio: Di jendela tonton, klik kanan variabel objek dan pilih Buat ID Objek dari menu konteks.
Sayangnya, ini adalah langkah manual, dan saya tidak yakin pengenal dapat diakses melalui kode.
sumber
Anda harus menetapkan sendiri pengenal tersebut, secara manual - baik di dalam instance, atau secara eksternal.
Untuk rekaman yang terkait dengan database, kunci utama mungkin berguna (tetapi Anda masih bisa mendapatkan duplikat). Sebagai alternatif, gunakan a
Guid
, atau pertahankan penghitung Anda sendiri, alokasikan menggunakanInterlocked.Increment
(dan buat cukup besar sehingga tidak akan meluap).sumber
Saya tahu ini telah dijawab, tetapi setidaknya berguna untuk dicatat bahwa Anda dapat menggunakan:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
Yang tidak akan memberi Anda "id unik" secara langsung, tetapi dikombinasikan dengan WeakReferences (dan hashset?) Dapat memberi Anda cara yang cukup mudah untuk melacak berbagai contoh.
sumber
Informasi yang saya berikan disini bukanlah hal baru, saya hanya menambahkan ini untuk kelengkapan.
Ide kode ini cukup sederhana:
RuntimeHelpers.GetHashCode
memberi kita semacam ID unikobject.ReferenceEquals
GUID
, yang menurut definisi unik.ConditionalWeakTable
.Gabungan, itu akan memberi Anda kode berikut:
Untuk menggunakannya, buat instance
UniqueIdMapper
dan gunakan GUID yang dikembalikannya untuk objek.Tambahan
Jadi, ada sedikit lagi yang terjadi di sini; izinkan saya menulis sedikit tentang
ConditionalWeakTable
.ConditionalWeakTable
melakukan beberapa hal. Yang paling penting adalah ia tidak peduli dengan pengumpul sampah, yaitu: objek yang Anda rujuk dalam tabel ini akan tetap dikumpulkan. Jika Anda mencari objek, pada dasarnya berfungsi sama dengan kamus di atas.Penasaran bukan? Lagi pula, ketika sebuah objek dikumpulkan oleh GC, ia memeriksa apakah ada referensi ke objek tersebut, dan jika ada, ia mengumpulkannya. Jadi jika ada objek dari
ConditionalWeakTable
, mengapa objek yang direferensikan dikumpulkan?ConditionalWeakTable
menggunakan trik kecil, yang juga digunakan oleh beberapa struktur .NET lainnya: alih-alih menyimpan referensi ke objek, ia sebenarnya menyimpan IntPtr. Karena itu bukan acuan yang sebenarnya, benda itu bisa dikoleksi.Jadi, saat ini ada 2 masalah yang harus diatasi. Pertama, objek bisa dipindahkan di heap, jadi apa yang akan kita gunakan sebagai IntPtr? Dan kedua, bagaimana kita tahu bahwa objek memiliki referensi aktif?
DependentHandle
- tapi saya yakin ini sedikit lebih canggih.DependentHandle
.Solusi terakhir ini memang mengharuskan runtime tidak menggunakan kembali bucket daftar sampai mereka dibebaskan secara eksplisit, dan juga mengharuskan semua objek diambil dengan panggilan ke runtime.
Jika kami menganggap mereka menggunakan solusi ini, kami juga dapat mengatasi masalah kedua. Algoritme Tandai & Sapu melacak objek mana yang telah dikumpulkan; segera setelah dikumpulkan, kami tahu saat ini. Setelah objek memeriksa apakah objek ada di sana, ia memanggil 'Gratis', yang menghapus penunjuk dan entri daftar. Benda itu benar-benar hilang.
Satu hal penting yang perlu diperhatikan pada saat ini adalah bahwa ada yang salah jika
ConditionalWeakTable
diperbarui di beberapa utas dan jika tidak aman utas. Hasilnya akan menjadi kebocoran memori. Inilah sebabnya mengapa semua panggilan masukConditionalWeakTable
melakukan 'kunci' sederhana yang memastikan ini tidak terjadi.Hal lain yang perlu diperhatikan adalah pembersihan entri harus dilakukan sesekali. Sementara objek sebenarnya akan dibersihkan oleh GC, entri tidak. Inilah mengapa
ConditionalWeakTable
hanya tumbuh dalam ukuran. Setelah mencapai batas tertentu (ditentukan oleh peluang tabrakan dalam hash), ini memicu aResize
, yang memeriksa apakah objek harus dibersihkan - jika ya,free
dipanggil dalam proses GC, menghapusIntPtr
pegangan.Saya percaya ini juga mengapa
DependentHandle
tidak diekspos secara langsung - Anda tidak ingin mengacaukan banyak hal dan akibatnya mendapatkan kebocoran memori. Hal terbaik berikutnya untuk itu adalahWeakReference
(yang juga menyimpanIntPtr
alih - alih objek) - tetapi sayangnya tidak menyertakan aspek 'ketergantungan'.Yang tersisa adalah Anda bermain-main dengan mekanik, sehingga Anda dapat melihat ketergantungan dalam tindakan. Pastikan untuk memulainya beberapa kali dan lihat hasilnya:
sumber
ConditionalWeakTable
mungkin lebih baik, karena ini hanya akan mempertahankan representasi untuk objek sementara referensi ada padanya. Juga, saya menyarankan bahwaInt64
mungkin lebih baik daripada GUID, karena akan memungkinkan objek diberi peringkat yang persisten . Hal-hal seperti itu mungkin berguna dalam skenario penguncian (misalnya, seseorang dapat menghindari kebuntuan jika satu semua kode yang perlu memperoleh banyak kunci melakukannya dalam beberapa urutan yang ditentukan, tetapi untuk bekerja harus ada urutan yang ditentukan).long
s; itu tergantung pada skenario Anda - di f.ex. sistem terdistribusi terkadang lebih berguna untuk bekerja denganGUID
s. AdapunConditionalWeakTable
: Anda benar;DependentHandle
memeriksa keaktifan (CATATAN: hanya jika ukurannya berubah!), yang dapat berguna di sini. Namun, jika Anda membutuhkan kinerja, penguncian dapat menjadi masalah di sana, jadi dalam hal ini mungkin menarik untuk menggunakan ini ... jujur saya pribadi tidak suka penerapannyaConditionalWeakTable
, yang mungkin mengarah pada bias saya menggunakan yang sederhanaDictionary
- bahkan meskipun kamu benar.ConditionalWeakTable
sebenarnya cara kerjanya. Fakta bahwa itu hanya memungkinkan item untuk ditambahkan membuat saya berpikir bahwa itu dirancang untuk meminimalkan overhead terkait konkurensi, tetapi saya tidak tahu cara kerjanya secara internal. Saya merasa penasaran bahwa tidak adaDependentHandle
pembungkus sederhana yang tidak menggunakan tabel, karena pasti ada saat-saat penting untuk memastikan bahwa satu objek tetap hidup untuk seumur hidup yang lain, tetapi objek terakhir tidak memiliki ruang untuk referensi ke yang pertama.ConditionalWeakTable
mengizinkan entri yang telah disimpan dalam tabel untuk dimodifikasi. Dengan demikian, saya akan berpikir bahwa ini dapat diimplementasikan dengan aman menggunakan penghalang memori tetapi tidak dengan kunci. Satu-satunya situasi bermasalah adalah jika dua utas mencoba menambahkan kunci yang sama secara bersamaan; yang dapat diatasi dengan meminta metode "tambah" melakukan penghalang memori setelah item ditambahkan, dan kemudian memindai untuk memastikan bahwa tepat satu item memiliki kunci tersebut. Jika beberapa item memiliki kunci yang sama, salah satunya akan dapat diidentifikasi sebagai "pertama", sehingga memungkinkan untuk menghilangkan yang lain.Jika Anda menulis modul dalam kode Anda sendiri untuk penggunaan tertentu, metode majkinetor MUNGKIN berhasil. Namun ada beberapa masalah.
Pertama , dokumen resmi TIDAK menjamin bahwa
GetHashCode()
pengembalian sebuah pengenal unik (lihat Object.GetHashCode Method () ):Kedua , asumsikan Anda memiliki jumlah objek yang sangat kecil sehingga
GetHashCode()
akan berfungsi dalam banyak kasus, metode ini dapat diganti oleh beberapa jenis.Misalnya, Anda menggunakan beberapa kelas C dan itu menimpa
GetHashCode()
untuk selalu mengembalikan 0. Kemudian setiap objek C akan mendapatkan kode hash yang sama. Sayangnya,Dictionary
,HashTable
dan beberapa wadah asosiatif lainnya akan membuat menggunakan metode ini:Jadi, pendekatan ini memiliki keterbatasan yang besar.
Dan terlebih lagi , bagaimana jika Anda ingin membangun perpustakaan serba guna? Anda tidak hanya tidak dapat mengubah kode sumber dari kelas yang digunakan, tetapi perilakunya juga tidak dapat diprediksi.
Saya menghargai bahwa Jon dan Simon telah memposting jawaban mereka, dan saya akan memposting contoh kode dan saran tentang kinerja di bawah ini.
Dalam pengujian saya,
ObjectIDGenerator
akan mengeluarkan pengecualian untuk mengeluh bahwa ada terlalu banyak objek saat membuat 10.000.000 objek (10x dari pada kode di atas) difor
loop.Selain itu, hasil benchmarknya adalah
ConditionalWeakTable
implementasinya 1,8x lebih cepat daripadaObjectIDGenerator
implementasinya.sumber