Does 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:
вы можете написать итератор производительности, который возвратил один результат из родительского итератора
Да, я думаю, что это поможет работать с 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 ...)
Но нужно ли это?
Ответы
Ответ 1
Вообще говоря, во многих случаях вызов Dispose() не вызывает больших проблем: хорошо написанные одноразовые объекты будут реализовывать ту же логику, которая необходима для очистки вещей в финализаторе.
(Отказ от ответственности: я не говорю "не вызывайте dispose": он существует по какой-то причине! Например, Finalization может произойти намного позже. Я только описываю, каковы последствия здесь).
Однако объекты AD являются заметным исключением; в частности, SearchResultCollection
известен из-за этой проблемы (ссылки: MSDN (как документы класса, так и другие статьи) и Active Directory: проектирование, развертывание и запуск Active Directory). Кажется, что по техническим причинам невозможно освободить ресурсы в своем финализаторе, поэтому не вызывать dispose приведет к утечкам памяти.
Как указано Скоттом и Джо, многие примеры MSDN не вызывают удаление предметов в коллекции; однако, Райан Данн, бывший Технический евангелист Windows Azure и соавтор Руководства разработчика .NET для программирования служб каталогов, рекомендует использовать для вызова на каждый элемент в этом сообщении в блоге. Из сообщения:
В общем, всегда явно вызывать Dispose() для следующих типов объектов:
- DirectoryEntry
- SearchResultCollection (from.FindAll())
- DirectorySearcher (если вы явно не установили SearchRoot)
Это самое близкое, что вы можете иметь к "авторитетному источнику", я полагаю; однако мое личное мнение:
- если вы можете, выполните вызов dispose. Это не будет плохо, особенно если вы можете вернуть функциональность LINQ с помощью метода расширения Joe
- пойдите и используйте рефлектор /ilspy/ildasm И/ИЛИ профиль памяти, такой как dotTrace, чтобы действительно увидеть, что происходит (в основном, что Скотт уже сделал, но глубже)
Ответ 2
Я изначально пришел на сайт, чтобы задать тот же вопрос, но, видя, что ваш вопрос дал мне мотивацию вырваться 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 для вас на каждом члене.
Итак, да, он уничтожает все члены, а затем некоторые.