4

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 ブロックで宣言しようとしましたが、finally ブロックfsでエラーが発生します。The name fs does not exsist in the current context

ところで: Using ブロックを使用できることはわかっていますが、私の質問のポイントは、try/catch/finally ブロックの正しい使用法を理解することです。

4

6 に答える 6

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();
        }
    }
于 2010-07-21T20:45:22.520 に答える
4

仕様のセクション 5.3 を参照してください。

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

関数メンバーの実行可能コード内の特定の場所で、変数が自動的に初期化されたか、少なくとも 1 つの割り当てのターゲットであったことをコンパイラが静的フロー解析によって証明できる場合、その変数は確実に割り当てられていると言われます。

try/catch/finally では、tryブロック内のオブジェクトにアクセスしようとしたときに、ブロックの割り当てが保証されませんfinallynullこれまで見てきたように、変数 (この場合は ) に初期値を割り当てることで要件を満たすことができます。

于 2010-07-21T20:52:10.530 に答える
3

C#設計チームは、物事を明示的に初期化することを確認することは良い考えだと感じています。私は同意する傾向があります。私はC++で十分に初期化されていない変数からのバグに噛まれてきました。

于 2010-07-21T20:47:42.267 に答える
2

初期化fsnull正しい使用法です。コンパイラーは、重大なミスを避けるために、初期化された変数のみを読み取っていることを確認したいと考えています。また、tryブロックの初期化が実行されることを保証することはできません

于 2010-07-21T20:48:21.593 に答える
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)
            }
        }
    }

Dr. 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
}
于 2010-07-21T22:29:27.917 に答える
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ステートメントを使用できない場合、それは非常に受け入れられると思います。

于 2010-07-21T20:45:02.147 に答える