使用"using"的方法出现CA2000警告,但未使用try/finally

3

我遇到了一个奇怪的情况,想要理解。这段代码产生了CA2000 (在所有引用之前调用对象的Dispose方法)

var ms = new MemoryStream(Encoding.Default.GetBytes(DefaultControlTemplateXaml));
using(ms)
{
    var x = XamlReader.Load(ms);
    _defaultControlTemplate = x as ControlTemplate;
}

然而,另一部分并非如此:
var ms = new MemoryStream(Encoding.Default.GetBytes(DefaultControlTemplateXaml));
try
{
    var x = XamlReader.Load(ms);
    _defaultControlTemplate = x as ControlTemplate;
}
finally { ms.Dispose(); }

根据Microsoft的文档

使用using语句确保在调用对象方法时发生异常时仍会调用Dispose。您可以通过将对象放入try块中,然后在finally块中调用Dispose来实现相同的结果;实际上,这就是编译器如何翻译using语句。

所以我真的迷失了...这两个语句不应该是一样的吗?

更新

由于人们坚持(没有阅读我的评论)解释using规范。我会这样说:

  using (var ms = new MemoryStream(Encoding.Default.GetBytes(DefaultControlTemplateXaml)))
  {
    var x = XamlReader.Load(ms);
    _defaultControlTemplate = x as ControlTemplate;
  }

这个在fxcop上仍然会报CA2000的错误,因此原始问题仍然存在。

更新2

添加一些屏幕截图,以便您可以看到这是Visual Studio 2010并且包含整个函数。

第一个版本(有警告): With warning

第二个版本(没有问题): Correct build


当然,我知道在using括号内实例化IDisposable是最佳实践,但我将其放在前面,以使语句尽可能相同。 - Jcl
@xanatos:这也是我的想法,但是我查看了C#语言规范,它指出对象是在try块之外创建的。 - Martin Liversage
@LasseV.Karlsen 我正在使用VS2010并收到警告。 - Jcl
@Jcl:我无法在您的Update 2代码中重现CA2000警告的第一个版本。您是否在运行未安装SP1或针对.NET Framework版本低于4.0的VS 2010? - Nicole Calinoiu
@Nicole,不是的,这是针对.NET 4.0的SP1...这一定是由其他原因引起的错误。我已经在另一个项目上测试了一些类似于这样的孤立代码,并且没有发出警告。我在Connect上投票了一个错误报告 - Jcl
显示剩余2条评论
3个回答

4
您可能正在遇到CA2000报告的误报。您可以在Microsoft Connect中搜索CA2000。有许多问题(并非所有问题都是误报错误)。
个人而言,由于这些误报,我已经在一些项目中关闭了CA2000。我使用带代码分析的Visual Studio 2010,并且我刚刚验证过,在太多次抑制CA2000之后,我们决定在我现在正在工作的项目中关闭它。

1
但是,“using”根本不能解决您提到的问题。 “using”并不神奇;假设在分配资源之后,变量ms被分配引用以保存值的对象之前发生ThreadAbortException。在这种情况下,“using”的finally块可能会做什么呢?该变量从未初始化,因此仍具有其默认的null值,因此finally块无法处理它。事实上,“using”只是为了礼貌而已;它是为了做出“善意”的努力来释放资源。这就是线程中止如此糟糕的原因。 - Eric Lippert
@EricLippert Fxcop应该分析代码输出,如果微软文档(我提供的链接)说两个语句由编译器产生相同的输出,那么我看不出一个发出代码分析警告的原因,而另一个没有发出 :-( - Jcl
@MartinLiversage 这个关于Microsoft Connect的问题(http://connect.microsoft.com/VisualStudio/feedback/details/469815/code-analysis-warning-ca2000-when-using-the-using-c-construct#details)听起来像是我现在遇到的问题。我似乎无法登录Connect进行投票... 我暂时就这样,只使用try/finally(我并不反对...我只是想知道为什么会发生这种情况)。谢谢,Microsoft Connect的链接很有帮助。 - Jcl
@Jcl:我没有任何头绪为什么FXCOP会或不会报告任何特定的警告;你需要找一个知道FXCOP的人来解决这个问题,而那个人不是我。我的评论根本不是为了回答你的问题;它是为了回答Martin错误的说法,即“using”块在面对线程中止异常时是健壮的;它绝对不是。使用“using”块不能保证已分配的资源实际上是否被释放;线程可能会在分配资源后立即中止。 - Eric Lippert
@EricLippert 哦,是的,我把你的评论误读成针对我的问题了,现在明白多了,对此我感到很抱歉。 - Jcl
已被接受为答案,因为它似乎确实是一个错误(我在Connect上点赞并发表了评论)。在其他完全相同的项目中无法复现...肯定是其他原因导致的,但由于该代码不应该(也没有)发出警告,所以这一定是个bug。 - Jcl

0

这两段代码有所不同,这可能是为什么 FxCOP 标记其中一个而不标记另一个的原因。

请注意,Visual Studio 2010 Premium 中的代码分析不会使用该警告标记任何一段代码,因此与旧版 FxCOP 相比可能增加了一些理解。

无论如何,区别在于实际上,using 块并不转换为问题中显示的 try/catch 块。

以下是更好的翻译:

var ms = new MemoryStream(Encoding.Default.GetBytes(DefaultControlTemplateXaml));
var temp = (IDisposable)ms;
try
{
    var x = XamlReader.Load(ms);
    _defaultControlTemplate = x as ControlTemplate;
}
finally
{
    if (temp != null)
        temp.Dispose();
}

请注意,我并不是在说这就是您收到警告的原因,我只是在说这两段代码之间存在一个区别。请注意,我并不是在说这就是您收到警告的原因,我只是在说这两段代码之间存在一个区别。


-1

正确的代码应该是:

using(var ms= new MemoryStream(Encoding.Default.GetBytes(DefaultControlTemplateXaml)))
{
    var x = XamlReader.Load(ms);
    _defaultControlTemplate = x as ControlTemplate;
}

你需要在using语句中声明和赋值。这样,它将正确调用dispose方法。

编辑:澄清引用问题

我假设你正在执行以下操作:

var ms;
using(ms= new MemoryStream(Encoding.Default.GetBytes(DefaultControlTemplateXaml)))
{
    var x = XamlReader.Load(ms);
    _defaultControlTemplate = x as ControlTemplate;
}

这时候,即使已经释放了ms变量,你仍然可以在后续代码中使用它。这就是这个消息想要说明的。或者你可以在using代码块内将ms赋值给另一个变量。


我把它添加为问题的评论,以便让它更易读。与原始问题类似,fxcop上出现了CA2000的错误。对象两种方式都能正确处理,只是我不喜欢在fxcop上看到那个警告。 - Jcl

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