- PR -

C# 戻り値の型を動的に変更することは可能ですか?

投稿者投稿内容
Atsushi.Eno
ベテラン
会議室デビュー日: 2003/04/23
投稿数: 60
投稿日時: 2005-05-01 21:02
引用:

例外を使えだけではなく、Object 型が利用できることが最初のレスで付いたことは、すばらしいことだと思いますけど、そんなに偏っていますかね?


objectで返すのはイケてない、だから例外を投げるのがいい、と、勝手なユースケースを想定して話を進めているのがイケてないから、ref/outの話を書いたんですが。object型で値を返すことのデメリットは(ダブルミーニングという観点でも指摘されましたが)、不必要なboxingが生じてしまうことにもあります。イケてないといっても例外を投げるよりはずっとマシですが。

被害妄想がどーのこーのというのは意味不明なのですが(「被害」って何です?)、ダブルミーニングによる危険性の方が、常に例外を投げて呼び出し元で誰彼かまわずcatchしてしまう危険性より高いという決めつけが理解できません(そう主張しているんですよね。「定説」なんですから)。あなた(誰だか知りませんが)は何度もダブルミーニングや複数戻り値が危険である、何で分からないんだ、と書き続けていますが、誰もダブルミーニングが危険ではない、なんて言ってないのです(なるべく安全にしようとしているということは書きましたが)。

VBのERR云々も「かもしれませんが」と書いていることから理解出来て当然だと思いますが、あくまで推測ですし、拘るところでもないと思いますが。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-05-01 21:14
最初の例を、最初の質問通り、動的に戻り値の型を変更するとなると、このようなコードになります。
コード:

object myFucntion(int value1) {
try {
return value1 * 10000;
} catch (Exception e) {
return e.ToString();
}
}


このメソッドの呼び出し側は、次のようになるでしょう。
コード:

int val;
string err;
object obj;
obj = myFucntion(some_value);
if (obj is string) {
err = (string) obj;
// 何か、エラーの処理
} else if (obj is int) {
val = (int) obj;
// 何か、エラーでないときの処理
} else {
// 戻り値が変
}


 最初に私が提示したものと比べてみてください。最初のコードが、かなりすっきりしていることがわかると思います。
**編集→「最初のコードが」を追加。

 私が例外として実装することを薦めるのは、この呼び出し側のコードをすっきりさせる、つまり、使うときに迷わない、というところです。

 今回、myFunctionは、int型かstring型しか返しませんが、それは本当でしょうか。誰がそれを保証するのでしょう?
 objectクラスは、.NET Frameworkでは、すべてのクラスの基本クラスです。どんなクラス宣言をしようが、暗黙的に親クラスとして指定されます(値型については知りません)。object型を返すということは、どんな型でも返せるということでもあります。もし、戻り値をint型としていれば、コンパイル時にcatchブロックのreturn文がエラーになるでしょう。しかし、object型を返すことになっているため、int型とstring型以外の型を返そうとしていても、そのミスは検出されません。
 そのため、if文の判定で、intとstringに加え、elseを追加しています。
**以下追加
 また、2回目の投稿で、『ほとんど必要ない検査を毎回実行するというのもパフォーマンスが悪いと思います』と書きました。このコードでは、戻り値がエラーが発生したかという検査を先に行っています。エラーが発生する状況は、正常な状況よりも少ないはずですから、エラーであるかという検査を先に行う分、パフォーマンスが下がります。もちろんこれは、検査の順番を入れ替えればすむことです。しかし、そこまで考えて呼び出し側をコーディングしなければならない、とも言えます。
**ここまで追加

 このように呼び出し、というよりそのあとのコードが複雑になると、保守の手間を大幅に増加させます。2〜3ヶ月経てば、自分が書いたコードも他人コードです。何をやっていたのかわからなくなります。もちろん、ドキュメントが整理されていればそんなこともないのですが、理想と現実にギャップがあることは周知の事実です。

[ メッセージ編集済み 編集者: Jitta 編集日時 2005-05-01 21:47 ]
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2005-05-01 22:46
http://www.microsoft.com/japan/msdn/net/bda/exceptdotnet.asp
ここに下記のような記述があります。

引用:

例外は適切に使用する
例外の送出は、コードの仮定に外れた条件が発生したときにのみ行います。
言い換えると、意図した機能を提供するための手段として例外を使用してはなりません。
たとえば、ユーザーはアプリケーションにログオンするときに無効なユーザー名やパスワードを
入力することがあります。この場合、ログオンは成功しませんが、
これは予期された有効な結果であり、例外が送出されるべきではありません。





引用:

int finalValue= value1*10000;



よって、この演算結果がオーバーフローすることが、newborn氏の作っているプログラムで
どのような意味をもつのかが不明確なので結論はでないのではないでしょうか。



引用:

イケてないといっても例外を投げるよりはずっとマシですが。



これはさすがに言いすぎでしょう。
Jitta氏が戻り値をobjectにした例を書かれていますが、これをマシとは呼べないですよね
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-05-02 09:40
outパラメータを使う例
コード:
// 呼び出し
string err = "";
int ret;
ret = myFunction(value, err);
if (err.Length == 0) {
    // エラーの処理
} else {
    // 正常の処理
}

// myFunction
int myFunction(int value, out string err) { // こんな宣言だっけ?
    if (value > ???) { // 1万倍するとintの範囲を超える値
        err = "オーバーフロー";
        return -1; // とりあえず-1にするが、意味のない値にする必要あり
    }
    err = "";
    return value * 10000;
}


 う〜ん、余計にしんどいです。
 先のobjectで返す方法と違って、エラーかどうかの判定を必ず行わなくてはなりません。
 例が単純だったので、例外が発生する状況がオーバーフローするということだけと思われたため、例外を発生させずに検査するようにしてみました。

 実はこれ、バグがあります。負数の側の検査をしていません。しかし、コードからそれを読みとるのは困難ではないでしょうか。
 また、エラーの時の戻り値を-1にしていますが、コメントに書いているとおり、業務ロジック上意味のない値にしておく必要があります(他所でのバグを回避するため)。業務上何らかの変更があって、意味のない値が変更になったとき、この値を変更しなければなりません。
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2005-05-02 10:27
返り値をobjectにするなどというのはJittaさんの説明からも論外というのは分かるとして、
引用:

burton999さんの書き込み (2005-05-01 22:46) より:
よって、この演算結果がオーバーフローすることが、newborn氏の作っているプログラムで
どのような意味をもつのかが不明確なので結論はでないのではないでしょうか。


オーバーフローすることが仮定に含まれているというのなら、そのような作り自体が問題だと思います。
返すのは引数を1万倍したintの値です。
もしintの範囲を超えても良いなら、返り値をDecimalなどにすれば良いだけです。
そうしていないのですからintの範囲を超える場合はその値を使って作業を続行できない(しても意味がない)ということです。実際はもっと小さい値かもしれませんね。(-9,999〜9,999とか)
ですので、まず"1万倍する"というメソッドに渡す前に値のチェックを終えておくのが「意図した機能を提供するための手段として例外を使用し(ない)」ための正しいつくりだと思います。

newbornさんのメソッドで例外を投げるのは何の問題もありません。


[ メッセージ編集済み 編集者: 一郎 編集日時 2005-05-02 10:30 ]
未記入
ぬし
会議室デビュー日: 2004/09/17
投稿数: 667
投稿日時: 2005-05-02 10:55
引用:
CLRでは、例外はアプリケーションエラーやシステムエラーを表現するものなので基本的に業務フローから出るときに使用されます。


なるほど .NET の例外(Exception) は Java の例外(Exception) に対応するものではなく、Java のエラー(Error) に近い位置付けになるようですね。そのようなデザインルールになっているということであれば「投げっぱなし」「キャッチすべきではない」というのも納得せざるを得ないですね。.NET では Java でいうところの例外機構が扱えないということなので、不便そうな感じはしますが・・・。

引用:
「被害」って何です?


私は、ref/out とやらの手法に対して、プログラマが気付きやすいとはいえ if文での判定を必要とする危険性は同じようにあります、と補足しただけなんですが。それに対して「誤解され」「信用されず」「全く使用されなくなってしまう」と風評被害にあったかのように言っているので、被害妄想かなあ〜と。誰も ref/out を否定していないのに、「誤解され」「信用されず」と感じること自体が被害妄想だよねえ。

引用:
VBのERR云々も「かもしれませんが」と書いていることから理解出来て当然だと思いますが、あくまで推測ですし、拘るところでもないと思いますが。


いや、推測だからということではなく。ref/out も ERR もプログラマが if文を書くという点では同じでしょ。それなのに、自分ひとりで両案を持ち出して、ref/out は ERR よりずっと安全だ! とか言っているのが自作自演で滑稽だなあと思ったの。そんな自作自演してれば、そりゃ話もあさっての方向に行くだろうよ。だって、例外との比較がまったくなされていないんだもの。

とはいえ、結局のところ .NET のことを良く知らない私が(Javaを念頭において)例外の安全性を述べたことが場を乱してしまったようですね。すみませんでした。.NET ではユーザーコードで例外は「投げちゃいけない」「キャッチしちゃいけない」そもそも例外は使っちゃいけないものだったんですね。いろいろと勉強になりました。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-05-02 20:38
引用:

未記入さんの書き込み (2005-05-02 10:55) より:

とはいえ、結局のところ .NET のことを良く知らない私が(Javaを念頭において)例外の安全性を述べたことが場を乱してしまったようですね。すみませんでした。.NET ではユーザーコードで例外は「投げちゃいけない」「キャッチしちゃいけない」そもそも例外は使っちゃいけないものだったんですね。いろいろと勉強になりました。


投げちゃいけない、キャッチしちゃいけない、というのは、極論かな?と。

 例えば、ファイルを扱う場合、前もって検査することはできません。例外が投げられます。これはキャッチするべきでしょう。そうでないと、ユーザにとっては意味不明なメッセージが表示されますから。

 「キャッチしろ」というのではなく、「キャッチしちゃいけない」は極論過ぎる、ということで。結局、ケースバイケースでしょう。
_________________
nak2k
ベテラン
会議室デビュー日: 2003/07/17
投稿数: 86
投稿日時: 2005-05-03 04:46
未記入さんの意見への補足かな?

ここらへんの話、私なりに整理するとこんな感じですかね。

コード:
【仕様レベル】

  +----------+------------+-----------+
  | 正常応答 | エラー応答 | 異常応答  |
  |          | (*1)     | (*2)    |
  +----------+------------+-----------+
  |     正常系            |   異常系  |
  +-----------------------+-----------+

  *1 … 入力エラーや権限不足エラーなど
  *2 … システムエラー、アプリケーションエラー

【DotNET】

  +----------+------------+-----------+
  |  戻り値(ref/out含む)| Exception |
  +-----------------------+-----------+

【Java】

  +----------+------------+-----------+
  | 戻り値   | Exception  | Error     |
  |          | (検査例外) |           |
  +----------+------------+-----------+



>> Jittaさん
未記入さんが言われているのは、「DotNETの例外はJavaの検査例外とは別物だから、Javaの検査例外と同じような感じで『投げちゃいけない』、『キャッチしちゃいけない』のですね」ってことなんだと思います。

ここらへん私も前々から気になっておりまして、DotNETのデザインガイドに厳密に従うと、エラー応答処理があちこちに分散するか、もしくはあちこちのメソッドでエラー情報を返すoutパラメータだらけになるかと思うのですが……、実際問題、どのような設計をされてますか?

私はデザインガイド違反を承知でJavaっぽくやっちゃってます^^;

スキルアップ/キャリアアップ(JOB@IT)