Pregunta

Tengo un método C # que acepta un predicado < Foo > y devuelve una lista de elementos coincidentes ...

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

El filtro a menudo será uno de un conjunto común ...

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

... pero puede ser un delegado anónimo.

Ahora me gustaría que este método guarde en caché sus resultados en el caché ASP.NET, por lo que las llamadas repetidas con el mismo delegado solo devuelven el resultado almacenado en caché. Para esto, necesito crear una clave de caché del delegado. ¿Delegate.GetHashCode () producirá resultados sensibles para este propósito? ¿Hay algún otro miembro del Delegado al que deba mirar? ¿Harías esto de otra manera por completo?

¿Fue útil?

Solución

Para realizar su tarea de almacenamiento en caché, puede seguir las otras sugerencias y crear un Diccionario < Predicate < Foo >, List < Foo > < !> gt; (estático para el campo global o miembro de lo contrario) que almacena en caché los resultados. Antes de ejecutar realmente el Predicate & Lt; Foo & Gt ;, necesitaría verificar si el resultado ya existe en el diccionario.

El nombre general para el almacenamiento en caché de esta función determinista se llama Memoization, y es increíble :)

Desde que C # 3.0 agregó lambda's y el botín de delegados de Func / Action, agregar Memoization a C # es bastante fácil.

Wes Dyer tiene una gran publicación eso lleva el concepto a C # con algunos excelentes ejemplos.

Si quieres que te muestre cómo hacer esto, avísame ... de lo contrario, la publicación de Wes debería ser adecuada.

En respuesta a su consulta sobre delegar códigos hash. Si dos delegados son iguales, d1.GetHashCode () debería ser igual a d2.GetHashCode (), pero no estoy al 100% al respecto. Puede verificar esto rápidamente al probar Memoization y agregar una WriteLine a su método FindAll. Si esto no es cierto, otra opción es usar Linq.Expression & Lt; Predicate & Lt; Foo & Gt; & Gt; como un parámetro Si las expresiones no son cierres, entonces las expresiones que hacen lo mismo deberían ser iguales.

Déjame saber cómo va esto, me interesa saber la respuesta sobre delegate.Equals.

Otros consejos

La igualdad de delegado examina cada invocación en la lista de invocación, probando la igualdad del método a invocar y el objetivo del método.

El método es una parte simple de la clave de caché, pero el objetivo del método (la instancia para llamarlo, suponiendo un método de instancia) podría ser imposible de almacenar en caché de forma serializable. En particular, para las funciones anónimas que capturan el estado, será una instancia de una clase anidada creada para capturar ese estado.

Si todo esto está en la memoria, solo mantener el delegado como la clave hash estará bien, aunque puede significar que algunos objetos que los clientes esperarían que se recolectaran basura se quedan. Si necesita serializar esto en una base de datos, se vuelve más complicado.

¿Podría hacer que su método acepte también una clave de caché (por ejemplo, una cadena)? (Eso supone que una memoria caché en memoria es inadecuada).

Mantener los resultados almacenados en caché en un Diccionario < Predicar < Foo >, List < Foo > > es incómodo para mí porque quiero que el caché ASP.NET maneje la caducidad para mí en lugar de almacenar en caché todos los resultados para siempre, pero de lo contrario es una buena solución. Creo que terminaré con el Diccionario de Will & Lt; Predicate & Lt; Foo & Gt;, string & Gt; para almacenar en caché una cadena que puedo usar en la clave de caché ASP.NET.

Algunas pruebas iniciales sugieren que delegar igualdad hace " lo correcto " como otros han dicho, pero Delegate.GetHashCode es patológicamente inútil. Reflector revela

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

Entonces cualquier predicado < Foo > devuelve el mismo resultado.

Mi problema restante era cómo funciona la igualdad para los delegados anónimos. ¿Qué significa & Quot; mismo método invocado en el mismo objetivo & Quot; significa entonces? Parece que mientras el delegado se definió en el mismo lugar, las referencias son iguales. Los delegados con el mismo cuerpo definido en diferentes lugares no lo son.

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
}

Esto debería estar bien para mis necesidades. Las llamadas con los predicados predefinidos se almacenarán en caché. Las llamadas múltiples a un método que llama a FindAll con un método anónimo deberían obtener resultados en caché. Dos métodos que llaman a FindAll con aparentemente el mismo método anónimo no compartirán los resultados almacenados en caché, pero esto debería ser bastante raro.

A menos que esté seguro de que la implementación del delegado de GetHashCode es determinista y no genera colisiones, no confiaría en ella.

Aquí hay dos ideas. Primero, almacene los resultados de los delegados dentro de un diccionario Predicate / List, utilizando el predicado como clave, y luego almacene todo el diccionario de resultados bajo una sola clave en el caché. Lo malo es que pierde todos los resultados almacenados en caché si se pierde el elemento de caché.

Una alternativa sería crear un método de extensión para Predicate, GetKey (), que utiliza un diccionario de objetos / cadenas para almacenar y recuperar todas las claves para todos los Predicados. Se indexa en el diccionario con el delegado y devuelve su clave, creando una si no la encuentra. De esta manera, tiene la seguridad de que está obteniendo la clave correcta por delegado y no hay colisiones. Una ingenua sería type name + Guid.

La misma instancia de un objeto siempre devolverá el mismo código hash (requisito de GetHashCode () en .Net). Si sus predicados están dentro de una lista estática y no los está redefiniendo cada vez, no veo ningún problema en usarlos como claves.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top