在运行时计算类代码的哈希值(C#)?

9
在C#中是否有可能在运行时计算类的哈希值(可能是通过反射实现)?要明确的是,我不想计算所述类的实例的哈希码,我想计算类代码本身的哈希值(如果类中的函数发生变化,则期望计算出不同的哈希值)。理想情况下,这只会对对象代码的更改敏感(而不仅仅是代码本身的字符串表示的哈希值)。
感谢您的帮助,
-- Breck
3个回答

11
如果您拥有 Type,则可以使用 GetMethod 获取 MethodInfo 实例,该实例具有 GetMethodBody,返回所述方法的 IL。然后,您可以对其进行哈希处理。
我很好奇。首先,您为什么要这样做?

3
我想这么做的原因是,在我的情况下,我有一个时间复杂度相当高的函数f。问题在于,我正在尝试确定f应该返回的值,因此f经常发生变化。如果特定版本的f,fi,已经针对某个输入x运行过,我希望缓存结果fi(x),以便如果fi再次运行,它可以直接使用记忆化的值。 - Breck Fresen
1
在我的情况下,原因是我保存了一个带有内部方法的 POCO 类的快照(记忆化值)。如果类的某个内部方法被修改,我需要使该快照失效。我会尝试这样做,谢谢。 - Narvalex
在我的情况下,是为了确保保存在数据库中的一些结果仍然有效,使用类的“版本”。为此,我创建了一个类,将其作为解决方案添加到了这个问题中。 - Master DJon
这在测试中非常有用 -- 我有一个测试,它设置一些静态数据并将其保存到磁盘上的文件中,以便我可以在每个测试实例中重复使用它。也就是说,如果我更改函数定义,我希望它能使用新的定义重新构建缓存文件的新实例。 - undefined

0

我知道这个问题很老了,但我想分享一下我的HashClassCalculator,它可以给出所有方法和属性代码的哈希值。因此,如果这些内容发生任何变化,哈希值也会改变。

internal static class HashClassCalculator
{
    private static Dictionary<Type, string> hashes = new ();
    private static object lockingObject = new ();

    /// <summary>
    /// Calculate an hash of the code for a specific type
    /// </summary>
    /// <param name="type">Type to hash the code</param>
    /// <returns>MD5 hash of the class code</returns>
    /// <remarks>Thread protected + Cached for faster access</remarks>
    public static string CalculateHash(Type type)
    {
        if (hashes.ContainsKey(type)) return hashes[type]; // Fast return

        var currentHash = "";
        lock (lockingObject)
        {
            if (hashes.ContainsKey(type)) return hashes[type]; // Fast return

            var allFlag = System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic
                | System.Reflection.BindingFlags.Instance
                | System.Reflection.BindingFlags.Static;
            var hasher = MD5.Create();

            // Get all properties hashes
            var props = type.GetProperties(allFlag);
            foreach (var prop in props)
            {
                var hashBytes = hasher.ComputeHash(prop.GetMethod.GetMethodBody().GetILAsByteArray());
                currentHash += Convert.ToBase64String(hashBytes); // Concatenate all the hashes
            }

            // Get all methods hashes
            var methods = type.GetMethods(allFlag);
            foreach (var method in methods)
            {
                if (method.DeclaringType != type) continue;

                var hashBytes = hasher.ComputeHash(method.GetMethodBody().GetILAsByteArray());
                currentHash += Convert.ToBase64String(hashBytes); // Concatenate all the hashes
            }

            // Hashing all hashes into one smaller
            var hashOfHashes = hasher.ComputeHash(Encoding.ASCII.GetBytes(currentHash));
            currentHash = Convert.ToBase64String(hashOfHashes);

            // Add to cache
            hashes.Add(type, currentHash);
        }

        return currentHash;
    }
}

-1
你不需要使用反射。你可以尝试使用 System.Type.GetHashCode()。我不确定它是如何工作的,如果不够优化,那么你可以随时使用type.FullName.GetHashCode()

1
Type.GetHashCode() 似乎在重新编译时会改变其值。而 type.FullName.GetHashCode() 则不会因为方法的更改而改变哈希值。 - Jimmy
System.Type.GetHashCode会很好地工作,如果方法名称更改或参数名称/类型更改等,则会更改。但是,type.FullName.GetHashCode不起作用,因为类型名称可能保持不变,但方法可能会更改。 - jjxtra
@Jeff:class Program { static void Main() { Console.Write(typeof(Program).GetHashCode(); } } 在编译之间更改其输出。 - Jimmy
@Jimmy,你说得对,我应该用完全相同的类运行测试两次,抱歉... - jjxtra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接