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。相反,我们必须依赖下一个最好的东西,即
    RuntimeHelpers.GetHashCode
    ,来获得一种唯一的ID
  • 要检查唯一性,这意味着我们需要使用
    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();
    }
}