对象为GUID / UUID

时间:2016-09-08 06:19:36

标签: c# object uuid guid

我想接受任何对象并获得代表该对象的guid。

我知道这需要很多东西。我正在为常见的应用程序寻找一个足够好的解决方案。

我的具体用例是缓存,我想知道用于创建我正在缓存的东西的对象已经在过去做过了。将有2种不同类型的对象。每种类型仅包含公共属性,并且可能包含列表/ ienumable。

假设对象可以序列化我的第一个想法是将它序列化为json(通过原生jsonserlizer或newtonsoft),然后获取json字符串并将其转换为uuid版本5,详见详见How can I generate a GUID for a string?

我的第二种方法,如果它不可序列化(例如包含字典)将使用公共属性上的反射来生成某种类型的唯一字符串,然后将其转换为uuid版本5.

这两种方法都使用uuid版本5将字符串带到guid。是否有一个经过验证的c#类可以生成有效的uuid 5指南?要点看起来很好,但想要确定。

我正在考虑使c#名称空间和类型名称成为uuid 5的名称空间。这是名称空间的有效使用吗?

我的第一种方法对于我的简单用例来说足够好,但我想探索第二种方法,因为它更灵活。

如果创建guid无法保证合理的唯一性,则应该抛出错误。当然,超级复杂的物体会失败。如果使用反射,我怎么知道这种情况?

我正在寻找第二种方法的新方法或关注/实施。

3 个答案:

答案 0 :(得分:0)

正如评论中所说,这里并没有完全用白银制造的子弹,但是有一些子弹非常接近。使用哪一个取决于您要与您的类一起使用的类型以及您的上下文,例如您何时认为两个对象相等。但是,请注意,您将始终面临可能的冲突,一个GUID不足以保证避免冲突。您所要做的就是减少发生碰撞的可能性。

就您而言,

  

已经过去了

听起来像您不想引用引用相等,但想要使用值相等的概念。最简单的方法是信任类使用值相等来实现相等,因为在这种情况下,您已经使用GetHashCode完成了操作,但是冲突发生的可能性更高,因为它只有32位。此外,您会假设编写班级的人做得很好,但这并不总是一个好的假设,特别是因为人们倾向于先怪您而不是自己。

否则,您最好的机会是将序列化与您选择的哈希算法结合使用。我会推荐MD5,因为它是最快的,可以产生GUID所需的128位。如果您说您的类型仅包含公共属性,那么我建议像这样使用XmlSerializer

    private MD5 _md5 = new MD5CryptoServiceProvider();
    private Dictionary<Type, XmlSerializer> _serializers = new Dictionary<Type, XmlSerializer>();
    public Guid CreateID(object obj)
    {
      if (obj == null) return Guid.Empty;
      var type = obj.GetType();
      if (!_serializers.TryGetValue(type, out var serializer))
      {
        serializer = new XmlSerializer(type);
        _serializers.Add(type, serializer);
      }
      using (var stream = new MemoryStream())
      {
         serializer.Serialize(stream, obj);
         stream.Position = 0;
         return new Guid(_md5.ComputeHash(stream));
      }
    }

几乎所有串行器都有其缺点。 XmlSerializer无法序列化循环对象图,DataContractSerializer要求您的类型具有专用属性,并且基于SerializableAttribute的旧序列化程序也需要设置该属性。您必须以某种方式做出假设。

答案 1 :(得分:0)

平等问题是一个难题。
这里有一些关于如何解决问题的想法。

散列序列化的对象
一种方法是序列化一个对象,然后按照Georg的建议对结果进行哈希处理。
使用md5校验和可通过正确的输入为您提供强大的校验和。
但是正确是问题所在。

您可能无法使用通用的序列化框架,因为:

  • 他们不在乎浮点数是1.0还是1.000000000000001。
  • 他们对平等的理解可能与您/您的雇主不同。
  • 他们使用不需要的符号在序列化的文本上膨胀。 (性能)
  • 序列化文本中的少许偏差会导致散列的GUID / UUID发生较大的偏差。

这就是为什么,您应该仔细测试所做的任何序列化。
否则,您可能会得到对象的假阳性/假阳性(大多是假阴性)。

需要考虑的几点:

  • 浮球和双打:
    始终以相同的方式编写它们,最好使用相同的位数,以防止像1.000000000000001 vs 1.0这样的东西受到干扰。
  • DateTime,TimeStamp等:
    应用不会改变且无歧义的固定格式。
  • 无序集合:
    在对数据进行序列化之前对其进行排序。顺序必须明确
  • 字符串:
    平等区分大小写吗?如果不是,则将所有字符串都小写或大写。
    如有必要,使它们的文化不变。
  • 更多:
    对于每种类型,请仔细考虑什么相等和什么不相等。特别考虑边缘情况。 (float.NaN,-0与0,null等)

由您使用现有的序列化器还是自己决定。
自己执行此操作比较容易出错,但是您可以完全控制相等性和序列化的各个方面。
使用现有的串行器也容易出错,因为您需要测试或证明结果是否始终如您所愿。


引入明确的顺序并使用树
如果您可以控制源代码,则可以引入自定义订单功能。
订单必须考虑所有属性,子对象,列表等。 然后,您可以创建一个二叉树,并使用该命令来插入和查找对象。

第一种方法提到的相同问题仍然适用,您需要确保这样检测到相等的值。 大的O性能也比使用散列更糟糕。但是在大多数实际的示例中,实际性能应该是可比的或至少足够快。

好处是,一旦发现不相等的属性或值,就可以停止比较两个对象。因此,无需始终关注整个对象。 一棵二叉树需要进行O(log2(n))比较才能查找,因此相当快。

不好的是,您需要访问所有实际对象,从而将它们保存在内存中。 哈希表只需要进行O(1)比较即可查找,因此(理论上至少)会更快。


将它们放入数据库
如果将所有对象存储在数据库中,则数据库可以为您进行查找。
数据库在比较对象方面非常出色,并且内置了处理等价/接近等价问题的机制。

我不是数据库专家,因此对于此选项,其他人可能会对此解决方案的性能有更深入的了解。

答案 2 :(得分:-1)

正如其他人在评论中所说的那样,如果你愿意以GetHashCode作为你的关键,那么int可能会为你提供帮助。如果没有,则有一个Guid构造函数,其长度为byte[]。您可以尝试以下内容

using System.Linq;
class Foo
{
    public int A { get; set; }
    public char B { get; set; }
    public string C { get; set; }
    public Guid GetGuid()
    {
        byte[] aBytes = BitConverter.GetBytes(A);
        byte[] bBytes = BitConverter.GetBytes(B);
        byte[] cBytes = BitConverter.GetBytes(C);
        byte[] padding = new byte[16];
        byte[] allBytes =
            aBytes
                .Concat(bBytes)
                .Concat(cBytes)
                .Concat(padding)
                .Take(16)
                .ToArray();
        return new Guid(allBytes);
    }
}