Да, обработчики событий в основном будут вызывать утечку, если не будут удалены. Чтобы получить почти одинарный строковый эквивалент того, что вы выражаете в своем коде, и удалить обработчики, вам понадобится экземпляр некоторого класса, который представляет полный жизненный цикл вызова и занимается уборкой.
Я создал класс Caller<TResult>
, который использует базовый клиентский прокси WCF, следуя этому базовому шаблону:
- создание
Caller
экземпляра вокруг существующего или нового клиентского прокси (жизненный цикл прокси находится за пределами объема выполняемого вызова (так что вы можете использовать новый недолговечный или существующий долгоживущий) .
- использует одну из
Caller
различных перегрузок CallAsync<TArg [,...]>
, чтобы указать асинхронный метод для вызова, и предназначенный обратный вызов для вызова после завершения. Этот метод выберет асинхронный метод, который также принимает параметр state . Параметром состояния будет сам экземпляр Caller
.
- Я говорю предназначен , потому что обработчик real , который будет подключен, будет выполнять немного больше служебных операций. Реальный обратный вызов - это то, что будет вызываться в конце асинхронного вызова, и будет
- проверьте, что
ReferenceEquals(e.UserState, this)
в вашем реальном обработчике
- если не верно, немедленно вернуть (событие не было задумано как результат этого конкретного вызова и должно быть проигнорировано; это очень важно, если ваш прокси долгоживущий)
- в противном случае немедленно удалите реальный обработчик
- Позвоните на ваш предполагаемый, фактический обратный звонок с
e.Result
- Измените реальный обработчик
Caller
, чтобы выполнить требуемый обратный вызов в нужном потоке (более важно для WPF, чем Silverlight)
Вышеприведенная реализация также должна иметь отдельные обработчики для случаев, когда e.Error
не равен нулю или e.Cancelled
равно true. Это дает вам преимущество не проверять эти случаи в вашем предполагаемом обратном вызове. Возможно, ваши перегрузки принимают дополнительные обработчики для этих случаев.
В любом случае, вы в конечном итоге начнете агрессивно очищать обработчики за счет некоторой проводки для каждого вызова. Это немного дороже за звонок, но при правильной оптимизации все равно оказывается намного дешевле, чем по вызову WCF по проводному каналу.
Вот пример вызова с использованием класса (вы заметите, что во многих случаях я использую группы методов для повышения читабельности, хотя HandleStuff
могло бы быть result =>
использовать результат )
Первая группа методов важна, поскольку CallAsync
получает владельца этого делегата (т. Е. Экземпляра службы), который необходим для вызова метода; в противном случае служба может передаваться как отдельный параметр).
Caller<AnalysisResult>.CallAsync(
// line below could also be longLivedAnalyzer.AnalyzeSomeThingsAsync
new AnalyzerServiceClient().AnalyzeSomeThingsAsync,
listOfStuff,
HandleAnalyzedStuff,
// optional handlers for error or cancelled would go here
onFailure:TellUserWhatWentWrong);