Вопрос

У меня есть метод C #, который принимает предикат < Foo > и возвращает список подходящих элементов ...

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

Фильтр часто будет одним из общего набора ...

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

... но может быть анонимным делегатом.

Теперь я хотел бы, чтобы этот метод кэшировал свои результаты в кэше ASP.NET, поэтому повторные вызовы с одним и тем же делегатом просто возвращают кэшированный результат. Для этого мне нужно создать ключ кеша от делегата. Будет ли Delegate.GetHashCode () давать разумные результаты для этой цели? Есть ли какой-нибудь другой член Делегата, на которого я должен взглянуть? Вы бы сделали это совсем по-другому?

Это было полезно?

Решение

Чтобы выполнить задачу кэширования, вы можете следовать другим советам и создать словарь < Predicate < Foo >, List < Foo > < !> Гт (статическое для глобального или членское поле в противном случае), которое кэширует результаты. Перед выполнением предиката & Lt; Foo & Gt; необходимо проверить, существует ли результат в словаре.

Общее название для этой детерминированной функции кэширования называется Memoization - и это здорово:)

С тех пор, как в C # 3.0 были добавлены лямбда-выражения и замена делегатов Func / Action, добавить Memoization в C # довольно просто.

Уэс Дайер написал отличный пост это приводит концепцию к C # с некоторыми замечательными примерами.

Если вы хотите, чтобы я показал вам, как это сделать, дайте мне знать ... в противном случае пост Уэса должен быть адекватным.

В ответ на ваш запрос о хэш-кодах делегата. Если два делегата одинаковы, d1.GetHashCode () должен быть равен d2.GetHashCode (), но я не на 100% об этом. Вы можете быстро это проверить, запустив Memoization и добавив WriteLine в метод FindAll. Если это не соответствует действительности, другим вариантом является использование Linq.Expression & Lt; Predicate & Lt; Foo & Gt; & Gt; в качестве параметра. Если выражения не являются замыканиями, выражения, выполняющие одно и то же, должны быть равными.

Дайте мне знать, как это происходит, мне интересно узнать ответ о делегате. Уравнения.

Другие советы

Равенство делегатов просматривает каждый вызов в списке вызовов, проверяет равенство вызываемого метода и цель метода.

Метод представляет собой простую часть ключа кэша, но цель метода (экземпляр, для которого он вызывается - предполагается, что метод экземпляра) может быть невозможно кэшировать сериализуемым способом. В частности, для анонимных функций, которые фиксируют состояние, это будет экземпляр вложенного класса, созданного для захвата этого состояния.

Если все это находится в памяти, то можно просто сохранить сам делегат в качестве хеш-ключа - хотя это может означать, что некоторые объекты, которые клиенты ожидают собрать как мусор, зависают. Если вам нужно сериализовать это в базу данных, она становится более привлекательной.

Не могли бы вы также заставить свой метод принимать ключ кэша (например, строку)? (Предполагается, что кэш в памяти не подходит.)

Сохранение кэшированных результатов в словаре < Predicate < Foo >, List < Foo > > это неудобно для меня, потому что я хочу, чтобы кэш ASP.NET обрабатывал истечение для меня, а не кэшировал все результаты навсегда, но в остальном это хорошее решение. Я думаю, что в итоге я перейду к Словарю Уилла & Lt; Predicate & Lt; Foo & Gt;, string & Gt; кэшировать строку, которую я могу использовать в ключе кэша ASP.NET.

Некоторые первоначальные тесты предполагают, что равенство делегатов выполняет " правильную вещь " как уже говорили другие, но Delegate.GetHashCode патологически бесполезен. Отражатель раскрывает

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

Так что любой предикат < Foo > возвращает тот же результат.

Моя остающаяся проблема заключалась в том, как работает равенство для анонимных делегатов. Что означает & Quot; один и тот же метод вызывается для той же цели & Quot; значит тогда? Кажется, что, пока делегат был определен в том же месте, ссылки равны. Делегаты с одинаковым телом, определенным в разных местах, не являются.

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 с, по-видимому, одним и тем же анонимным методом, не будут совместно использовать кэшированные результаты, но это должно быть довольно редко.

Если вы не уверены, что реализация метода GetHashCode в Делегате является детерминированной и не приводит к каким-либо коллизиям, я бы ей не поверил.

Вот две идеи. Сначала сохраните результаты делегатов в словаре Predicate / List, используя предикат в качестве ключа, а затем сохраните весь словарь результатов под одним ключом в кэше. Плохо то, что вы потеряете все свои кэшированные результаты, если элемент кэша потерян.

Альтернативой может быть создание метода расширения для Predicate, GetKey (), который использует словарь объектов / строк для хранения и извлечения всех ключей для всех предикатов. Вы индексируете в словаре делегата и возвращаете его ключ, создавая его, если не нашли его. Таким образом, вы уверены, что получаете правильный ключ для каждого делегата, и нет никаких коллизий. Наивным будет имя типа + Guid.

Один и тот же экземпляр объекта всегда будет возвращать один и тот же хэш-код (требование GetHashCode () в .Net). Если ваши предикаты находятся внутри статического списка, и вы не переопределяете их каждый раз, я не вижу проблемы в использовании их в качестве ключей.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top