Как мне инициализировать переменные, которые будут использоваться в блоке try / catch / finally? - PullRequest
4 голосов
/ 22 июля 2010

Если я использую блок 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 в ноль?Я также пытался объявить fs в блоке try, но затем я получил ошибку The name fs does not exsist in the current context в блоке finally.

Кстати: я знаю, что мог бы использовать блок Using, но суть моего вопросапонять правильное использование блока try / catch / finally.

Ответы [ 6 ]

5 голосов
/ 22 июля 2010

Назначить 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();
        }
    }
4 голосов
/ 22 июля 2010

См. Раздел 5.3 спецификации.

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

В указанном месте в исполняемом коде члена функции переменная определенно назначается, есликомпилятор может доказать с помощью статического анализа потока, что переменная была автоматически инициализирована или была целью как минимум одного присваивания.

С вашим try / catch / finally присваивание *Блок 1010 * не может быть гарантирован при попытке доступа к объекту в блоке finally.Как вы уже видели, вы можете выполнить требование, присвоив начальное значение переменной (null, в данном случае).

3 голосов
/ 22 июля 2010

Команда разработчиков C # считает, что удостовериться, что вы явно инициализируете вещи, - это хорошая идея.Я склонен согласиться;Меня укусили ошибки от неинициализированных переменных, достаточно в C ++.

2 голосов
/ 22 июля 2010

Инициализация fs до null - правильное использование. Компилятор хочет убедиться, что вы читаете только инициализированные переменные, чтобы избежать серьезных ошибок. И он не может гарантировать, что инициализация в вашем блоке try когда-либо будет выполнена

1 голос
/ 22 июля 2010

Пурист во мне хотел бы сделать что-то вроде этого:

    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)
            }
        }
    }

Может быть, я только что испорчен кодированием злого 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
}
0 голосов
/ 22 июля 2010

Вы рядом.Я бы установил объявление равным 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.

...