【问题标题】:Creating consistent Guid for an object为对象创建一致的 Guid
【发布时间】:2017-07-25 01:15:48
【问题描述】:

我们必须为一个对象创建一致的 Guid。我正在考虑使用以下方法为具有相同属性的对象创建唯一的 guid,但不知何故感觉不对。

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public override string ToString()
    {
        return "Person: " + Name + " " + Age;
    }
}

// Below tool is taken from http://stackoverflow.com/questions/2642141/how-to-create-deterministic-guids
Guid guid = GuidUtility.Create(GLOBAL, new Person(..).ToString();

我想要。

我正在考虑的另一个可能的解决方案是将Map<Guid, serialized Object> 保存在持久存储中,并且每次在创建新对象时查看具有相同属性的对象是否已经存在。

RFC 4122 保证它们很有可能在每个命名空间和名称中都是确定性和唯一的。 https://www.rfc-editor.org/rfc/rfc4122#page-13 请有任何建议。谢谢

【问题讨论】:

  • 我不明白你为什么要使用Guid
  • 好吧,换句话说,你为什么要使用Guid

标签: c# .net oop uuid


【解决方案1】:

我在这个答案GetHashCode 上使用散列算法提出了这个问题。显然,对于具有相同哈希码的不同值对象,总是存在冲突问题。但是这个问题是无法解决的,因为您实际上是从潜在的无限域转换为非常有限的范围,对于 Guid 的 128 位,主要是 32 位。解决此类问题的唯一方法是,即使发生的可能性非常低,正如您所说,实施查找表。

public static class ConsistentGuid
{
    public static System.Guid Generate(object obj)
    {
        var bytes = new byte[16];

        var type = obj.GetType();

        var features = new object[]
        {
            type,
            obj
        };

        BitConverter.GetBytes(LongHash(features))
            .CopyTo(bytes, 0);

        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        features = new object[properties.Length];
        for (int i = 0; i < properties.Length; i++)
            features[i] = properties[i].GetValue(obj);

        BitConverter.GetBytes(LongHash(features))
            .CopyTo(bytes, 8);

        return new System.Guid(bytes);
    }

    public static int Hash(object[] features, uint seed = 2166136261)
    {
        // https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416
        unchecked // Overflow is fine, just wrap
        {
            int hash = (int)seed;
            for (int i = 0; i < features.Length; i++)
            {
                if (features[i] == null) // Suitable nullity checks etc, of course :)
                    continue;

                hash = (hash * 16777619) ^ features[i].GetHashCode();
            }

            return hash;
        }
    }

    private static long LongHash(object[] features, ulong seed = 2166136261)
    {
        // https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416
        unchecked // Overflow is fine, just wrap
        {
            long hash = (long)seed;
            for (int i = 0; i < features.Length; i++)
            {
                if (features[i] == null) // Suitable nullity checks etc, of course :)
                    continue;

                hash = (hash * 16777619) ^ features[i].GetHashCode();
            }

            return hash;
        }
    }
}

这是通过的测试;

public class ConsistentGuidTests
{
    [Fact]
    public void Referencewise_Equal_Objects_Should_Generate_Same_Guids()
    {
        var obj = new object();
        var obj2 = obj;

        var guid1 = ConsistentGuid.Generate(obj);
        var guid2 = ConsistentGuid.Generate(obj2);

        Assert.True(ReferenceEquals(obj, obj2));
        Assert.Equal(guid1, guid2);
    }

    [Fact]
    public void ValueObjects_Of_DifferentTypes_Should_Generate_Different_Guids()
    {
        var obj = new object();
        var other = new int();

        var guid1 = ConsistentGuid.Generate(obj);
        var guid2 = ConsistentGuid.Generate(other);

        Assert.NotEqual(guid1, guid2);
    }

    [Fact]
    public void ValueObjects_With_Same_Values_Should_Generate_Same_Guids()
    {
        var obj = 123;
        var other = 123;

        var guid1 = ConsistentGuid.Generate(obj);
        var guid2 = ConsistentGuid.Generate(other);

        Assert.False(ReferenceEquals(obj, other));
        Assert.Equal(guid1, guid2);
    }

    [Fact]
    public void ValueObjects_With_Different_Values_Should_Generate_Different_Guids()
    {
        var obj = 123;
        var other = 124;

        var guid1 = ConsistentGuid.Generate(obj);
        var guid2 = ConsistentGuid.Generate(other);

        Assert.NotEqual(guid1, guid2);
    }

    class AReferenceType
    {
        public int SomeProperty { get; set; }
        public string SomeOtherProperty { get; set; }

        public AReferenceType(int a, string b)
        {
            SomeProperty = a;
            SomeOtherProperty = b;
        }

        public override int GetHashCode()
        {
            return ConsistentGuid.Hash(new object[]
            {
                SomeProperty,
                SomeOtherProperty
            });
        }
    }

    [Fact]
    public void ReferenceObjects_With_Same_Values_Should_Generate_Same_Guids()
    {
        var a = 123;
        var b = "asd";

        var obj = new AReferenceType(a, b);
        var other = new AReferenceType(a, b);

        var guid1 = ConsistentGuid.Generate(obj);
        var guid2 = ConsistentGuid.Generate(other);

        Assert.False(ReferenceEquals(obj, other));
        Assert.Equal(obj.GetHashCode(), other.GetHashCode());
        Assert.Equal(guid1, guid2);
    }

    [Fact]
    public void ReferenceObjects_With_Different_Values_Should_Generate_Different_Guids()
    {
        var a = 123;
        var b = "asd";

        var obj = new AReferenceType(a, b);
        var other = new AReferenceType(a + 1, b);

        var guid1 = ConsistentGuid.Generate(obj);
        var guid2 = ConsistentGuid.Generate(other);

        Assert.NotEqual(guid1, guid2);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 2020-11-17
    • 2015-03-09
    • 2019-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多