2010年09月07日

ApplicationExceptionが推奨されない理由

   このエントリーをはてなブックマークに追加 Clip to Evernote
Ognacの雑感 − ApplicationException は非推奨です で話題になっている、ApplicationExceptionですが、

MSDNの「CA1058: 型は、一定の基本型を拡張することはできません」 のページには、
.NET Framework Version 1 では、ApplicationException から新しい例外を派生することが推奨されていました。この推奨事項は変更されました。新しい例外は、System.Exception または System 名前空間のサブクラスの 1 つから派生する必要があります。

と記載があります。このとおり、マイクロソフトはApplicationExceptionの利用を推奨していません。

その理由ですが、.NET Framework の中で、ApplicationExecptionが設計者の意図に反した使い方がされているからだと思われます。

msdn で ApplicationException のページを開いてもらえばお分かりになりますが、 なぜか、.NETFrameworkで、このApplicationExceptionを継承している例外クラスがあります。



空いた口が塞がらない、というのはまさしくこのことです。
ApplicationExecptionが完全に破たんしていることがわかります。
これは、バグと言っても良いと思います。

Microsoftの人も ApplicationExceptionを理解しないでコードを書いてしまった人がいるってことですね。

もし、ApplicationExceptionを直接throwした場合や、独自例外を ApplicationExceptionから派生させた場合、
ApplicationExceptionをcatchしてしまうと、上記例外との区別がつかなくなってしまいます。

ApplicationExceptionをcatchせずに、その派生クラスをcatchするようにすれば、回避できますが、
それよりは、ApplicationExceptionを使わないようにしたほうが良いと思います。
  

Posted by gushwell at 23:41Comments(4)TrackBack(1)

2007年06月26日

スレッド内で発生した例外 まとめ

   このエントリーをはてなブックマークに追加 Clip to Evernote
かなり回りくどい書き方をしてきたが、まとめれば以下の通り。
(ThreadPool.QueueUserWorkItemとDelegateのコードは省略)

○スレッド内で未処理の例外がある場合の動作         
Thread                 プログラム強制終了         ○
ThreadPool プログラム強制終了 ○
Timer プログラム継続 × 例外は行方不明
BeginInvoke プログラム継続 × 例外は行方不明
BeginInvoke+EndInvoke プログラム強制終了 ○

○×は、AppDomain.UnhandledExceptionで処理できるかどうか。

結論は、スレッド内で発生した例外は、そのスレッド内で処理しろってこと。
AppDomain.UnhandledException で処理させようとしても、うまくいかない。
もっとも、プログラムが終了してしまってよいのならば、UnhandledException使っても良いけどね。  
Posted by gushwell at 20:59Comments(2)TrackBack(0)

2007年06月25日

スレッド内で発生した例外 - System.Timers.Timer

   このエントリーをはてなブックマークに追加 Clip to Evernote
では、System.Timers.Timerを使った非同期処理の場合はどうなるんだろうか?
この Timerもスレッドが生成されるので、同じ動きだろうか?

public static void Example() {
System.Timers.Timer tm = new System.Timers.Timer();
tm.Elapsed += new System.Timers.ElapsedEventHandler(tm_Elapsed);
tm.Interval = 1000;
tm.Enabled = true;
Console.ReadLine();
Console.WriteLine("end");
}

static void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
try {
Console.WriteLine("Hello World!");
Thread.Sleep(1010);
throw new Exception("1");
} finally {
Thread th = Thread.CurrentThread;
Console.WriteLine(
th.ManagedThreadId + " " +
th.IsThreadPoolThread);
}

}


というコードで試してみる。
あえて、tm.Interval よりも、em_Elapsed メソッドの方が時間がかかるようにしてある。

これを実行すると、今までのプログラムと違って、強制終了されない。
このコードを動かすと、当然だが、IsThreadPoolThread は、常に trueだ。
一方、ManagedThreadId は、4,5,6の値が何度も表示され、7以上の値が表示されることはない。

スレッド内で例外を処理しなくても、スレッドが自動的にスレッドプールに返却されているようだ。

さらに、興味深い?のは、AppDomain.UnhandledExceptionで例外がキャッチできないことだ。tm_Elapsed 内で未処理の例外があると、この例外は行方不明になってしまう。

これまでの結論は、AppDomain.UnhandledExceptionで は使えないってこと。


それと、繰り返しになるが、Timerの場合は、例外をキャッチしなくてもプログラムが強制終了してしまうことは無い。
一方、Threadの場合は、強制終了されてしまう。
この2つの違いは、スレッドプールと、通常のマニュアルスレッドの違いから来るのかな?
それとも、Timer独自の仕様なのだろうか?

ThreadPool.QueueUserWorkItem や DelegateのBeginInvoke についても調べないといけないな。
  
Posted by gushwell at 21:30Comments(0)TrackBack(0)

2007年06月22日

スレッド内で発生した例外 その2

   このエントリーをはてなブックマークに追加 Clip to Evernote
前回「スレッド内で発生した例外」の続き。
どうも気になったので調べてみた。
スレッドで例外が発生し、その例外を処理しないと、スレッドは終了せず、Runningの状態のままみたい。

となると、AppDomain.UnhandledException で例外を処理できたとしても、迷子になったスレッドが残ってしまい、スレッドを沢山起動するようなプログラムの場合は、問題が隠されてしまうだけで、本当の問題解決にはならないってことだな。

だから、スレッドで処理されない例外がある場合は、システム側で処理を強制終了させてしまうという仕様になってるんだろう。

と理解した。

MSDNライブラリを調べてみると、「マネージ スレッドの例外」には、それらしいことが書いてあった。

けど、事前にコードを書いて確かめてたから、少しは理解できたけど、日本語読解力が不足しているせいか、僕には、どうもピンとこない文章だ。

----
.NET Framework Version 2.0 では、共通言語ランタイムはスレッド内のほとんどの未処理の例外をそのまま続行させます。ほとんどの場合、これは未処理の例外によって-アプリケーションが終了することを意味します。
---
はまだ良いけど、

---
マネージ コードが例外ハンドラをインストールする機会を得る前に、ランタイムは未処理の例外をスローできます。マネージ コードにこのような例外を処理する機会がない場合でも、例外を続行させることができます。
---
になると?だ。
  
Posted by gushwell at 23:34Comments(0)TrackBack(0)

2007年06月21日

スレッド内で発生した例外

   このエントリーをはてなブックマークに追加 Clip to Evernote
昨日の記事「AppDomain.UnhandledException」の続き。

では、コンソールアプリで、スレッドを利用した場合の例外処理ってどうなるのだろうか?

public class Test {

[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAp
public static void Example() {
AppDomain currentDomain = Thread.GetDomain();
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandle

Thread th = new Thread(MyThread);
th.Start();
Console.ReadLine();
Console.WriteLine("end");
}

static void MyThread() {
Console.WriteLine("Thread 開始");
throw new Exception("my thread error");
}

static void MyHandler(object sender, UnhandledExceptionEventArgs args) {
Exception e = (Exception)args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
}

public static void Main() {
try {
Example();
} catch ( Exception ex ) {
Console.WriteLine("処理されていない例外: " + ex.Message);
}
}
}

というコードで試してみる。
UnhandledExceptionイベントハンドラは処理されるが、スレッド内で未処理の例外があるということで、昨日のコードと同様、プログラムが強制終了してしまう。

やはり、UnhandledExceptionで例外を処理するのではなく、MyThread()内で、try-catch を使い、例外処理をする必要があるようだ。  
Posted by gushwell at 21:08Comments(0)TrackBack(0)

2007年06月20日

AppDomain.UnhandledException

   このエントリーをはてなブックマークに追加 Clip to Evernote
どうも、昔調べたときと、例外処理の動作が変わっているみたいだ。
なので、再度調べなおしてみる。

まずは、MSDNドキュメントの「AppDomain.UnhandledException イベント」のページにあるサンプルを参考に、以下のコードを書いてみた。(まあ、ほとんど同じコードだけど)


using System;
using System.Security.Permissions;

public class Test {

[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlAppDomain)]
public static void Example() {
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);

throw new Exception("2");

}

static void MyHandler(object sender, UnhandledExceptionEventArgs args) {
Exception e = (Exception) args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
}

public static void Main() {
Example();
}
}

実行すると、確かに制御が MyHandlerに行っているので、一応UnhandledExceptionイベンの処理は出来ているのだが、その後がいけない。

例の

「問題が発生したため、ConsoleApplication1 を終了します。 ご不便をおかけして申し訳ありません。」

というダイアログが出て、これを閉じると、コンソールに、

「ハンドルされていない例外: System.Exception: 2 ...」

というメッセージが表示されてしまう。どこが悪いのかさっぱりわからない。これってバグですか?
しかたないので、

Main() メソッドで、try-catch で、すべての例外を捕まえることにした。

 public static void Main() {
try {
Example();
} catch ( Exception ex ) {
Console.WriteLine("処理されていない例外: " + ex.Message);
}
}

コンソールアプリなんだし、こっちのほうが素直なコードだな。
ということで、これ以上深入りするのは辞めた。
  
Posted by gushwell at 22:56Comments(0)TrackBack(0)

2006年07月08日

ApplicationException クラス

   このエントリーをはてなブックマークに追加 Clip to Evernote
MSDNライブラリをながめていたら、こんな記述を見つけた。

ほとんどのアプリケーションでは、Exception クラスからカスタム例外を派生します。本来、カスタム例外は ApplicationException クラスから派生しなければならないと考えられていましたが、実際には、このことで大きな価値が付加されたことはないようです。

ドキュメントにこういうことを書くのも珍しいな。
でも、自己否定できるから、より良い解に近づくことが出来るんだろうな。


  
Posted by gushwell at 21:44Comments(0)TrackBack(0)

2005年12月26日

ApplicationExceptionからの例外クラスの派生は×

   このエントリーをはてなブックマークに追加 Clip to Evernote
匣の向こう側 - あまりに.NETなで紹介されていましたが、ApplicationException から新しい例外を派生することは推奨されなくなったようです。
たしかに、Visual Studio 2005 Team System のドキュメントにも、
.NET Framework Version 1 では、ApplicationException から新しい例外を
派生することが推奨されていました。この推奨事項は変更されました。
新しい例外は、System.Exception または System 名前空間のサブクラスの
1 つから派生する必要があります。

と書いてあります。
書籍「プログラミング .NET Framework」と同じ見解になったということなのでしょうね。
  
Posted by gushwell at 09:44Comments(0)TrackBack(0)