Располагает ли PrincipalSearchResult<T> автоматически все элементы в своей коллекции?
Ничего не могу найти в документации MSDN по этому вопросу.
Т.е. этого достаточно, сказать:
using(PrincipalSearcher searcher = ...)
{
foreach (var principal in searcher.FindAll())
{
... do something ...
} // The PrincipalSearchResult<T> returned by searcher.FindAll is disposed here
}
что делает большинство примеров, которые я видел, или я должен делать:
using(PrincipalSearcher searcher = ...)
{
foreach(var principal in searcher.FindAll())
{
using (principal)
{
// ... do something ...
}
}
}
Последний (явное удаление каждого элемента во время итерации) выглядит "более безопасным" - то есть соответствует директиве по явному удалению всех IDisposable объектов - но это немного грязно; например, это исключает использование LINQ для перебора результатов поиска.
В ответ на комментарий @Rup:
Вы могли бы написать итератор yield, который возвратил один результат из родительского итератора
Да, я думаю, что это будет работать, чтобы включить LINQ. Что-то вроде следующего метода расширения:
public static IEnumerable<T> EnumerateAndDispose<T>(this IEnumerable<T> collection) where T : IDisposable
{
foreach (T item in collection)
{
using (item)
{
yield return item;
}
}
}
который можно использовать как:
searcher.FindAll().EnumerateAndDispose().Select(... use LINQ ...)
Но нужно ли это?
2 ответа
Вообще говоря, во многих случаях отказ от вызова Dispose() не вызовет больших проблем: хорошо написанные одноразовые объекты будут реализовывать ту же логику, которая необходима для очистки вещей в финализаторе. (Отказ от ответственности: я не говорю "не звоните распоряжаться": это происходит по причине! Например, Завершение может произойти намного позже. Я только описываю, каковы последствия здесь).
Однако объекты AD являются заметным исключением; особенно, SearchResultCollection
известен тем, что страдает от этой проблемы (ссылки: MSDN (как документация по классу и другие статьи), так и Active Directory: проектирование, развертывание и запуск Active Directory). Кажется, что по техническим причинам невозможно освободить ресурсы в его финализаторе, поэтому отсутствие вызова dispose приведет к утечкам памяти.
Как отмечают Скотт и Джо, многие примеры MSDN не вызывают dispose для элементов в коллекции; однако Райан Данн, бывший технический евангелист Windows Azure и соавтор Руководства для разработчиков.NET по программированию служб каталогов, рекомендует использовать функцию dispose для каждого элемента в этом сообщении в блоге. Из поста:
В общем, всегда явно вызывайте Dispose() для следующих типов объектов:
- DirectoryEntry
- SearchResultCollection (из.FindAll ())
- DirectorySearcher (если вы явно не установили SearchRoot)
Я думаю, это самое близкое к "авторитетному источнику"; однако мое личное мнение таково:
- если можете, позвоните утилизировать. Это не сделает ничего плохого, особенно если вы можете вернуть функциональность LINQ с помощью метода расширения Джо.
- иди и используй отражатель /ilspy/ildasm И / ИЛИ профиль памяти, такой как dotTrace, чтобы реально увидеть, что происходит (в основном то, что Скотт уже сделал, но углубляется)
Первоначально я пришел на сайт, чтобы задать тот же вопрос, но видение вашего вопроса дало мне мотивацию разжечь ILSpy и выяснить, стоит ли это делать.
Первый Функция удаления результатов поиска:
// System.DirectoryServices.AccountManagement.PrincipalSearchResult<T>
public void Dispose()
{
if (!this.disposed)
{
if (this.resultSet != null)
{
lock (this.resultSet)
{
this.resultSet.Dispose();
}
}
this.disposed = true;
}
}
Оттуда я проверил resultSet.Dispose()
(в моем случае resultSet был ADDNLinkedAttrSet
)
// System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet
public override void Dispose()
{
try
{
if (!this.disposed)
{
if (this.primaryGroupMembersSearcher != null)
{
this.primaryGroupMembersSearcher.Dispose();
}
if (this.queryMembersResults != null)
{
this.queryMembersResults.Dispose();
}
if (this.currentMembersSearcher != null)
{
this.currentMembersSearcher.Dispose();
}
if (this.memberSearchResults != null)
{
this.memberSearchResults.Dispose();
}
if (this.memberSearchersQueue != null)
{
foreach (DirectorySearcher directorySearcher in this.memberSearchersQueue)
{
directorySearcher.Dispose();
}
this.memberSearchersQueue.Clear();
}
IDisposable disposable = this.members as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
IDisposable disposable2 = this.membersEnum as IDisposable;
if (disposable2 != null)
{
disposable2.Dispose();
}
if (this.membersQueue != null)
{
foreach (IEnumerable enumerable in this.membersQueue)
{
IDisposable disposable3 = enumerable as IDisposable;
if (disposable3 != null)
{
disposable3.Dispose();
}
}
}
if (this.foreignGroups != null)
{
foreach (GroupPrincipal groupPrincipal in this.foreignGroups)
{
groupPrincipal.Dispose();
}
}
this.disposed = true;
}
}
finally
{
base.Dispose();
}
}
Вы можете видеть циклы foreach, где он проходит по всем имеющимся элементам. Так что это делает Dispose для вас на каждом члене.
Так что, да, он располагает всеми членами, а затем и некоторыми.