问题 PrincipalSearchResult 是否自动处理其集合中的所有元素?


在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 ...)

但是有必要吗?


2744
2018-05-23 07:16


起源

有趣。要解决LINQ的情况,如果你肯定知道这些项目一次只能使用一个你可以编写一个yield迭代器,它从父迭代器返回一个结果但保留了对它的引用,然后在返回下一个之前处理它。 - Rup
@Joe,你是未来的我吗?我是来这里的 精确 与你相同的问题,仅仅8个小时之后。 - Scott Chamberlain
@Scott,不,我是你在一个平行的宇宙中。无法在那里得到答案,所以我想我会在这里尝试:) - Joe
@Joe我今天又跑到这里看到你仍然没有你的权威答案。我也想知道,所以我提出了赏金。 - Scott Chamberlain
关于beaviour的文档关闭的事情在这里: msdn.microsoft.com/en-us/library/y1c0575c.aspx 是否声称Dispose“释放SearchResultCollection对象使用的所有资源”。但是,应该证明a)PrincipalSearchResult <T>确实是一个SearchResultCollection(可能,但不确定); b)“所有资源”也包括这些项目。 - Lorenzo Dematté


答案:


通常情况下,在许多情况下,不调用Dispose()不会导致大问题:写得好的一次性对象将实现在终结器中清理所需的相同逻辑。 (免责声明:我不是说“不要召唤处理”:这是有原因的!例如,终结可能会发生很多次。我只是在描述这里的后果)。

但是,AD对象是一个值得注意的例外;尤其是, SearchResultCollection 以解决这个问题而闻名(参考文献:MSDN(包括类文档和其他文章),以及 Active Directory:设计,部署和运行Active Directory)。似乎由于技术原因,不可能在其终结器中释放资源,因此不调用dispose将导致内存泄漏。

正如Scott和Joe所指出的,许多MSDN示例都没有对集合中的项目进行dispose调用;然而,Ryan Dunn,前Windows Azure技术布道师和合着者 .NET开发人员目录服务编程指南,建议用来对每个项目进行处理 这篇博文。从帖子:

通常,始终在以下对象类型上显式调用Dispose():

  • 的DirectoryEntry
  • SearchResultCollection(来自.FindAll())
  • DirectorySearcher(如果您尚未明确设置SearchRoot)

我相信这是你对“权威来源”最接近的; 然而 我的个人意见是:

  • 如果可以,请拨打电话。它不会有任何不好,特别是如果你可以使用Joe的扩展方法获得LINQ功能
  • 去使用reflector / ilspy / ildasm和/或内存配置文件 dotTrace 真正看到发生了什么(基本上,斯科特已经做了什么,但更深入)

12
2018-01-31 13:46



谢谢!我认为这与我们将获得的权威一样。你得到了400。 - Scott Chamberlain
@ScottChamberlain谢谢!我很乐意帮忙 - Lorenzo Dematté


我最初来到网站问同样的问题,但看到你的问题给了我突破的动力 ILSpy 并弄清楚自己是否确实这样做了。

首先搜索结果的dispose功能:

// 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。

所以,是的它确实处理了所有成员,然后是一些成员。


4
2018-05-23 16:21



感谢你的回答。我也看了一下实现,但得出了与你不同的结论。 (1)ResultSet是抽象的,因此需要检查每个具体的实现; (2)我可以从上面看到ADDNLinkedAttrSet可能正在处理它当前拥有的项目,但我不确定已经返回给调用者的项目; (3)我希望看到记录的行为(即PrincipalSearcher实施的合同的一部分),而不是依赖于我对内部实施细节的解释。 - Joe