Pergunta

Eu tenho um método C # que aceita um predicado e retorna uma lista de itens correspondentes ...

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

O filtro será muitas vezes um de um conjunto comum ...

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

... mas pode ser um delegado anônimo.

Eu agora gostaria de ter este cache de métodos seus resultados no cache ASP.NET, chamadas assim repetidas com o mesmo delegado apenas retornar o resultado em cache. Para isso, eu preciso criar uma chave de cache do delegado. Will Delegate.GetHashCode () produzir resultados sensíveis para este fim? Existe algum outro membro do delegado que eu deveria olhar? Você faria isso de outra maneira inteiramente?

Foi útil?

Solução

Para executar a sua tarefa de cache, você pode seguir as outras sugestões e criar um Dictionary , List > (estático para, ou campo global membro de outra forma) que armazena em cache os resultados. Antes de executar o predicado , você precisa verificar se o resultado já existe no dicionário.

O nome geral para este cache função determinística é chamado Memoização - e seu impressionante:)

Desde que C # 3.0 adicionado lambda de e ganhos do de delegados Func / ação, acrescentando Memoização para C # é bastante fácil.

Wes Dyer tem um ótimo post que traz o conceito de C # com alguns bons exemplos.

Se você quer que eu te mostrar como fazer isso, deixe-me saber ... caso contrário, pós Wes' deve ser adequada.

Em resposta à sua pergunta sobre códigos de hash delegado. Se dois delegados são os mesmos, d1.GetHashCode () deve ser igual d2.GetHashCode (), mas eu não estou 100% sobre isso. Você pode verificar isso rapidamente, dando Memoização um ir, e adicionando um WriteLine em seu método FindAll. Se isso acaba não sendo verdade, outra opção é usar Linq.Expression > como um parâmetro. Se as expressões não são fechamentos, então expressões que fazem a mesma coisa deve ser igual.

Deixe-me saber como isso vai, eu estou interessado em saber a resposta sobre delegate.Equals.

Outras dicas

Delegar igualdade olha para cada invocação na lista de invocação, o teste para a igualdade de método a ser invocado, e alvo de método.

O método é um simples pedaço da chave de cache, mas o alvo do método (a instância de chamá-lo on - assumindo um método de instância) poderia ser impossível de cache de forma serializável. Em particular, para funções anônimas que afirmam captura, será uma instância de uma classe aninhada criado para capturar esse estado.

Se isto é tudo na memória, apenas manter o delegado-se como a chave da mistura vai ficar bem - embora isso pode significar que alguns objetos que os clientes seria de esperar para ser lixo coletado pendurar ao redor. Se você precisar serializar isso para um banco de dados, torna-se mais peludo.

Você poderia fazer o seu método de aceitar uma chave de cache (por exemplo, uma string) também? (Isso supondo um em cache de memória é insuficiente.)

Manter os resultados em cache em um Dictionary , List > é estranho para mim, porque eu quero o cache ASP.NET do termo punho para mim, em vez de cache todos os resultados para sempre, mas é de outra maneira uma boa solução. Acho que vou acabar indo com Dictionary , string> de Will para armazenar em cache uma cadeia que eu possa usar na chave de cache ASP.NET.

Alguns testes iniciais sugerem que a igualdade delegado faz a "coisa certa", como já foi dito, mas Delegate.GetHashCode é patologicamente inútil. Refletor revela

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

Assim, qualquer predicado retorna o mesmo resultado.

Meu problema restante era como funciona a igualdade para os delegados anônimos. O que significa "mesmo método chamado no mesmo alvo" significa, então? Parece que, enquanto o delegado foi definida no mesmo lugar, as referências são iguais. Delegados com o mesmo corpo definidas em lugares diferentes não são.

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
}

Isso deve ficar bem para minhas necessidades. Chamadas com os predicados predefinidos serão armazenados em cache. Várias chamadas para um método que chama FindAll com um método anônimo deve obter resultados em cache. Dois métodos de chamada FindAll com aparentemente o mesmo método anônimo não vai compartilhar os resultados em cache, mas esta deve ser bastante raro.

A menos que você tem certeza de implementação de GetHashCode do Delegado é determinista e não resulta em qualquer colisão eu não confiar nele.

Eis duas ideias. Primeiro, armazenar os resultados dos delegados dentro de um dicionário Predicado / List, utilizando o predicado como a chave e, em seguida, armazenar todo o dicionário de resultados sob uma única chave no cache. Bad coisa é que você perde todos os seus resultados em cache se o item de cache é perdido.

Uma alternativa seria criar um método de extensão para predicado, GetKey (), que usa um dicionário de objetos / string para armazenar e recuperar todas as chaves para todos os predicados. Você índice para o dicionário com o delegado e retornar sua chave, criando um se você não encontrá-lo. Desta forma, você está certo de que você está recebendo a chave correta por delegado e não há qualquer colisão. A um naiive seria tipo de nome + Guid.

A mesma instância de um objeto sempre retornará o mesmo hashcode (exigência de GetHashCode () em .net). Se os seus predicados estão dentro de uma lista estática e você não está redefinindo-los cada vez, eu não consigo ver um problema em usá-los como chaves.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top