返回用于在C#中使用using的变量

11

我正在在 using 语句中创建变量,并在该语句内部返回它(听起来很有趣):

public DataTable foo ()
{
    using (DataTable properties = new DataTable())
    {
       // do something
       return properties;
    }
}

这会释放properties变量吗?

执行此操作后,仍然收到此警告:

警告 34 CA2000:Microsoft.Reliability:在方法“test.test”中,在所有引用它的范围之前在对象“properties”上调用System.IDisposable.Dispose。

有任何想法吗?

谢谢


3
不管怎样,这只是糟糕的设计,应该进行改进。 - Nick Larsen
7个回答

12

如果你想返回它,就不能将它装入using语句中,因为一旦离开大括号,它就会超出范围并被处理掉。

你需要像这样实例化它:

public DataTable Foo() 
{ 
    DataTable properties = new DataTable();
    return properties; 
} 

稍后要调用 Dispose() 方法。


4
感觉foo()等同于GetUsefulDataTable(),而using语句块应该调用这个函数。 - Nick Larsen

10

是的,它将销毁它 - 然后将其返回。这几乎总是不好的做法。

实际上对于 DataTableDispose 几乎从不做任何事情(异常情况是如果它被远程传输到其他地方),但这仍然是一个普遍的不好的想法。通常您应该将已释放对象视为不可用。


那么,如果要从方法中返回一个IDisposable对象而不触发CA2000警告,正确的模式是什么? - Jhonny D. Cano -Leftware-
@Jhonny:说实话,我不知道 - 我从未使用过这样的代码分析。我希望有一些方法可以抑制警告。 - Jon Skeet
@JhonnyD.Cano-Leftware- 如果您要实例化并返回IDisposable对象,则需要在代码中显式处理它们的释放。您的代码分析“应该”会检测到您手动在其他地方进行了释放。 - IanNorton

7

据说,这是创建一次性对象的工厂方法的模式。但是,我仍然看到代码分析也对此进行了抱怨:

        Wrapper tempWrapper = null;
        Wrapper wrapper = null;

        try
        {
            tempWrapper = new Wrapper(callback);
            Initialize(tempWrapper);

            wrapper = tempWrapper;
            tempWrapper = null;
        }
        finally
        {
            if (tempWrapper != null)
                tempWrapper.Dispose();
        }

        return wrapper;

这应该可以确保如果初始化失败,对象将被正确处理,但如果一切顺利,未处理的实例将从方法返回。
MSDN文章:CA2000:在失去范围之前处理对象

1
这不就相当于一个 catch 块吗?为什么不写成 Wrapper x = null; try { ... } catch { if (x != null) x.Dispose(); }。这样做的意图不仅更加明显,而且避免了不必要的临时变量和手动清理。 - Juliet
我并不反对。但是最近我刚查了一下这个问题,不是因为我担心在失败时处理对象,而是因为我正试图寻找能够消除 CA2000 警告的代码模式,而无需通过属性来抑制它。由于规则的本质,代码分析过程特别检查对象是否在 finally 块中被处理。我认为这个问题实际上是关于 CA2000,而不是关于处理对象的。 - Toby
1
@Juliet:catch语句缺少重新抛出,即使有重新抛出,语义也与没有catch不同。 其中,如果try块包含对某个可能引发异常的方法blah的两次调用,则捕获并重新抛出将导致堆栈跟踪显示重新抛出的行号而不是对blah的调用(blah内部的堆栈跟踪将是正确的,但调用的行号不会)。 - supercat

3

2

使用 using 块的目的是为值或对象创建一个人工范围。当 using 块完成时,因为不再需要该对象,所以会对其进行清理。如果你真的想要返回正在创建的对象,则不应使用 using。

这将完美地运行。

public DataTable foo ()
{
    DataTable properties = new DataTable();
    // do something
    return properties;
}

1

你使用 "using" 关键字编写的代码扩展为:

{
    DataTable properties = new DataTable();
    try
    {
        //do something
        return properties;
    }
    finally
    {
        if(properties != null)
        {
            ((IDisposable)properties).Dispose();
        }
    }
}

你的变量被 using 的工作方式所释放。如果你想要返回属性,不要将它包装在 using 块中。


0
其他的回答都是正确的:一旦你退出using块,你的对象就被释放了。using块非常适合确保对象及时被释放,所以如果你不想依赖于函数的使用者记得后续释放对象,可以尝试这样做:
public void UsingDataContext (Action<DataContext> action)
{
    using (DataContext ctx = new DataContext())
    {
       action(ctx)
    }
}

这样你就可以说:

var user = GetNewUserInfo();
UsingDataContext(c => c.UserSet.Add(user));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接