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

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

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

Re[14]: 例外処理をするべきか否か


(過去ログ 36 を表示中)

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

■17850 / inTopicNo.1)  例外処理をするべきか否か
  
□投稿者/ 通りすがり (16回)-(2008/05/02(Fri) 13:16:22)

分類:[C#] 

ISerializableを実装してシリアライズとデシリアライズの挙動を自分で定義しようとしています。
デシリアライズ用のコンストラクタにて、SerializationInfoオブジェクトのGetValueやらGetInt32やらGet***に対して存在しない名前で値を取ってこようとすると例外が投げられますよね。
なので、自分は今現在以下の「コード1」と「コード2」の2通りを考えています。
 コード1:SerializationEntryのNameとValueをDictionaryに詰め込んでおくやり方
 コード2:逐一try-catchするやり方

これって、どちらが好ましいでしょうか?
デシリアライズの問題ではなくて、「キーの存在を確かめて操作するのがいいか」「なにも考えずtry-catchするのがいいか」に帰着すると思います。

あ、ちなみに名前が見つからなかったときは、その型のコンストラクタでもって初期化してやろうと思っています。

もっとスマートなやり方あるだろうが!ヴォケ!って場合は、是非お教え願えませんか。
自分としては、SerializationInfoにContainsName的なメソッドがあったらなぁ、と思いました。
コメントをよろしくお願いします。

////////////////////////////////////////////////////////////
// コード1
////////////////////////////////////////////////////////////
[Serializable]
class Hoge : ISerializable
{
 int _n;
 string _str;
 Foo _foo;

 public Hoge()
 {
  _n = 42;
  _str = "hoge";
  _foo = new Foo();
 }

 // デシリアライズ用
 public Hoge(SerializationInfo info, StreamingContext context)
 {
  Dictionary<string, object> entries = new Dictionary<string, object>();
  foreach (SerializationEntry entry in info)
  {
   if (!entries.ContainsKey(entry.Name)) entries.Add(entry.Name, entry.Value);
  }

  if (entries.ContainsKey("n")) _n = info.GetInt32("n");
  else _n = 0;
  if (entries.ContainsKey("str")) _str = info.GetString("str");
  else _str = string.Empty;
  if (entries.ContainsKey("foo")) _foo = info.GetValue("foo", typeof(Foo)) as Foo;
  else _foo = new Foo();
 }

 // シリアライズ用
 public void GetObjectData(SerializationInfo info, StreamingContext context)
 {
  info.AddValue("n", _n);
  info.AddValue("str", _str);
  info.AddValue("foo", _foo);
 }
}



////////////////////////////////////////////////////////////
// コード2
////////////////////////////////////////////////////////////
[Serializable]
class Hoge : ISerializable
{
 int _n;
 string _str;
 Foo _foo;

 public Hoge()
 {
  _n = 42;
  _str = "hoge";
  _foo = new Foo();
 }

 // デシリアライズ用
 public Hoge(SerializationInfo info, StreamingContext context)
 {
  // _nを初期化
  try{_n = info.GetInt32("n");}
  catch(SerializationException ex){_n = 0;}

  // _strを初期化
  try{_str = info.GetString("str");}
  catch(SerializationException ex){_str = string.Empty;}

  // _fooを初期化
  try{_foo = info.GetString("foo");}
  catch(SerializationException ex){_foo = new Foo();}
 }

 // シリアライズ用
 public void GetObjectData(SerializationInfo info, StreamingContext context)
 {
  info.AddValue("n", _n);
  info.AddValue("str", _str);
  info.AddValue("foo", _foo);
 }
}

引用返信 編集キー/
■17867 / inTopicNo.2)  Re[1]: 例外処理をするべきか否か
□投稿者/ れい (518回)-(2008/05/02(Fri) 17:20:13)
No17850 (通りすがり さん) に返信
> ISerializableを実装してシリアライズとデシリアライズの挙動を自分で定義しようとしています。
> デシリアライズ用のコンストラクタにて、SerializationInfoオブジェクトのGetValueやらGetInt32やらGet***に対して存在しない名前で値を取ってこようとすると例外が投げられますよね。
> なので、自分は今現在以下の「コード1」と「コード2」の2通りを考えています。
>  コード1:SerializationEntryのNameとValueをDictionaryに詰め込んでおくやり方
>  コード2:逐一try-catchするやり方
>
> これって、どちらが好ましいでしょうか?

私なら、両方ともやりません。
大抵の場合は、何もチェックせずにそのまま設定します。

public Hoge(SerializationInfo info, StreamingContext context)
 {
  _n = info.GetInt32("n");
  _str = info.GetString("str");
  _foo = info.GetString("foo");
 }

引用返信 編集キー/
■17873 / inTopicNo.3)  Re[1]: 例外処理をするべきか否か
□投稿者/ 渋木宏明(ひどり) (737回)-(2008/05/02(Fri) 19:49:29)
渋木宏明(ひどり) さんの Web サイト
> これって、どちらが好ましいでしょうか?

その設定値が「必ず存在するべきものなのかどうか」で

・何もしない
・例外を無視する、など必要な措置を行う

のどちらかです>じぶん

引用返信 編集キー/
■17905 / inTopicNo.4)  Re[2]: 例外処理をするべきか否か
□投稿者/ 通りすがり (17回)-(2008/05/03(Sat) 13:17:03)
あ、説明不足だったかもしれません。すみません。

作っているアプリケーションの設定情報をデシリアライズしようとしてるんですけど、
アプリのバージョンアップに伴って、フィールドが若干増えました。
なので、増えた分は名前で値をとってくることができないので、この質問スレッドを立てさせてもらった次第です。
逆に、削除されたメンバの場合はGet***しようとしないので問題ないですが。

もし、_valが新たに追加されたフィールドの場合、

 _val = info.GetDouble("val");

ってやると死んじゃうので、どうしようかなぁー...、と。
_valは必ず存在するべき値です。
なので、_valが見つからなかったら適当な値で初期化したいんです。
引用返信 編集キー/
■17919 / inTopicNo.5)  Re[3]: 例外処理をするべきか否か
□投稿者/ 中博俊 (1284回)-(2008/05/03(Sat) 19:30:37)
中博俊 さんの Web サイト
わたしもどちらもやりません。
class Hoge1
class Hoge2

を作ってHoge1を読み込んだら内部でHoge2に変換してしまいます。
これで最新バージョンだけを意識したプログラムが可能です。
引用返信 編集キー/
■18239 / inTopicNo.6)  Re[4]: 例外処理をするべきか否か
□投稿者/ 通りすがり (18回)-(2008/05/11(Sun) 20:01:52)
長い間放置してしまいました...。
ごめんなさい ><

中博俊さん、コメントありがとうございます。

No17919 (中博俊 さん) に返信
> わたしもどちらもやりません。
> class Hoge1
> class Hoge2
>
> を作ってHoge1を読み込んだら内部でHoge2に変換してしまいます。
> これで最新バージョンだけを意識したプログラムが可能です。

ということですが...
メンバを追加したいんだったら、既存のクラス(Hogeクラス)に追加するんではなくて、
新しく作ったクラス(NewHogeとか)に持たせて、その新しいクラスが委譲なり継承なりでHogeに対するアクセスを提供してやるということですか?
引用返信 編集キー/
■18245 / inTopicNo.7)  Re[5]: 例外処理をするべきか否か
□投稿者/ 中博俊 (1289回)-(2008/05/11(Sun) 20:48:06)
中博俊 さんの Web サイト
じゃなくって古いバージョンの情報は読み込んだら常に最新版へコンバートするんです。
よくアプリで古いセーブデータを新しいセーブデータへのコンバートするじゃないですか。あれをするわけです。
そうすると本体プログラムは古いバージョンのことは一切考えなくて良くなります。
引用返信 編集キー/
■18254 / inTopicNo.8)  Re[6]: 例外処理をするべきか否か
□投稿者/ 通りすがり (19回)-(2008/05/12(Mon) 12:59:37)
...すみません、頭の仕様がアレなのでいろいろ理解できてません。

まず、コンバートとかそれ以前の問題ですが、
> じゃなくって古いバージョンの情報は読み込んだら常に最新版へコンバートするんです。
ってことは、「それが古いかどうか判断する手段」が必要なわけですね。
今考えているクラスではバージョン番号を整数値で保持しているので、その点問題無しです。
逆に、シリアライズするクラスで変更がありそうなクラスには、「それが古いかどうか判断する手段」を持たせなくてはならない、ということですね。

次に、コンバートに関して
> class Hoge1
> class Hoge2
> を作ってHoge1を読み込んだら内部でHoge2に変換してしまいます。
ということは、Hoge1は修正(メンバの追加)が入る前のクラス、Hoge2がメンバ追加後のクラスということですよね。
別なクラスなんですよね。
んで、自分をHoge2に変換するための仕組みをHoge1の方に持たせてあげるんですね。

となると、
・Hoge2はほぼHoge1と同じソースコードになる
・ソースコード中の「Hoge1」を「Hoge2」に置換していく必要がある
ということになりますよね。

それは避けられないことなんでしょうか。
もっと楽してできないもんなんでしょうか...。



ちなみに、現在は以下のようなことやっています。
デシリアライズ時に、Get***で存在しない名前で値をとろうとすると例外が投げられて、_hogeがnullのままだからHogeをnewし直してます。
だもんで、新たに追加したメンバのおかげで、それまで保存しといた他の多数の情報が捨てられているんです。
こいつらを生かしてあげたいし、追加されたメンバも適切に初期化したい...。
/* あわよくば最小限の手間で。 */

FileStream fs = null;
BinaryFormatter bf = new BinaryFormatter();
try
{
 s = new FileStream(filePath, FileMode.Open);
 _hoge = bf.Deserialize(fs) as Hoge;
}
catch (Exception e)
{
 _hoge = null;
}
if (fs != null) fs.Close();
if (_hoge == null || _hoge.Version != Hoge.LatestVersion) _hoge = new Hoge();

引用返信 編集キー/
■18260 / inTopicNo.9)  Re[7]: 例外処理をするべきか否か
□投稿者/ シャノン (400回)-(2008/05/12(Mon) 14:08:23)
No18254 (通りすがり さん) に返信
> 次に、コンバートに関して
>>class Hoge1
>>class Hoge2
>>を作ってHoge1を読み込んだら内部でHoge2に変換してしまいます。
> ということは、Hoge1は修正(メンバの追加)が入る前のクラス、Hoge2がメンバ追加後のクラスということですよね。
> 別なクラスなんですよね。

そうですね。

> んで、自分をHoge2に変換するための仕組みをHoge1の方に持たせてあげるんですね。

個人的には、Hoge2 側に、Hoge1 を引数に取るメソッドでも追加するほうが好みですね。

> となると、
> ・Hoge2はほぼHoge1と同じソースコードになる
> ・ソースコード中の「Hoge1」を「Hoge2」に置換していく必要がある
> ということになりますよね。

同じ処理に関しては、Hoge1 が Hoge2 側に委譲すればいいんじゃないでしょうか。
引用返信 編集キー/
■18265 / inTopicNo.10)  Re[8]: 例外処理をするべきか否か
□投稿者/ 774RR (166回)-(2008/05/12(Mon) 15:07:29)
なんかすごい話をしているような気がするけど
ソースコード上の Hoge1 をぜーんぶ Hoge2 に置換するなんて俺的にはありえない

// 新しい Hoge の実装
class Hoge {
 シリアライズ関数() {
  if (追加されたメンバーが見つからない) { // ということは旧バージョンを読み込んだ場合だ
   適当に当該メンバーの値をセット; // これで自動的に新しい Hoge になれる
  }
 }
}
っつーことぢゃねーの?

メンバーが見つからないとき=try/catch で引っかかった場合っつーことで。
引用返信 編集キー/
■18288 / inTopicNo.11)  Re[9]: 例外処理をするべきか否か
□投稿者/ 通りすがり (20回)-(2008/05/12(Mon) 18:09:36)
> 個人的には、Hoge2 側に、Hoge1 を引数に取るメソッドでも追加するほうが好みですね。
あ、そうですね。
例えばSystem.Drawing.ColorにもFromArgbっていうのがあったりしますね。

> 同じ処理に関しては、Hoge1 が Hoge2 側に委譲すればいいんじゃないでしょうか。
あれー?やっぱり移譲ですよね。
となるとHoge2がHoge1のインスタンスを保持することになりますね。


> ソースコード上の Hoge1 をぜーんぶ Hoge2 に置換するなんて俺的にはありえない。
はい、僕的にもあり得ないです。やりたくないです。^^;
例に示してもらったソースコードは、私が最初に示したソースコードのコード1の方針なわけですよね。
デシリアライズ時に、シリアライズした情報から名前を探して無ければ適当に初期化する、ってな感じですよね。
シリアライズ関数()っていうのは、シリアライズするところではなくてデシリアライズするところですよね?


うーむ...
引用返信 編集キー/
■18289 / inTopicNo.12)  Re[10]: 例外処理をするべきか否か
□投稿者/ シャノン (401回)-(2008/05/12(Mon) 18:28:26)
No18288 (通りすがり さん) に返信
>>個人的には、Hoge2 側に、Hoge1 を引数に取るメソッドでも追加するほうが好みですね。
> あ、そうですね。
> 例えばSystem.Drawing.ColorにもFromArgbっていうのがあったりしますね。
>
>>同じ処理に関しては、Hoge1 が Hoge2 側に委譲すればいいんじゃないでしょうか。
> あれー?やっぱり移譲ですよね。
> となるとHoge2がHoge1のインスタンスを保持することになりますね。
>

1が2に委譲する(1は2を用いて処理をする)んですから、1が2のインスタンスを持つ、でしょう。
ただ、1の処理をするためには委譲先の2のインスタンスが必要、2のインスタンスを作るためにはコンストラクタで1を渡す、というのでは循環しますから、2のコンストラクタに渡すのは1のインスタンスではないほうがいいですね。
やるとすれば、1のインスタンスを構築するのに必要な全データ、とかにするのがいいでしょう。
引用返信 編集キー/
■18295 / inTopicNo.13)  Re[11]: 例外処理をするべきか否か
□投稿者/ 通りすがり (21回)-(2008/05/12(Mon) 21:51:14)
2008/05/12(Mon) 21:52:17 編集(投稿者)

> 1が2に委譲する(1は2を用いて処理をする)んですから、1が2のインスタンスを持つ、でしょう。
あぁ!思い込みで発言してました。ごめんなさい。

Hoge1がHoge2に移譲デスカ!?
また理解不能になってきました。

Hoge2は新しく追加することになったメンバのみを持ったクラスですよね。
そいつに委譲するんデスカ!?
Hoge2は委譲されるほど高機能じゃないような気がするのですが。

ん?まさかHoge2は
・Hoge1のすべてメンバ
・新しく追加されるメンバ
を持つクラスなのかな...
でも、だとすると、Hoge1はHoge2を持つことで約2倍のヒープを消費することになるわけですね。
しかも、「大量のソースコードをコピペ」という一番やりたくないことになっちゃうわけですよね。
今後またメンバが追加されるかもしれない、と思うとちょっと許容しがたいなぁ。

前述の
> ・ソースコード中の「Hoge1」を「Hoge2」に置換していく必要がある
という問題も依然としてペンディング中。


だいぶ理解が追い付いていないので、誤解だらけかもしれません。
ごめんなさいごめなさいごめんなさい。
引用返信 編集キー/
■18299 / inTopicNo.14)  Re[12]: 例外処理をするべきか否か
□投稿者/ Jitta (474回)-(2008/05/12(Mon) 22:46:46)
# ツリーは適当

 一応、MSDN ライブラリで説明されているんだけど?

バージョン トレラントなシリアル化
http://msdn.microsoft.com/ja-jp/library/ms229752.aspx


ん。。。.NET Framework 1.x の頃の方がいいかもしれない。VS2003 などを持っているなら、それの MSDN ライブラリを参照してみてください。

引用返信 編集キー/
■18304 / inTopicNo.15)  Re[13]: 例外処理をするべきか否か
□投稿者/ 通りすがり (23回)-(2008/05/13(Tue) 01:48:04)
> 一応、MSDN ライブラリで説明されているんだけど?
>
> バージョン トレラントなシリアル化
> http://msdn.microsoft.com/ja-jp/library/ms229752.aspx

キタコレ!!!!!!
Jittaさん、もの凄い情報ありがとうございます。
「トレラント」っていうキーワード知らなくても、このページにたどり着ける情報収集の能力が欲しいです。

ということで、とりあえずは自分が直面していた問題はこれであっさり片付きそうです。

しかし、僕のようなケースは珍しいはずがないですよね。
こういった仕組みが無い言語では、僕が悩んでいるようなことを実際に行わなければならないと思います。
なので、このことについて考えることは大切なことだと思うので、これからも考えていきたいです。


一応解決済みチェックをつけておきますが、引き続きコメント大歓迎です。
解決済み
引用返信 編集キー/
■18307 / inTopicNo.16)  Re[12]: 例外処理をするべきか否か
□投稿者/ シャノン (402回)-(2008/05/13(Tue) 09:44:03)
2008/05/13(Tue) 09:45:22 編集(投稿者)

No18295 (通りすがり さん) に返信
> ん?まさかHoge2は
> ・Hoge1のすべてメンバ
> ・新しく追加されるメンバ
> を持つクラスなのかな...

です。
2はそれ単体で完全な2の実装を持ちます。

もし将来的にバージョン3とか4とか出てきて、1のサポートを停止する、という決断を下したときにも、2が1に依存していると面倒ですが、1が2に依存しているなら切捨ては簡単です。

> でも、だとすると、Hoge1はHoge2を持つことで約2倍のヒープを消費することになるわけですね。
> しかも、「大量のソースコードをコピペ」という一番やりたくないことになっちゃうわけですよね。
> 今後またメンバが追加されるかもしれない、と思うとちょっと許容しがたいなぁ。

コピペではなくて移動ですよ。
1の機能は2に移動して、重複するコードは1にはありません。
引用返信 編集キー/
■18331 / inTopicNo.17)  Re[13]: 例外処理をするべきか否か
□投稿者/ 通りすがり (25回)-(2008/05/13(Tue) 12:59:48)
シャノンさん、ありがとうございます。
なんかいろいろ誤解していたようですみませんでした。
ここまで教えてもらったので、具体的にイメージできてきました。
とりあえずコードを書いて、それでも躓いたらまた来ます。
結果報告も後ほど...。


ちなみに、
> ということで、とりあえずは自分が直面していた問題はこれであっさり片付きそうです。
片付きませんでした。 ゜・(ノД`;)・゜・
ISerializableインターフェースを実装し、デシリアライズ処理をカスタマイズした場合、OptionalFieldは効かないようです。

結局これに頼ることはできないみたいなので、Hoge1とHoge2の方の案で進めていきたいと思います。
引用返信 編集キー/
■18384 / inTopicNo.18)  Re[14]: 例外処理をするべきか否か
□投稿者/ 通りすがり (26回)-(2008/05/13(Tue) 22:41:04)
とりあえず、書いてみました。
これでいかがでしょうか...。
だいぶ長いです。申し訳ありません。

Hogeクラスに新しいメンバ_xを追加したという想定です。

//////////////////////////////
// 旧バージョンのHoge
//////////////////////////////
[Serializable]
class Hoge : ISerializable
{
 int _n;
 string _str;

 public Hoge(int n, string str)
 {
  _n = n;
  _str = str;
 }

 public int N
 {
  get { return _n; }
  set { _n = value; }
 }

 public string Str
 {
  get { return _str; }
  set { _str = value; }
 }

 public void Greet()
 {
  Console.WriteLine("Hi, I'm Hoge.");
 }

 /// <summary></summary>
 public Hoge(SerializationInfo info, StreamingContext context)
 {
  _n = info.GetInt32("n");
  _str = info.GetString("str");
 }

 public void GetObjectData(SerializationInfo info, StreamingContext context)
 {
  info.AddValue("n", _n);
  info.AddValue("str", _str);
 }
}


//////////////////////////////
// メンバが追加された後のHoge
//////////////////////////////
[Serializable]
class Hoge : ISerializable
{
 // 委譲先
 Hoge2 _hoge2;

 public Hoge(int n, string str)
 {
  _hoge2 = new Hoge2(n, str);
 }

 public int N
 {
  get { return _hoge2.N; }
  set { _hoge2.N = value; }
 }

 public string Str
 {
  get { return _hoge2.Str; }
  set { _hoge2.Str = value; }
 }

 public double X
 {
  get { return _hoge2.X; }
  set { _hoge2.X = value; }
 }

 public void Greet()
 {
  _hoge2.Greet();
 }

 public override string ToString()
 {
  return _hoge2.ToString();
 }

 public Hoge(SerializationInfo info, StreamingContext context)
 {
  int n = info.GetInt32("n");
  string str = info.GetString("str");

  // 移譲先を作る
  _hoge2 = new Hoge2(n, str);
 }

 public void GetObjectData(SerializationInfo info, StreamingContext context)
 {
  info.AddValue("n", _hoge2.N);
  info.AddValue("str", _hoge2.Str);
  info.AddValue("x", _hoge2.X);
 }
}

//////////////////////////////
// Hogeの仕事を任されてくれるHoge2
//////////////////////////////
[Serializable]
class Hoge2 : ISerializable
{
 int _n;
 string _str;

 // 新しいメンバ
 double _x;

 public Hoge2(int n, string str) : this(n, str, 42.0) { }

 public Hoge2(int n, string str, double x)
 {
  _n = n;
  _str = str;
  _x = x;
 }

 public int N
 {
  get { return _n; }
  set { _n = value; }
 }

 public string Str
 {
  get { return _str; }
  set { _str = value; }
 }

 public double X
 {
  get { return _x; }
  set { _x = value; }
 }

 public void Greet()
 {
  Console.WriteLine("Mmm..., Am I Hoge?");
 }

 public override string ToString()
 {
  return string.Format("_n={0}, _str={1}, _x={2}", _n, _str, _x);
 }

 public Hoge2(SerializationInfo info, StreamingContext context)
 {
  _n = info.GetInt32("n");
  _str = info.GetString("str");
  _x = info.GetDouble("x");
 }

 public void GetObjectData(SerializationInfo info, StreamingContext context)
 {
  info.AddValue("n", _n);
  info.AddValue("str", _str);
  info.AddValue("x", _x);
 }
}



> ・ソースコード中の「Hoge1」を「Hoge2」に置換していく必要がある
ってのは大きな誤解だったんですかね。
そのままHogeを使ってもよさげ...、なのかな。
いや、でもHogeを使ってたんじゃ、_xは常にデフォルト値(ここではHoge2のコンストラクタで与えた42)が使われるってことか。
...ぁあれ?
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -