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

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

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

1523閲覧

try-catch-finallyとusingによるClose

kkg_No_05

総合スコア13

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

0クリップ

投稿2020/03/19 08:51

編集2020/03/19 09:30

前提・実現したいこと

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

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

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

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

宜しくお願い致します。

該当のソースコード

C#

1private void Button_Click(object sender, RoutedEventArgs e) 2 { 3 //アプリケーションパス取得 4 string exePath = Environment.GetCommandLineArgs()[0]; 5 string exeFullPath = System.IO.Path.GetFullPath(exePath); 6 string startupPath = System.IO.Path.GetDirectoryName(exeFullPath); 7 8 string path_D = startupPath + "\Work_Down\"; 9 string path_U = startupPath + "\Work_Up\"; 10 11 string Path = path_U; 12 string UpPath = "※※※※※"; 13 14 string ipAddress = "※※※※※"; 15 16 try 17 { 18 // ファイル名を取得する 19 string[] names; 20 21 names = Directory.GetFiles(path_U, "*.txt", SearchOption.AllDirectories); 22 23 // ファイル名をリストにする 24 List<string> files = new List<string>(); 25 26 foreach (var name in names) 27 { 28 if (name != "") 29 { 30 files.Add(name.Substring(name.LastIndexOf("\") + 1)); 31 } 32 } 33 34 //アップロード 35 foreach (var file in files) 36 { 37 //アップロードするファイル 38 string upFile = Path + file; 39 40 //アップロード先のURI 41 Uri u = new Uri("ftp://" + ipAddress + UpPath + file); 42 43 //FtpWebRequestの作成 44 FtpWebRequest ftpReq = (FtpWebRequest) 45 WebRequest.Create(u); 46 47 //ログインユーザー名とパスワードを設定 48 ftpReq.Credentials = new NetworkCredential("※※※", "※※※"); 49 50 //MethodにWebRequestMethods.Ftp.UploadFile("STOR")を設定 51 ftpReq.Method = WebRequestMethods.Ftp.UploadFile; 52 53 //要求の完了後に接続を閉じる 54 ftpReq.KeepAlive = false; 55 56 //バイナリモードで転送する 57 ftpReq.UseBinary = true; 58 59 //PASVモードを無効にする 60 ftpReq.UsePassive = false; 61 62 //タイムアウトを設定する 63 ftpReq.Timeout = 30000; 64 65 // ↓===ここから=== 66 67 //ファイルをアップロードするためのStreamを取得 68 Stream reqStrm = ftpReq.GetRequestStream(); 69 //アップロードするファイルを開く 70 FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read); 71 72 //アップロードStreamに書き込む 73 byte[] buffer = new byte[1024]; 74 while (true) 75 { 76 int readSize = fs.Read(buffer, 0, buffer.Length); 77 if (readSize == 0) 78 break; 79 reqStrm.Write(buffer, 0, readSize); 80 } 81 82 //閉じる 83 fs.Close(); 84 reqStrm.Close(); 85 86           // ↑ここまで 87 } 88 89 } 90 catch 91 { 92 } 93 }

試したこと

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

C#

1//ファイルをアップロードするためのStreamを取得 2Stream reqStrm = ftpReq.GetRequestStream(); 3//アップロードするファイルを開く 4FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read); 5 6try 7{ 8 //アップロードStreamに書き込む 9 byte[] buffer = new byte[1024]; 10 while (true) 11 { 12 int readSize = fs.Read(buffer, 0, buffer.Length); 13 if (readSize == 0) 14 break; 15 reqStrm.Write(buffer, 0, readSize); 16 } 17} 18catch 19{ 20} 21finally 22{ 23 //閉じる 24 fs.Close(); 25 reqStrm.Close(); 26}

C#

1try 2{ 3 //ファイルをアップロードするためのStreamを取得 4 using (Stream reqStrm = ftpReq.GetRequestStream()) 5 { 6 //アップロードするファイルを開く 7 using (FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read)) 8 { 9 //アップロードStreamに書き込む 10 byte[] buffer = new byte[1024]; 11 while (true) 12 { 13 int readSize = fs.Read(buffer, 0, buffer.Length); 14 if (readSize == 0) 15 break; 16 reqStrm.Write(buffer, 0, readSize); 17 } 18 } 19 20 21 } 22} 23catch 24{ 25}

以下、再修正版

C#

1//ファイルをアップロードするためのStreamを取得 2using (Stream reqStrm = ftpReq.GetRequestStream()) 3{ 4//アップロードするファイルを開く 5using (FileStream fs = new FileStream(upFile, FileMode.Open, FileAccess.Read)) 6{ 7 try 8 { 9 //アップロードStreamに書き込む 10 byte[] buffer = new byte[1024]; 11 while (true) 12 { 13 int readSize = fs.Read(buffer, 0, buffer.Length); 14 if (readSize == 0) 15 break; 16 reqStrm.Write(buffer, 0, readSize); 17 } 18 } 19 catch 20 { 21 } 22} 23 24}

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

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

hayamura.akito👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

例外処置については以下の記事を読むことをお勧めします。特に 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 09:14

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kkg_No_05

2020/03/19 09:35

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

退会済みユーザー

2020/03/19 10:06

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

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 09:18

takeaship

総合スコア129

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kkg_No_05

2020/03/19 09:31

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

2020/03/19 09:43

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問