C# .NET唯一对象标识符
有没有办法获取实例的唯一标识符C# .NET唯一对象标识符,c#,unique,hashcode,gethashcode,C#,Unique,Hashcode,Gethashcode,有没有办法获取实例的唯一标识符 GetHashCode()对于指向同一实例的两个引用是相同的。但是,两个不同的实例可以(非常容易)获得相同的哈希代码: Hashtable hashCodesSeen = new Hashtable(); LinkedList<object> l = new LinkedList<object>(); int n = 0; while (true) { object o = new object(); // Remember
GetHashCode()
对于指向同一实例的两个引用是相同的。但是,两个不同的实例可以(非常容易)获得相同的哈希代码:
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);
}
引用是对象的唯一标识符。我不知道如何将其转换为字符串等。在压缩过程中,引用的值会发生变化(如您所见),但之前的每个值a都会变为值B,因此就安全代码而言,它仍然是唯一的ID
如果所涉及的对象在您的控制之下,您可以使用(避免垃圾收集)对您选择的ID(GUID、整数等)的引用来创建映射。但是,这会增加一定的开销和复杂性。
RuntimeHelpers.GetHashCode()
可能会有帮助()。您必须自己手动分配这样的标识符,无论是在实例内部还是外部
对于与数据库相关的记录,主键可能很有用(但仍然可以获得重复项)。或者,使用Guid
,或者保留您自己的计数器,使用联锁分配。增量
(并使其足够大,以免溢出)。是否签出该类?这就是你想要做的,和Marc Gravell描述的
ObjectedGenerator跟踪以前识别的对象。当您请求对象的ID时,ObjectedGenerator知道是返回现有ID,还是生成并记住新ID
ID在ObjectedGenerator实例的生命周期中是唯一的。通常,ObjectedGenerator的寿命与创建它的格式化程序的寿命一样长。对象ID仅在给定的序列化流中有意义,用于跟踪哪些对象引用了序列化对象图中的其他对象
ObjectedGenerator使用哈希表保留分配给哪个对象的ID。唯一标识每个对象的对象引用是运行时垃圾收集堆中的地址。对象引用值可以在序列化过程中更改,但表会自动更新,因此信息是正确的
对象ID是64位数字。分配从1开始,因此零永远不是有效的对象ID。格式化程序可以选择零值来表示值为空引用的对象引用(在Visual Basic中为空)
你可以在一秒钟内开发出你自己的东西。例如:
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++;
}
}
类程序
{
静态void Main(字符串[]参数)
{
var a=新对象();
var b=新对象();
Console.WriteLine(“,a.GetId(),b.GetId());
}
}
公共静态类MyExtensions
{
//本词典应使用弱键引用
静态字典d=新字典();
静态int-gid=0;
公共静态int GetId(此对象为o)
{
如果(d.ContainsKey(o))返回d[o];
返回d[o]=gid++;
}
}
您可以选择自己想要的唯一ID,例如System.Guid.NewGuid()或integer,以实现最快的访问速度。我知道这已经得到了回答,但至少需要注意的是,您可以使用:
它不会直接为您提供“唯一id”,但与WeakReferences(和hashset?)相结合可以为您提供一种非常简单的跟踪各种实例的方法。在Visual Studio中可以创建唯一的对象标识符:在“监视”窗口中,右键单击对象变量,然后从上下文菜单中选择“生成对象id” 不幸的是,这是一个手动步骤,我不相信标识符可以通过代码访问。这种方法如何: 将第一个对象中的字段设置为新值。如果第二个对象中的同一字段具有相同的值,则可能是同一实例。否则,按不同的方式退出 现在将第一个对象中的字段设置为不同的新值。如果第二个对象中的同一字段已更改为不同的值,则它肯定是同一实例 不要忘记在退出时将第一个对象中的字段设置回其原始值 问题?仅限.NET 4及更高版本 各位,好消息 用于此项工作的完美工具是.NET4中内置的,名为。这一类:
- 可用于将任意数据与托管对象实例关联,就像字典一样(尽管它不是字典)
- 不依赖于内存地址,因此不受压缩堆的GC的影响
- 不能仅仅因为对象已作为键输入到表中而使其保持活动状态,因此可以使用它,而不会使流程中的每个对象永远活动
- 使用引用等式确定对象标识;除此之外,类作者无法修改此行为,以便可以在任何类型的对象上一致地使用它
- 可以动态填充,因此不需要在对象构造函数中插入代码
- 对象需要唯一的ID,默认情况下不存在该ID。相反,我们必须依赖下一个最好的东西,即
,来获得一种唯一的IDRuntimeHelpers.GetHashCode
- 要检查唯一性,这意味着我们需要使用
object.ReferenceEquals
- 但是,我们仍然希望有一个唯一的ID,所以我添加了一个
,根据定义它是唯一的GUID
- 因为我不喜欢在不需要的情况下锁定所有内容,所以我不使用
ConditionalWeakTable
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;
}
}
如果您正在用自己的代码为特定用途编写模块,可能ha
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;
}
}
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
}
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Collections.Generic;
namespace ObjectSet
{
public interface IObjectSet
{
/// <summary> check the existence of an object. </summary>
/// <returns> true if object is exist, false otherwise. </returns>
bool IsExist(object obj);
/// <summary> if the object is not in the set, add it in. else do nothing. </summary>
/// <returns> true if successfully added, false otherwise. </returns>
bool Add(object obj);
}
public sealed class ObjectSetUsingConditionalWeakTable : IObjectSet
{
/// <summary> unit test on object set. </summary>
internal static void Main() {
Stopwatch sw = new Stopwatch();
sw.Start();
ObjectSetUsingConditionalWeakTable objSet = new ObjectSetUsingConditionalWeakTable();
for (int i = 0; i < 10000000; ++i) {
object obj = new object();
if (objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
if (!objSet.Add(obj)) { Console.WriteLine("bug!!!"); }
if (!objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public bool IsExist(object obj) {
return objectSet.TryGetValue(obj, out tryGetValue_out0);
}
public bool Add(object obj) {
if (IsExist(obj)) {
return false;
} else {
objectSet.Add(obj, null);
return true;
}
}
/// <summary> internal representation of the set. (only use the key) </summary>
private ConditionalWeakTable<object, object> objectSet = new ConditionalWeakTable<object, object>();
/// <summary> used to fill the out parameter of ConditionalWeakTable.TryGetValue(). </summary>
private static object tryGetValue_out0 = null;
}
[Obsolete("It will crash if there are too many objects and ObjectSetUsingConditionalWeakTable get a better performance.")]
public sealed class ObjectSetUsingObjectIDGenerator : IObjectSet
{
/// <summary> unit test on object set. </summary>
internal static void Main() {
Stopwatch sw = new Stopwatch();
sw.Start();
ObjectSetUsingObjectIDGenerator objSet = new ObjectSetUsingObjectIDGenerator();
for (int i = 0; i < 10000000; ++i) {
object obj = new object();
if (objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
if (!objSet.Add(obj)) { Console.WriteLine("bug!!!"); }
if (!objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public bool IsExist(object obj) {
bool firstTime;
idGenerator.HasId(obj, out firstTime);
return !firstTime;
}
public bool Add(object obj) {
bool firstTime;
idGenerator.GetId(obj, out firstTime);
return firstTime;
}
/// <summary> internal representation of the set. </summary>
private ObjectIDGenerator idGenerator = new ObjectIDGenerator();
}
}