【问题标题】:Generating hash of a class structure in C#在 C# 中生成类结构的哈希
【发布时间】:2022-08-24 18:36:46
【问题描述】:

我们的应用程序使用状态的序列化快照,它只是类在某个时间点的序列化版本。

我们在类上有一个版本号,每次修改类时都会增加一个版本号,我们用它来指示以前的快照无效并忽略它们。

偶尔有人忘记增加版本,我们会得到错误。

我想为类的结构生成一个散列并用它来存储快照,所以如果它发生变化,那么以前的快照将自动失效。

我已经考虑过使用 Roslyn 加载类的表示并在其上调用 GetHashCode,但无法弄清楚如何将现有类加载到 Roslyn。

此外,我还研究了 Visual Studio 在构建时生成哈希:https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/march/visual-studio-hashing-source-code-files-with-visual-studio-to-assure-file-integrity 但这似乎有点矫枉过正。

那么如何生成现有 C# 类结构(不是类的实例)的哈希?

更新:

这是一个没有方法的 POCO。使快照无效的更改包括添加、删除或重命名属性。

  • 什么样的更改会使您的快照无效?仅更改字段还是更改方法声明甚至更改实现?
  • 序列化成不关心额外属性的格式怎么样,比如 JSON?如果 V1 有 2 个属性(姓名、地址)而 V2 有 3 个属性(姓名、地址、dob),那么 JSON 会将相同的数据序列化到两者中,没有任何问题。
  • 如果有人忘记更新版本号,那么在我看来,你错过了一个单元测试用例。
  • 您可以使用Source Generator。你可以让你的班级成为部分班级。生成器将创建第二个部分类文件,其中包含类的属性或带有哈希码的字符串常量。
  • 必须在每次编译时使类无效(即使没有更改)和更保守地忽略类获得某种不一定重要的更改之间取得平衡。每次以任何方式更改类时更改一个数字是最简单的(也是最保守的),但这也会计算外部观察者不可见或无关紧要的更改(添加 cmets、添加私有字段以缓存数据、添加属性),这可能是不可取的。

标签: c# hash roslyn


【解决方案1】:

我建议使用反射来获得它。

类似于以下内容:

private int GetHashOfClass(Type myType)
{
    int hash = 0;
    foreach (var f in myType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic))
    {
        if (f.MemberType == MemberTypes.Field ||
            (f.MemberType & MemberTypes.Property) != 0)
        {
            hash ^= CreateHash(f.Name);
        }

        if (f is FieldInfo fi)
        {
            hash ^= CreateHash(fi.FieldType.Name);
        }
    }

    return hash;
}

从类的字段和属性生成哈希码。 CreateHash 只是一个从字符串生成哈希的函数。做不是使用现有的string.GetHashCode,因为每次启动应用程序时该函数的结果都会改变。

【讨论】:

  • 我们以此为基础开始,但注意到嵌套对象也需要检查,因此使方法递归。如果数据类型发生变化,还可以根据类型和名称生成散列。
【解决方案2】:

您可以使用 JSchemaGenerator(来自 Newtonsoft.Json.Schema nuget):

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

JSchemaGenerator generator = new JSchemaGenerator();

string schema = generator.Generate(typeof(Person)).ToString();
//{
//  "type": "object",
//  "properties": {
//    "Name": {
//      "type": [ "string", "null" ]
//    },
//    "Age": { "type": "integer" }
//  },
//  "required": [ "Name", "Age" ]
//}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 2020-12-21
    • 1970-01-01
    • 2020-11-30
    • 1970-01-01
    相关资源
    最近更新 更多