我有一个C#方法接受谓词<!> lt; Foo <!> gt;并返回匹配项列表...

public static List<Foo> FindAll( Predicate<Foo> filter )
{
    ...
}

过滤器通常是常见的一组......

public static class FooPredicates
{
    public static readonly Predicate<Foo> IsEligible = ( foo => ...)
    ...
}

...但可能是匿名代表。

我现在想让这个方法将其结果缓存在ASP.NET缓存中,因此使用相同委托的重复调用只返回缓存的结果。为此,我需要从委托创建一个缓存键。 Delegate.GetHashCode()会为此目的产生明智的结果吗?我应该看一下代表团的其他成员吗?你会完全这样做吗?

有帮助吗?

解决方案

要执行缓存任务,您可以按照其他建议创建一个词典<!> lt; Predicate <!> lt; Foo <!> gt ;, List <!> lt; Foo <!> gt; < !> GT; (用于全局的静态,否则为成员字段),用于缓存结果。在实际执行Predicate <!> lt; Foo <!> gt;之前,您需要检查结果是否已存在于字典中。

这个确定性函数缓存的通用名称叫做Memoization - 它真棒:)

自从C#3.0添加了lambda和Func / Action委托之后,向C#添加Memoization非常容易。

Wes Dyer有精彩帖子通过一些很好的例子将这个概念引入C#。

如果您希望我告诉您如何操作,请告诉我...否则,Wes的帖子应该足够了。

回答有关委托哈希码的查询。如果两个委托是相同的,d1.GetHashCode()应该等于d2.GetHashCode(),但我不是100%关于这个。您可以通过给Memoization一个快速检查,并在FindAll方法中添加WriteLine。如果这最终不成立,另一种选择是使用Linq.Expression <!> lt; Predicate <!> lt; Foo <!> gt; <!> gt;作为参数。如果表达式不是闭包,那么执行相同操作的表达式应该相等。

让我知道这是怎么回事,我很想知道关于delegate.Equals的答案。

其他提示

委托相等性查看调用列表中的每个调用,测试要调用的方法的相等性以及方法的目标。

该方法是缓存键的一个简单部分,但该方法的目标(调用它的实例 - 假设实例方法)可能无法以可序列化的方式缓存。特别是,对于捕获状态的匿名函数,它将是为捕获该状态而创建的嵌套类的实例。

如果这一切都在内存中,只需将委托本身保留为散列键就可以了 - 虽然这可能意味着某些客户端希望被垃圾回收的对象会挂起。如果你需要将它序列化到一个数据库,它会变得更加毛茸茸。

您是否可以让您的方法接受缓存键(例如字符串)? (假设内存缓存不足。)

将缓存的结果保存在Dictionary <!> lt; Predicate <!> lt; Foo <!> gt ;, List <!> lt; Foo <!> gt; <!> gt;对我来说很尴尬,因为我希望ASP.NET缓存为我处理到期而不是永远缓存所有结果,但它是一个很好的解决方案。我想我最终将使用Will的词典<!> lt; Predicate <!> lt; Foo <!> gt ;, string <!> gt;缓存我可以在ASP.NET缓存键中使用的字符串。

一些初步测试表明,委托等式执行<!>“;正确的事情<!>”;正如其他人所说,但Delegate.GetHashCode在病理上是无益的。反射器显示

public override int GetHashCode()
{
    return base.GetType().GetHashCode();
}

所以任何谓词<!> lt; Foo <!> gt;返回相同的结果。

我剩下的问题是匿名代表的平等运作方式。 <!>相同的方法在同一个目标<!>上调用了什么?那么呢?似乎只要委托在同一个地方定义,引用就是平等的。具有在不同地方定义的相同机构的代表不是。

static Predicate<int> Test()
{
    Predicate<int> test = delegate(int i) { return false; };
    return test;
}

static void Main()
{
    Predicate<int> test1 = Test();
    Predicate<int> test2 = Test();
    Console.WriteLine(test1.Equals( test2 )); // True

    test1 = delegate(int i) { return false; };
    test2 = delegate(int i) { return false; };
    Console.WriteLine(test1.Equals( test2 )); // False
}

这应该可以满足我的需求。将缓存具有预定义谓词的调用。对一个使用匿名方法调用FindAll的方法的多次调用应获得缓存结果。调用具有明显相同匿名方法的FindAll的两种方法不会共享缓存结果,但这应该是相当罕见的。

除非你确定Delegate的GetHashCode实现是确定性的并且不会导致任何冲突,否则我不相信它。

这是两个想法。首先,使用谓词作为键,将委托的结果存储在Predicate / List字典中,然后将整个结果字典存储在缓存中的单个键下。糟糕的是,如果缓存项丢失,则会丢失所有缓存的结果。

另一种方法是为Predicate,GetKey()创建一个扩展方法,它使用一个对象/字符串字典来存储和检索所有Predicates的所有键。使用委托索引到字典并返回其密钥,如果找不到,则创建一个。通过这种方式,您可以确保每个代表获得正确的密钥,并且没有任何冲突。一个naiive将是类型名称+ Guid。

对象的同一实例将始终返回相同的哈希码(.Net中的GetHashCode()要求)。如果您的谓词位于静态列表中并且您不是每次都重新定义它们,我就不会发现将它们用作键的问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top