在C#中对委托函数进行哈希处理

4

我该如何在C#中获得委托函数的哈希值?我想要判断不同的委托是否被传递到我的函数中。我的代码大致如下:

public string GetContent(Func<string, bool> isValid)
{
// Do some work
SomeFunctionToHashAFunction(isValid)
}

我会使用 .GetHashCode() 方法,但是 .NET 框架不能保证这些哈希码是唯一的。
编辑: 我有一些缓存内容需要验证,但我只想验证一次。但是如果验证函数更改了,则需要重新验证缓存内容。我不确定 ObjectIdGenerator 在这种情况下是否适用,因为我需要确定两个匿名函数是否具有相同的实现。

哈希值从定义上来说并不保证唯一性。事实上,它的保证是两个在 Equals 方法中返回 true 的对象会返回相同的哈希码(反之则不一定)。这就是为什么 GetHashCode 方法只应用于查找,而比较时应使用 Equals 方法来验证两个对象是否真正匹配。 - Filip Navara
5个回答

5

根据定义,哈希值并不能保证唯一性,因此哈希不是您想要的。

相反,您想要确定委托实例是否之前已经被“看到”。为了做到这一点,您可以使用ObjectIdGenerator

private static readonly ObjectIdGenerator oidg = new ObjectIdGenerator();

public string GetContent(Func<string, bool> isValid)
{
    bool firstTime;

    oidg.GetId(isValid, out firstTime);

    if (!firstTime)
    {
        ...
    }
}

然而,即使使用这种技术也有一些需要注意的缺陷:
  • ObjectIdGenerator 存储了传递给它的每个对象的引用。
  • 委托到相同函数的是不同的对象,因此会返回不同的 ID。
也许如果您解释一下您试图实现的目标,可能会有更好的方法。 编辑:鉴于您更新的要求,我建议将验证委托定义为一个属性。如果该属性发生变化,则需要重新验证。因此,GetContent()不需要任何参数。
public Func<string, bool> IsValidHandler
{
    get { return this.isValidHandler; }
    set 
    {
        this.isValidHandler = value;
        this.requiresValidation = true;
    }
}

public string GetContent()
{
    if (this.requiresValidation && this.isValidHandler != null)
    {
        // do validation

        this.requiresValidation = false;
    }

    // return content
}

你甚至可以进一步简化,当设置IsValidHandler属性时进行验证(而不是在GetContent方法中)。

非常好的解决方案,肯特!我之前甚至不知道有 ObjectIdGenerator 这个工具 - 这将解决我的一些问题!真希望我能给你 +2 :) - Andrey

2

目前没有(至少不完全靠谱的)方法可以对匿名函数/委托进行哈希。即使函数实现相同,它可能是一个闭包 - 因此验证结果可能会根据上下文状态而有所不同。考虑以下示例:

public class Validator
{
    public string SomeState { get; set; }

    public Validator(string someState)
    {
        SomeState = someState;
    }

    public bool IsValid(string input)
    {
        return input == SomeState;
    }
}

// assume your 'input' being validated is "foo"
GetContent((new Validator("foo")).IsValid); // IsValid returns true
GetContent((new Validator("bar")).IsValid); // IsValid returns false

因此,确保验证函数的唯一性的唯一方法是让调用者定义验证实现的唯一性,并让调用者将该信息传递给您。您必须切换到使用某种验证器接口,类似于以下内容:

//
// Your code
//

public string GetContent(IValidator validator, 
    IEqualityComparer<IValidator> comparer)
{
    // for tracking used validators, use instance 
    // of 'new HashSet<IValidator>(comparer)'
    // this will give you a hashset of unique validators
}

public interface IValidator
{
    bool IsValid(string input);
}

//
// Your callers code
//

public class Validator : IValidator
{
    // same as Validator class code above
}

public class ValidatorEqualityComparer : IEqualityComparer<Validator>
{
    public bool Equals(Validator v1, Validator v2)
    {
        return GetHashCode(v1) == GetHashCode(v2);
    }

    public int GetHashCode(Validator v)
    {
        int hCode = GetMyStringHash(v.GetType().GUID.ToString() + v.SomeState);
        // as for GetMyStringHash() implementation for this example, 
        // you can use some simple string hashing: 
        // http://www.techlicity.com/blog/dotnet-hash-algorithms.html
        return hCode;
    }
}

然后,您可以像这样调用您的方法:
GetContent(new Validator("foo"), new ValidatorEqualityComparer());

因此,需要注意的最重要的部分是,在实现ValidatorEqualityComparer.GetHashCode()时,您需要使用验证器对象状态(基于对象值)哈希。只有这样才能确保验证逻辑的真正唯一性。

2

哈希值并不是唯一的。就等价性而言,你只能用它们来确定两个对象是否相同。因此,它们可以用作快速的第一次测试;如果哈希值不同,则没有必要进行任何进一步的比较;这两个对象不相同。如果哈希值相同,则这些对象可能相同,但也可能不同,因此您需要执行一些更深入的分析才能确定它们是否相等。


1

为什么不直接使用 HashSet 存储委托?然后你可以使用 .Contains(isValid) 来检查委托是否已经被给出。

换句话说,有人已经解决了这个问题。没有理由让你也去解决它。


-1

GetHashCode在不同对象之间的唯一性因子为2^122,这似乎非常安全。

否则,创建一个类,添加一个func属性和一个bool类型的HasBeenSeen。

应该能够完成工作。


2
这里有一些有趣的阅读材料:袜子、生日和哈希碰撞。引用:“无论如何,在大约9300次尝试后,你最终会有1%的碰撞几率,而在仅77000次尝试后就有50%的几率。” - Fredrik Mörk
哈哈,不错。我以前从未见过,但那是一篇非常好的文章。我推动将GUID重命名为GNUID。不太吸引人。 - Jake Kalstad
“相当安全”这种说法其实并不可取——如果你是认真从事软件工程的话,就不能接受“相当安全”作为一个原则。 - Ondrej Tucny
不,原帖作者想要检查他之前是否看到或没有看到委托,并且显然不能接受GetHashCode不是唯一的事实。因此,你的“相当安全”的推理是错误的。顺便说一下,我是雇主,所以我可以自由决定什么是浪费和什么不是;-) - Ondrej Tucny
我的观点是,以“相当安全”的方式进行操作意味着它们很可能会出现故障。确切的行为是首选(或者更好地说,必须具备),当然还需要简单、清晰、易于理解的解决方案。 - Ondrej Tucny
显示剩余2条评论

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