对象初始化器在using块中会生成代码分析警告CA2000。

19

如果我在使用块中使用对象初始化程序,则会收到代码分析警告,指出未正确处理对象的释放:

CA2000:Microsoft.Reliability:在方法'ReCaptcha.CreateReCaptcha(this HtmlHelper,string,string)'中,对象'<> g__initLocal0'沿所有异常路径未被处理。在所有对它的引用超出范围之前调用System.IDisposable.Dispose对对象'&lt;> g__initLocal0'。

这是代码:


使用ReCaptchaControl:
    //创建新的控件实例
    using (var control = new ReCaptchaControl()
    {
        ID = id,             //设置控件ID
        Theme = theme,       //设置主题
        SkipRecaptcha = false  //不跳过reCAPTCHA验证
    })
    {
        // 在这里执行相关操作
    }

如果我不使用对象初始化器,代码分析器会很高兴:


    使用 ReCaptchaControl()
    {
        ID = id;
        主题 = theme;
        跳过Recaptcha = false;
//在此处执行某些操作 }

这两个代码块有什么不同呢?我原以为它们会生成相同的中间语言(IL)代码。难道这是代码分析引擎的一个bug吗?

1个回答

33

不,两者是有区别的。

对象初始化器只会在所有属性被设置后才会将值分配给变量。换句话说,下面这段代码:

Foo x = new Foo { Bar = "Baz" };

等同于:

Foo tmp = new Foo();
tmp.Bar = "Baz";
Foo x = tmp;

这意味着如果你的情况下任何一个属性设置器抛出异常,对象都不会被销毁。

编辑:就像我想的那样...试试这个:

using System;

public class ThrowingDisposable : IDisposable
{
    public string Name { get; set; }

    public string Bang { set { throw new Exception(); } }

    public ThrowingDisposable()
    {
        Console.WriteLine("Creating");
    }

    public void Dispose()
    {
        Console.WriteLine("Disposing {0}", Name);
    }
}

class Test
{
    static void Main()
    {
        PropertiesInUsingBlock();
        WithObjectInitializer();
    }

    static void PropertiesInUsingBlock()
    {
        try
        {
            using (var x = new ThrowingDisposable())
            {
                x.Name = "In using block";
                x.Bang = "Ouch";
            }
        }
        catch (Exception)
        {
            Console.WriteLine("Caught exception");
        }
    }

    static void WithObjectInitializer()
    {
        try
        {
            using (var x = new ThrowingDisposable
            {
                Name = "Object initializer",
                Bang = "Ouch"
            })
            {
                // Nothing
            }
        }
        catch (Exception)
        {
            Console.WriteLine("Caught exception");
        }
    }
}

输出:

Creating
Disposing In using block
Caught exception
Creating
Caught exception
请注意,没有“Disposing Object initializer”这一行。

1
现在你写出来,这就有意义了。我认为这是一个容易被忽视的陷阱。幸运的是,Visual Studio比我更聪明。 - Tero
1
@Jon - 那么结论是不是正确的:'不要在实现IDisposable接口的类型上使用对象初始化语法'。(因为我们无法控制扩展来纠正CA引擎标记的问题。) - Gishu
2
@Gishu:如果你担心属性设置器可能会抛出异常,那就需要这么做。 - Jon Skeet
非常好地减轻了问题,谢谢。我同意之前的帖子:我很高兴FXCop比我聪明。我本来会忽略这个问题的。 - Quark Soup

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