C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: try-catchステートメントの挙動について疑問


(過去ログ 87 を表示中)

[トピック内 3 記事 (1 - 3 表示)]  << 0 >>

■52000 / inTopicNo.1)  try-catchステートメントの挙動について疑問
  
□投稿者/ tsutsumi (1回)-(2010/07/29(Thu) 01:23:19)

分類:[C#] 

2010/07/29(Thu) 01:27:13 編集(投稿者)
Windows7、Visual Studio 2008、C#、.NET Framework 3.0、Debugビルドで開発しています。

今、以下のような前提条件があって、
・画像を包括するSampleクラスがある(フィールドには画像ファイルのパスを持つのみ)。
・SampleクラスのオブジェクトはXMLシリアライズ/デシリアライズ可能である。

以下のような処理を考えています。
(1)指定したファイルに対し、Sampleオブジェクトへのデシリアライズを試みる。
(2)指定したファイルがデシリアライズできない場合、Bitmapインスタンスとして開く。
(※指定したファイルを「Sample」→「Bitmap」の優先度で開こうとする)

(1)、(2)はそれぞれ別のtry-catchステートメント内に記述されていますが(下記コード参照)、
「(1)の過程で例外が発生した場合、なぜか(2)でも例外が発生してしまう」という現象に悩まされています。
具体的には(1)でInvalidOperationException、(2)ではArgumentExceptionがスローされます。


はじめ、Sampleクラスの定義と処理は以下のようにしました。

=Sampleクラス(A)=========
class Sample {
  public Sample() {
    ;
  }
  public static Sample Load(string path) {
    XmlSerializer xml = new XmlSerializer(typeof(Sample));
    FileStream fs = new FileStream(path, FileMode.Open);
    Sample sample = (Sample)xml.Deserialize(fs);
    fs.Close();
    return sample;
  }
  // 以下、いくつかのフィールドとプロパティ
}
==========

=処理(A-1)=========
string path = "c:\\pict.jpg";
try {
  field_sample = Sample.Load(path); // field_sampleはSampleクラスのインスタンス(クラスフィールドとして宣言済)
  return;
} catch {
  MessageBox.Show("Error1");
}
try {
  field_bmp = new Bitmap(path); // field_bmpはBitmapクラスのインスタンス(クラスフィールドとして宣言済)
} catch {
  MessageBox.Show("Error2");
}
==========

上記を実行させたところ、「Error1」も「Error2」も表示されてしまいました。しかし、

=処理(A-2)=========
string path = "c:\\pict.jpg";
try {
  //field_sample = Sample.Load(path); // field_sampleはSampleクラスのインスタンス(クラスフィールドとして宣言済)
  //return;
} catch {
  MessageBox.Show("Error1");
}
try {
  field_bmp = new Bitmap(path); // field_bmpはBitmapクラスのインスタンス(クラスフィールドとして宣言済)
} catch {
  MessageBox.Show("Error2");
}
==========

上記のようにSampleオブジェクトの生成をスキップしたところ、「Error1」も「Error2」も表示されませんでした。
(field_bmpもnullのまま)


「Error1」が発生するとどうしても「Error2」も併せて発生してしまうため、
SampleクラスのLoadメソッド内で例外を検知してnullを返すように
Sampleクラスの定義と処理を以下のように書き換えてみました。

=Sampleクラス(B)=========
class Sample {
  public Sample() {
    ;
  }
  public static Sample Load(string path) {
    try {
      XmlSerializer xml = new XmlSerializer(typeof(Sample));
      FileStream fs = new FileStream(path, FileMode.Open);
      Sample sample = (Sample)xml.Deserialize(fs);
      fs.Close();
      return sample;
    } catch {
      return null;
    }
  }
  // 以下、いくつかのフィールドとプロパティ
}
==========

=処理(B)=========
string path = "c:\\pict.jpg";
field_sample = Sample.Load(path); // field_sampleはSampleクラスのインスタンス(クラスフィールドとして宣言済)
if (field_sample == null) {
  try {
    field_bmp = new Bitmap(path); // field_bmpはBitmapクラスのインスタンス(クラスフィールドとして宣言済)
  } catch {
    MessageBox.Show("Error2");
  }
}
==========

しかしながら上記のようにしても変わらず「Error2」が発生してしまいました。
そして、処理(A-2)と同様に Sample.Load(path); を呼ばなければ「Error2」は発生しません。


なぜ(1)、(2)の処理それぞれにtry-catchステートメントをあてて例外処理をしているにも関わらず、
先のステートメントで例外が発生すると、後のステートメントで別の例外が発生するのでしょうか?
自身の不勉強のせいもございますが、まったく原因がわからず困り果てております。
どなたか原因がお分かりになる方、ご教示いただければ幸いです。
なにとぞよろしくお願い致します。

引用返信 編集キー/
■52001 / inTopicNo.2)  Re[1]: try-catchステートメントの挙動について疑問
□投稿者/ Hongliang (677回)-(2010/07/29(Thu) 02:29:01)
> public static Sample Load(string path) {
>   XmlSerializer xml = new XmlSerializer(typeof(Sample));
>   FileStream fs = new FileStream(path, FileMode.Open);
>   Sample sample = (Sample)xml.Deserialize(fs);
>   fs.Close();
>   return sample;
> }
xml.Deserialize で例外が発生すると、fs を Close できません。
// いずれは GC によってファイナライザが動いてハンドルは解放されますが、そのタイミングは不定です。

FileAccess を指定せずに FileMode.Open を使って FileStream を作成した場合、
暗黙に FileAccess.ReadWrite / FileShare.Read が指定されることになります。
Bitmap(String) コンストラクタの内部で FileStream を作成する際には
FileShare.ReadWrite を指定はしないでしょうから、fs が Close できていない限り、
「そのファイルに対し他のストリームからは読み込みしか許可したくないのに
 既に読み書きで開いているのがいるから開けないよ」
エラーが出ることになります。

こういう場合、using 構文を使用することで例外が発生しても閉じることができます。

using (FileStream fs = new FileStream(path, FileMode.Open)) {
  Sample sample = (Sample)xml.Deserialize(fs);
  return sample;
}

引用返信 編集キー/
■52003 / inTopicNo.3)  Re[2]: try-catchステートメントの挙動について疑問
□投稿者/ tsutsumi (2回)-(2010/07/29(Thu) 08:38:15)
> Hongliangさん

> xml.Deserialize で例外が発生すると、fs を Close できません。
うわああああ、その通りでした! なんたる凡ミス……恥ずかしい……。
ご指摘いただいた部分を直して、無事意図する挙動を達成できました。
どうもありがとうございました!

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -