我应该如何初始化将在try / catch / finally块中使用的变量?

时间:2010-07-21 20:43:03

标签: c# .net try-catch finally

如果我使用try / catch / finally块我应该在哪里以及如何初始化变量?例如,假设我正在尝试使用FileStream。我想捕获创建或使用流时抛出的任何异常。然后,无论是否有任何问题,我都希望确保创建的任何流都已关闭。

所以我会做这样的事情:

        System.IO.FileStream fs;
        try
        {
            fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
            //do something with the file stream
        }
        catch (Exception exp)
        {
            //handle exceptions
        }
        finally
        {
            //ERROR: "unassigned local variable fs"
            if (fs != null)
            {
                fs.Close();
            }
        }

然而,这给了我一个错误,在finally块中说unassigned local variable fs。但是,如果我将fs的声明更改为System.IO.FileStream fs = null,则可以。

为什么我需要将fs显式设置为null?我也试过在try块中声明fs,但后来我在finally块中得到了错误The name fs does not exsist in the current context

BTW:我知道我可以使用Using块,但我的问题是要理解try / catch / finally块的正确用法。

6 个答案:

答案 0 :(得分:5)

指定fs = null;

  System.IO.FileStream fs = null;
    try
    {
        fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
        //do something with the file stream
    }
    catch (Exception exp)
    {
        //handle exceptions
    }
    finally
    {
        //ERROR: "unassigned local variable fs"
        if (fs != null)
        {
            fs.Close();
        }
    }

答案 1 :(得分:4)

见规范5.3节。

http://msdn.microsoft.com/en-us/library/aa691172(VS.71).aspx

  

在可执行文件中的给定位置   函数成员的代码,变量   据说是肯定的   编译器可以通过静态流程证明   分析,变量已经   自动初始化或已经过   至少一项任务的目标。

使用try / catch / finally时,当您尝试访问try块中的对象时,无法保证finally块的分配。如您所见,您可以通过为变量指定初始值来满足要求(在本例中为null)。

答案 2 :(得分:3)

C#设计团队认为确保明确初始化事物是一个好主意。我倾向于同意;在C ++中,我已经被来自未初始化变量的错误所困扰。

答案 3 :(得分:2)

fs初始化为null是正确的用法。编译器希望确保您只读取初始化变量,以避免严重错误。并且它无法保证您的try块中的初始化被执行

答案 4 :(得分:1)

我的纯粹主义者想要做这样的事情:

    void DoSomethingWithStream()
    {
        try
        {
            System.IO.FileStream fs = new System.IO.FileStream(@"C:\test.txt", System.IO.FileMode.Open);
            try
            {
                // do something with the file stream

            }
            catch (Exception ex)
            {
                // handle exceptions caused by reading the stream,
                // if these need to be handled separately from exceptions caused by opening the stream
            }
            finally
            {
                // FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
                fs.Dispose();
            }
        }
        catch (Exception ex)
        {
            // handle exceptions that were either thrown by opening the filestream, thrown by closing the filestream, or not caught by the inner try/catch
        }
    }

尽管如此,这将是一个混乱:

    void DoSomethingWithStream_PartDeux()
    {
        try
        {
            System.IO.FileStream fs = new System.IO.FileStream(@"C:\test.txt", System.IO.FileMode.Open);
            try
            {
                try
                {
                    // do something with the file stream

                }
                catch (Exception ex)
                {
                    // handle exceptions caused by reading the stream,
                    // if these need to be handled separately from exceptions caused by opening the stream
                }
                finally
                {
                    fs.Close();
                }
            }
            finally
            {
                // FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
                fs.Dispose();
            }
        }
        catch (Exception ex)
        {
            // handle exceptions
        }
    }

访问数据库可能更糟糕:

    void DoSomethingWithDatabase()
    {
        var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!");
        try
        {
            var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);

            connection.Open();
            try
            {
                var reader = command.ExecuteReader();
                try
                {
                    try
                    {
                        // read data from data reader (duh)
                    }
                    finally
                    {
                        reader.Close();
                    }
                }
                finally
                {
                    reader.Dispose();
                }
            }
            finally
            {
                connection.Close();
            }
        }
        finally
        {
            connection.Dispose();
        }
    }

但是,在大多数情况下,如果您要在之后立即处理它们(除非您真的是偏执狂),我真的不需要明确地关闭您的流/连接/数据读取器。因此,上面的数据库代码可以很容易就是这样:

    void DoSomethingWithDatabase_PartDeux()
    {
        using (var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!"))
        {
            var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);

            connection.Open();
            using(var reader = command.ExecuteReader())
            {
                // read data from data reader (duh)
            }
        }
    }

也许我刚被Wily博士的邪恶API编码所玷污。使用initialize-variable-to-null技巧不适用于他的框架:

    void DoSomethingWithDrWilyEvilBoobyTrap()
    {
        Dr.Wily.Evil.BoobyTrap trap = null;
        try
        {
            trap = new Dr.Wily.Evil.BoobyTrap(Dr.Wily.Evil.Evilness.Very);

            // do something with booby trap
        }
        catch (Exception ex)
        {
            // handle exceptions
        }
        finally
        {
            if (trap != null) // Exception thrown here!
                trap.Dispose(); // Exception thrown here as well!
        }
    }

以下是他的API中的一些源代码的预览:

public enum Evilness
{
    Slight,
    Moderate,
    Very,
}

class BoobyTrap : IDisposable
{
    public Evilness Evil { get; protected set; }

    public BoobyTrap(Evilness evil)
    {
        this.Evil = evil;
    }

    public void DoEvil()
    {
        // ... snip (sorry, it's just too evil) ...
    }

    public static bool IsNull(BoobyTrap instance)
    {
        throw new Exception("I bet you thought this function would work, didn't you?  Well it doesn't!  You should know whether or not your variables are null.  Quit asking me!");
    }

    public static bool operator !=(BoobyTrap x, object y)
    {
        if(y == null)
            throw new Exception("You cannot check if an instance of a BoobyTrap is null using the != operator.  Mwahahaha!!!");

        return x.Equals(y);
    }

    public static bool operator ==(BoobyTrap x, object y)
    {
        if (y == null)
            throw new Exception("You cannot check if an instance of a BoobyTrap is null using the == operator.  Mwahahaha!!!");

        return x.Equals(y);
    }

    #region IDisposable Members

    public void Dispose()
    {
        switch (this.Evil)
        {
            case Evilness.Moderate:
            case Evilness.Very:
                throw new Exception("This object is cursed.  You may not dispose of it.");
        }
    }

    #endregion
}

答案 5 :(得分:0)

你很亲密。我将声明设置为null

System.IO.FileStream fs = null;
try
{
    fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
    //do something with the file stream
}
catch (Exception exp)
{
    //handle exceptions
}
finally
{
    //ERROR: "unassigned local variable fs"
    if (fs != null)
    {
        fs.Close();
    }
}

我认为当你不能使用using声明时,这是非常可以接受的。