2

假设我有一个从 C# 中的 B 类继承的 A 类。B 类有一个名为 Checksum 的属性,当在运行时调用它时,它会计算 A 类实例上所有属性的校验和(使用的特定校验和算法并不重要,可能来自 BCL)。

重要的是,校验和算法必须忽略校验和属性,否则在稍后验证时它将失败(因为校验和值将发生变化)。

因此,据我所知,有两种选择:

1)使用反射遍历对象的所有公共属性,连接成一个字符串并校验和。

2)假装对象只是一堆连续的内存地址,并将其视为字节数组和校验和。

1 - 听起来很慢 2 - 听起来很困难,因为我不确定你如何让它忽略代表校验和本身的字符串,或者如何处理对其他对象的引用。

有没有人有比 1 更好的想法,这听起来像是这两种解决方案中更好的?

4

3 回答 3

5

您可以将校验和属性装饰为NonSerialized并将类的实例序列化为字节数组,然后计算校验和。这样,在序列化时该属性将被忽略。

于 2010-01-18T11:41:03.003 回答
2

为什么它必须是财产?如果它是一个方法,GetChecksum() 那么你就不必有任何特殊的逻辑,这样它就不会将自己包含在校验和计算中。现在,您创建的内容与现有的 GetHashCode() 方法的用途几乎完全相同——只需提供一个实现即可。

通常,人们会为每个类显式编码 GetHashCode(),尽管快速的网络搜索会发现使用反射来提供通用(虽然速度较慢)机制的方法。通常人们会将想要包含在哈希码中的每个字段,将其转换为整数并将其乘以一个固定数字,以便具有不同字段值的不同对象给出不同的哈希码,这些哈希码很好地分布在整数范围内。

例如,Resharper 生成如下所示的 GetHashCode() 方法:

public override int GetHashCode()
{
    unchecked
    {
        int result = a;
        result = (result * 397) ^ (b != null ? b.GetHashCode() : 0);
        result = (result * 397) ^ c.GetHashCode();
        return result;
    }
}

其中 a 是 int,b 是 string,c 是 long。中间值(结果)乘以 397,并在每个步骤中使用下一个组件的哈希码。未检查意味着如果整数溢出(这很可能),那么我们丢弃溢出并回绕。在大多数情况下,这应该可以合理地覆盖整数空间——尽管我建议测试覆盖率,因为糟糕的哈希码可能会对系统性能产生严重后果。

应注意处理任何字段的零,以免您乘以零并最终得到大量散列码为零的对象。

于 2010-01-18T11:55:14.690 回答
1

选项 3 是创建一个即时计算所有属性的校验和的方法,例如通过使用反射.emit。这只对第一次调用效率低下,但生成的方法可以被缓存。如果您知道必须对哪些类型进行校验和,则还可以使用代码生成在编译时为它们创建校验和方法。

于 2010-01-18T11:47:44.947 回答