質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.48%

try-catch-finallyとusingによるClose

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 720

score 13

前提・実現したいこと

ftpでのアップロード処理を勉強し始めたところで、適当なtxtファイルをターゲットに転送する簡易ソフトを作ってみています。

大容量ファイルや大量のファイル転送の途中に切断が起きた場合に、streamがcloseされる様にしたいと考えています。現在のコードだと、処理中のエラーでcloseされずに進んでしまうと思うので、改良を検討しています。

調べてみたところ、try-finaly、usingなどを利用するようで、試しに修正してみました。
一応動いているのですが、修正は合っておりますでしょうか?より良い方法があれば、ご教示下さい。

またCloseする場合に関して、try-catch-finallyとusingの使い方・使い分けなどを宜しければご教示頂ければと思います。

宜しくお願い致します。

該当のソースコード

private void Button_Click(object sender, RoutedEventArgs e)
        {
            //アプリケーションパス取得
            string exePath = Environment.GetCommandLineArgs()[0];
            string exeFullPath = System.IO.Path.GetFullPath(exePath);
            string startupPath = System.IO.Path.GetDirectoryName(exeFullPath);

            string path_D = startupPath + "\\Work_Down\\";
            string path_U = startupPath + "\\Work_Up\\";

            string Path = path_U;
            string UpPath = "※※※※※";

            string ipAddress = "※※※※※";

            try
            {
                // ファイル名を取得する
                string[] names;

                names = Directory.GetFiles(path_U, "*.txt", SearchOption.AllDirectories);

                // ファイル名をリストにする
                List<string> files = new List<string>();

                foreach (var name in names)
                {
                    if (name != "")
                    {
                        files.Add(name.Substring(name.LastIndexOf("\\") + 1));
                    }
                }

                //アップロード
                foreach (var file in files)
                {
                    //アップロードするファイル
                    string upFile = Path + file;

                    //アップロード先のURI
                    Uri u = new Uri("ftp://" + ipAddress + UpPath + file);

                    //FtpWebRequestの作成
                    FtpWebRequest ftpReq = (FtpWebRequest)
                    WebRequest.Create(u);

                    //ログインユーザー名とパスワードを設定
                    ftpReq.Credentials = new NetworkCredential("※※※", "※※※");

                    //MethodにWebRequestMethods.Ftp.UploadFile("STOR")を設定
                    ftpReq.Method = WebRequestMethods.Ftp.UploadFile;

                    //要求の完了後に接続を閉じる
                    ftpReq.KeepAlive = false;

                    //バイナリモードで転送する
                    ftpReq.UseBinary = true;

                    //PASVモードを無効にする
                    ftpReq.UsePassive = false;

                    //タイムアウトを設定する
                    ftpReq.Timeout = 30000;

                    // ↓===ここから===

                    //ファイルをアップロードするためのStreamを取得
                    Stream reqStrm = ftpReq.GetRequestStream();
                    //アップロードするファイルを開く
                    FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read);

                    //アップロードStreamに書き込む
                    byte[] buffer = new byte[1024];
                    while (true)
                    {
                        int readSize = fs.Read(buffer, 0, buffer.Length);
                        if (readSize == 0)
                            break;
                        reqStrm.Write(buffer, 0, readSize);
                    }

                    //閉じる
                    fs.Close();
                    reqStrm.Close();

           // ↑ここまで
                }

            }
            catch
            {
            }
        }

試したこと

catch部の内容は省略しています。

//ファイルをアップロードするためのStreamを取得
Stream reqStrm = ftpReq.GetRequestStream();
//アップロードするファイルを開く
FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read);

try
{
    //アップロードStreamに書き込む
    byte[] buffer = new byte[1024];
    while (true)
    {
        int readSize = fs.Read(buffer, 0, buffer.Length);
        if (readSize == 0)
            break;
        reqStrm.Write(buffer, 0, readSize);
    }
}
catch
{
}
finally
{
    //閉じる
    fs.Close();
    reqStrm.Close();
}
try
{
    //ファイルをアップロードするためのStreamを取得
    using (Stream reqStrm = ftpReq.GetRequestStream())
    {
        //アップロードするファイルを開く
        using (FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read))
        {
            //アップロードStreamに書き込む
            byte[] buffer = new byte[1024];
            while (true)
            {
                int readSize = fs.Read(buffer, 0, buffer.Length);
                if (readSize == 0)
                    break;
                reqStrm.Write(buffer, 0, readSize);
            }
        }


    }
}
catch
{
}

以下、再修正版

//ファイルをアップロードするためのStreamを取得
using (Stream reqStrm = ftpReq.GetRequestStream())
{
//アップロードするファイルを開く
using (FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read))
{
    try
    {
        //アップロードStreamに書き込む
        byte[] buffer = new byte[1024];
        while (true)
        {
            int readSize = fs.Read(buffer, 0, buffer.Length);
            if (readSize == 0)
                break;
            reqStrm.Write(buffer, 0, readSize);
        }
    }
    catch
    {
    }
}

}

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+3

例外処置については以下の記事を読むことをお勧めします。特に Part.2 に注目です。その中で Dispose をどうするかという話になるはずです。

NETの例外処理 Part.1
https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/

.NETの例外処理 Part.2
https://blogs.msdn.microsoft.com/nakama/2009/01/02/net-part-2/

記事の内容を簡単にまとめると:

(1) 予測可能で正しい業務フローに戻すことができる「業務エラー」(例:ユーザーの入力間違い)と、予測できないもしくは予測はできても何の対応もできない「例外」(例:DB サーバーダウン)を区別して対処。

(2) 「例外」はランタイムに拾わせてアプリケーションを停止させる。

(3) よほどのことがない限り try-catch は書かない。

(4) キャッチせざるを得ない場合でも Execption はキャッチしない。(範囲を絞る。例えば DB 関係の例外で予測される SqlException に限定して catch するとか)

(5) 間違って補足してしまった例外は throw する。例えば SqlException に含まれる PK 制約違反だけは「業務エラー」として処置したいが、その他が原因で発生した SqlException を catch してしまった場合。(注:catch ブロックでキャッチした例外を throw するとスタックトレースが途切れるので単に throw と書く)

(6) ユーザーへの通知が必要なら集約的例外処置を利用する。

あと、.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

破損状態例外を処理する
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2009/february/clr-inside-out-handling-corrupted-state-exceptions

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/19 18:35

    ご回答ありがとうございます。
    >(3) よほどのことがない限り try-catch は書かない。
    取り合えず try-catchを書いておく様な指導を受けていたので、良く読んで勉強します。
    要約もして頂き、助かります。

    キャンセル

  • 2020/03/19 19:06

    是非、その指導した人に何故 catch する必要があるのかを聞いて、その結果をここに書いていただくようお願いします。その答えでその人の知識と力量が分かると思います。

    キャンセル

checkベストアンサー

0

修正方針について

特段問題はないです。
ただ、以下の二点参考にしてください。

  • try - catchはusingの中に入れてしまったほうがコードの見通しが良くなる 
  • finallyは不要

usingを利用した場合、usingブロックを抜けた時点でstreamがDispose()されるので、Close()し忘れる心配はいりません。

usingを使わずにClose()する場合について

Close()はfinallyの中で行ってください。
finallyブロックに書いた処理は、何があっても実行されます。
tryが無事終了しようが、例外発生によりcatchブロックに入ろうが、tryの途中でreturnしようが、必ず実行されます。
なので、finallyに書いておけばClose()し忘れる心配はないし、またtryとcatchの両方にClose()を書くようなダサいコードも書かずに済みます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/19 18:31

    ご回答ありがとうございます。
    >try - catchはusingの中に入れてしまったほうがコードの見通しが良くなる
    というの追記してみましたが、合っておりますでしょうか?

    キャンセル

  • 2020/03/19 18:43

    そうですね。そんな具合です。
    try - catchを一番外に書くと、例外発生したときusingを一気に抜けてcatchに入るので、処理を追う認知コストが上がります。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る