返回一个实现IDisposable的对象是不是一个好主意?

时间:2011-09-14 06:28:44

标签: .net idisposable dispose

我的情况就是这样。

我正在使用System.DirectoryServices.AccountManagement来处理AD用户和组。我的Main方法(现在是一个控制台应用程序)调用一个返回PrincipalSearchResult<Principal>的方法。这两个对象都实现了IDisposable。

如果我退回,我怎样才能确保我可以丢弃所有这些一次性物品?

class Searcher
{    
    private PrincipalSearchResult<Principal> SearchForObjects(string searchString)
    {
        PrincipalContext ctx = null;
        PrincipalSearcher principalSearcher = null;
        Principal principal = null;

        ctx = new PrincipalContext(ContextType.Domain, "blah", "dc=blah,dc=com");

        principal = new GroupPrincipal(ctx) { Name = searchString };
        principalSearcher = new PrincipalSearcher { QueryFilter = principal };

        return principalSearcher.FindAll();
    }
}

static void Main(string[] args)
{
    Searchers searchers = new Searchers();

    PrincipalSearchResult<Principal> theGroups = searchers.SearchForGroupsMatching("some*");

    foreach (GroupPrincipal group in theGroups)
    {
        Console.WriteLine(group.DisplayName);
        // do stuff...
    }
}

由于与无人对象处理有关的原因,将PrincipalSearchResult<Principal>传回一个非常糟糕的主意?

我最好创建一个托管代理对象吗?

我想如果我只关心阅读属性的子集,那么创建仅包含这些属性的自定义对象可能“更好”。当回写时,在这种情况下回到AD组,我真正需要传递给方法的是改变的属性和密钥。这将允许我将非托管对象的创建约束到一个范围。或者所有这些都是不必要的,而且比它的价值更麻烦?我为半散的意识流道歉......

4 个答案:

答案 0 :(得分:2)

不,这不是一个坏主意。 .NET框架中有很多例子,例如, SqlClient.ExecuteReader

但是,您需要记录用户在完成使用后需要立即处理该对象。

你会这样做:

using(PrincipalSearchResult<Principal> theGroups = 
      searchers.SearchForGroupsMatching("some*"))
{
    foreach (GroupPrincipal group in theGroups)
    {
        Console.WriteLine(group.DisplayName);
        // do stuff...
    }
}

答案 1 :(得分:1)

返回实现IDisposable的类型不是错误,但这很不方便。

如果你知道你的调用者不需要任何超过几个简单属性的东西,那么创建和返回代理对象有很多东西要说 - 这可以简化调用者的代码并保证非托管资源是处理得当。

在你的情况下,如果你真的只想要DisplayName属性,那么我肯定会创建一个代理对象。那么你根本不需要担心PrincipalSearchResult<Principal>的生命周期,更关键的是,任何连接或者该对象保持打开的原因。

这也简化了测试:你可以很容易地模拟/删除返回代理的方法调用,因为代理对象是你的。

但是,如果您的调用者代码将使用需要完整PrincipalSearchResult<Principal>的方法,那么代理显然不会这样做。

答案 2 :(得分:1)

返回一个实现IDisposable的对象是一个同时是icky,有用的模式,在某种意义上说是不可避免的。让工厂仅将新创建的项目作为返回值提供可能会导致在对象执行了需要清理的操作之后但在将其存储在捕获到异常时仍然存在的位置之前可能抛出异常的危险

更安全的模式是通过引用传递工厂存储正在创建的对象的位置。在该模式之后,如果工厂在构建新对象之后存储了对新对象的引用,并且在完成任何需要清理的操作之前,那么调用工厂的代码可以在新创建的对象上调用Dispose在此之后的任何时候都会抛出异常。

实际上,除了两个因素外,我认为后一种对象构造方式要远远优于前者:

  1. “using”构造允许一个很好的清晰语法来自动化清理代码,它不会真正适用于那种工厂风格(人们可以使用它,但不能以“使用”方式清理它部分构造的对象,如果构造函数抛出)。
  2. 直接调用构造函数的常规方法是将新实例作为返回值提供给调用代码,如果构造函数抛出异常,则返回值消失。每个对象创建最终归结为对“new()”的调用,因此完全避免使用函数返回值的唯一方法是对IDisposable的唯一现有引用是没有类暴露任何公共构造函数,并且所有其private和protected构造函数接受一个引用参数,在该参数中可以走私新的对象实例。

让函数返回对新构造的IDisposable实例的唯一现有引用并不是很好,但它是一个非常常见的模式 - 因为 - 现在 - c#和.net都没有真正提供更好的东西。

答案 3 :(得分:0)

调用代码应该使用using块写入,这将确保调用Dispose。 IDisposable是 控制何时发生资源处置的方式,因此返回实现IDisposable的对象是标准习惯用法。