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

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

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

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

Q&A

4回答

24827閲覧

参照渡しで値を変えるか、戻り値を返すか

TOEICengineer

総合スコア36

C#

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

1グッド

0クリップ

投稿2016/07/11 14:18

既存システムで、戻り値をvoiにして、refを使って、値を代入してるコードを見ました。

かなりの処理にわたりrefで値を変えている。。

アドレスを参照してるので、早いとは思うのですが、入力値としての引数と、値が変わる引数が混在してるのは、個人的に好きではないのですが、、、

C言語っぽい感じがするんです。。。

しかも、voidですから、最低でもどのうような状態でおわったのか、戻り値で知らせるべき。。

もしくは、例外を投げるべき。。。

大規模になると、コードを追っていくのがわかりずらくなると思うんですが。。

戻り値を返し、メッセージパッシングをすべきだと思うのですが、、、

どのように使い分けているのでしょうか?

mpyw👍を押しています

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

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

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

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

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

guest

回答4

0

私の偏見ですが、そのコードを書いた人は元VB6(≠VB.NET)プログラマーだったのでは無いでしょうか?VB6時代の文法なんて、今時はExcel等のマクロに使うVBA(厳密には同じでは無い)ぐらいでしかお目にかかる事はありませんが、次のような特徴を持ってました。

  • 戻り値を返さない本当のサブルーチン(Sub)と戻り値を返す関数(Function)が別にある。(これだけはVB.NETも同じ)
  • 関数(Funciton)の戻り値の返し方が、Return文ではなく、関数名 = 値というものだった。しかもそこで処理が終わるわけでは無い。(なぜ、こんな書き方にしたのか私には理解できない。)
  • 引数のデフォルト(指定しない場合)がByRef(参照渡し)だった。(逆に、VB.NETからは省略すると勝手にByVal(値渡し)が付くようになったそうです)

Subの方が短い。関数名に代入が可読性を落とす(ような気がする)。デフォルトは参照渡しだから、そのまま書き換えた方が便利。等と思って、VB6時代に関数(function)ではなくSubを用いた手続き(procedure)を書くことがその人の主流になってしまったと思われます。

このような書き方はVB6時代の負の遺産だと私は思っています。C#では次の理由で(巨大な構造体を使っているなど特定の条件下を除いて)refを使うことは避けるべきです。

  • refで参照渡し(call by reference)にすることで速度面の恩恵を受けられるのは、値型である構造体のみです。(通常の参照型の値渡しは参照の値渡し(別名:共有渡しcall by sharing)になるため、値渡しだと遅くなるわけではありません。また、intなどの値型の単純型は、参照を同じするという処理が無い分、値渡しの方が速いと考えられます)
  • 複数の戻り値を返したい場合は、出力であることが明示できるoutを使うべきです。(入力と出力を兼任させる事は、コードを複雑にするため、避けるべきです。また、C#7以降はタプルを返せるようになるようですので、タプルを使うべきです。)

参照渡しができる言語は少ないほうであり、メジャーな言語としては、F#、VB.NET、C++、PHP、Perl(でも、Perl6にはなさそう?)ぐらいしかありません。Cはポインタの値渡しはできますが、参照渡しはできません。Javaは値渡ししかありません(オブジェクトは参照値を値渡ししているだけです)。PythonとRubyは全て参照の値渡しです。

  • C++のclass/structはC#でいう値型の構造体であるため、参照渡しは速度面で有利です。とくに、巨大なclass/structをC++で参照渡しをすることは大変意味があります。ただ、戻り値の一つとして扱わない場合は、constをつけて変更不可にすべきでしょう。
  • F#は.NETで他にあわせるために参照渡しがあるだけのようです。複数の戻り値を返す場合はタプルを使うべきでしょう。
  • PHPはarrayが値渡しであるという時点で、察してください。
  • PythonやRubyでは、すべてのオブジェクトがC#でいう参照型であり、速度面で参照渡しにする意味がありません。また、配列やタプルで複数の戻り値を返すことができるため、複数の戻り値のための参照渡しも意味がありません。ですので、参照渡しは初めから存在しません。

このように、参照渡しがある言語では速度や複数戻り値などの必要に迫られて実装しており、その必要がない言語では初めから実装しないようにしてあります。つまり、参照渡しは必要にならない限り、使うべきではないというのが私の中の結論です。不必要にrefを使っている、そんなVB6っぽいコードはさっさと捨てる事をお勧めします。

投稿2016/07/11 18:55

raccy

総合スコア21737

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

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

0

こういうのは意見が多い方がいいので特別な意見は無いのですが。。。

多分、書いた人の癖ですね。Cでは、戻り値をintにしてエラーコードを返す人がいるので、その手の話だと思います。

その言語で一般的に書かれている書き方をどのていど踏襲するかはそれぞれ意見があると思いますが、まあ、取り敢えず三人が”良くはない”とわざわざ書き込んだという程度には良くはないといえます。

また、returnで値戻せないので、ソースコードを読みづらくしている可能性が高いです。

アドレスを参照してるので、早いとは思うのですが、

(画像処理や機械学習など計算量がよほど多くないと)恐らく変わりません。渡しているのがオブジェクトであれば、refと同様ポインタが渡されます。基本型であれば、何バイトか増える程度なので、ファイルIOやDBアクセスやネットワーク通信がある通常のプログラムでははっきりした差にはならないはずです。


追記

しかも、voidですから、最低でもどのうような状態でおわったのか、戻り値で知らせるべき。。

見落としていました。本題とは直接の関係ないですが・・・
状態を伝える場合は、例外を使うのがC#の一般的な流儀です。
(致命的なエラーでなくても、例外は使います。)

投稿2016/07/11 16:02

編集2016/07/11 16:27
iwamoto_takaaki

総合スコア2883

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

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

0

便利な時もありますが、
基本的には可読性が悪いので、止めれるなら止めるべきだと思いますね。
使い分けというより、「使わない」って感じでしょうか。
もしくは使わない努力をする。

かなりの処理にわたりrefで値を変えている。。

この一言で何となくソースコードのイメージがつきます。
最悪だと思いますね。

参照から値を変更ってのは、関数型言語の思想とも反しますし、
もし今後、関数型言語をする時がきたら、
日常的に参照渡しを行っていたオブジェクト指向プログラマーはつらいかもしれないので、
やはり止めれるなら止めといた方がいいと思います。
ref変数を使うのでなくリターンすべきだと思います。

投稿2016/07/11 14:40

root_jp

総合スコア4666

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

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

0

こんにちは。

多数の関数群の間で、引数の位置を統一したかったということはないでしょうか?
後、outでなくrefと言うことは入出力しているとか?

そのどちらでもないか、失敗があり得る関数なのに、例外でもなく戻り値でもなくref変数で失敗を返しているとしたら、う~ん、そのプログラマの心は想像できません。
あまり良いI/F設計ではない印象を受けます。

投稿2016/07/11 14:43

Chironian

総合スコア23272

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問