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

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

ただいまの
回答率

88.63%

[C#] outとreturnのどちらが推奨される書き方ですか?

受付中

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 379

pokudama

score 0

outとreturnのどちらが効率がいいのでしょうか?

C#での話です。

以下の2つのコードはメモリ使用に関して、どちらの方が効率(合理的)がいいのでしょうか?

該当のソースコード

// その1
List<string> funcA(List<string> strList)
{
  // strListに関する処理。追加したり、削除したり。例えば以下の通り。
  for(int i = 0; i < strList.Count; i++)
  {
    if (strList[i] == "abc")
      strList[i].RemoveAt(i);
  }
  return strList;
}
void funcB(ref List<string> strList)
{
  // strListに関する処理。追加したり、削除したり。例えば以下の通り。
  for(int i = 0; i < strList.Count; i++)
  {
    if (strList[i] == "abc")
      strList[i].RemoveAt(i);
  }

  return;
}

メモリに詳しくはないのですが、その1はList<>を新しく作っているような感じで、その2はList<>をそのまま返しているような気がします。
自分としては、「感じで」「気のせいで」というぐらいの知識なので、どうにも自信がありません。
(その2はrefは必要ないかもしれませんが、つまり引数で返すということを示したつもりです。)

よろしくお願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Zuishin

    2020/08/05 08:26 編集

    効率はほぼ同じですが、微妙な効率化よりもバグの入りにくいコードが推奨されます。その点で、中身のまずさは別にしても、List を引数にして書き換える副作用を持つこの二つのコードはどちらも良くありません。

    キャンセル

  • fana

    2020/08/05 10:19

    > その1はList<>を新しく作っているような感じで、その2はList<>をそのまま返しているような気がします。

    ここから認識が間違っていると見えます.

    キャンセル

  • kikukiku

    2020/08/05 11:50

    回答ではありません。
    Zuishinさんの意見と同じです。
    fanaさんが指摘しているように理解は不十分に思います。
    もっと混乱するかもしれませんが、下記を理解できれば良いと思います。
    https://ufcpp.net/study/csharp/sp_ref.html
    その1で戻り値をviodに変更しても、読み出し元では、変化があることに注意。

    キャンセル

  • SurferOnWww

    2020/09/18 09:52

    質問者さん、最初の投稿以来無言ですが、多々回答がされていますのでそれらに対するフィードバックを返してください。役に立った/立たなかったぐらいの返事はすぐ返せるはず。役に立たなかったならどこが質問者さんの期待と違うのかも書いていただけるとありがたい。とにかく無言は NG です。マナー的にも。

    キャンセル

回答 3

+2

まず, List<string> とは, 標準のクラスである System.Collections.Generic.List<T> ですよね?
以下はそうであると仮定します.

そのコードは恐らく問題があるので要修正ではあるのですが,
質問の本質はそこでは無さそうなので割愛します.

まず, 結論としてはほぼ変わりません.
以下はその理由になります.

System.Collections.Generic.List<T> は class です.
C# には値型と参照型の区別があります.
struct や enum などの型は値型として, class は参照型として扱われます.

参照型である List クラスの実体 (インスタンス) を作るとします.
作られた List インスタンスは変数に直接結びつけられるのではありません.
List インスタンスがメモリ上のどこかに作られた上で,
作られた List インスタンスがどこにあるのかを示す「参照」という値 (参照の値, 参照値) が作られ,
変数に格納されるのは「参照」なのです.
この「参照」という値はただ場所を示すだけなので, サイズは小さなものです.
数値1つ渡すのと大差ないと考えて下さい.

funcA() のように普通にメソッド引数として渡す場合 (「値渡し」と呼びます) も,
変数に格納される場合と同じく「参照という値」が「値渡し」されます (参照の値渡し).

funcA(ls);  // ls は List<string> インスタンスへの参照.

// 略

List<string> funcA(List<string> strList)  // strList に参照値が値渡しされる.
{
    // 略
    strList.RemoveAt(i);  // strList の参照先のインスタンス (= ls と同じもの) を操作.
    // 略
}

渡されているのが参照であるため, funcA() 内で strList の参照先を変更しない限り,
結局のところ ls を直接操作するのと変わりません.
なぜなら ls と strList は全く同じインスタンスを参照しているからです.

それに対してメソッド引数に refinout などを付けた場合 (「参照渡し」. 「参照の値渡し」と混同しないよう注意) は,
いわば「変数そのもの」が引数に渡されることになります.

funcB(ls);

// 略

void funcB(ref List<string> strList)
{
    // 略
    strList.RemoveAt(i);  // strList は ls そのものを指す.
    // 略
}

確かに ls の参照を strList へとコピーするためのメモリ消費はあるかも知れませんが,
それは説明した通り数値1つ渡すのと変わらないコストです.
C# の処理系の大きさから考えると全く気にするものではありません.

refinout などを使う場面はもっと別の場面です.

まず out. 引数は渡す前に初期化が必須ではなく, 逆にメソッド内では必ず変更されます.
戻り値以外に何かしらの結果を得たい場合に使う感じですね.

次に in. 引数は渡す前に必ず初期化されている必要があり, メソッド内では変更されません.
class ではほぼ意味がありません. 巨大な struct を渡す場合などに,
コピーせずにそのまま参照するために使われるものと思われます.

そして ref です. 引数は渡す前に初期化されている必要があります.
メソッド内では変更されるかも知れませんし, されないかも知れません.
引数として渡してもらう際に設定されている値を使った上で,
そこに何かしらの変更を加える可能性がある場合に使うということになるでしょう.
...ですが, class の場合は普通の引数でも参照の値渡しなので, 普通に渡せば済みます.

in も ref も class であればほとんど意味はありません.
参照であれば, そもそも巨大なコピーは発生しないのですから.
out だけは class であっても使う場面がありますね.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

outとreturnのどちらが推奨される書き方ですか?

両者を利用する目的が間違っている と思います。

out パラメーター修飾子 と return ステートメント はそもそも 違う機能 です。
ですから、どちらが推奨なのか比較するのは正しくありません。

メモリ効率を減らすという目的のためにどちらかを選ぶのではなく、それぞれの機能が持つ  本来の目的 に応じて使い分けてください。

目的に応じてコーディングを行うことで読みやすいコードができます。
そして、読みやすいコードは一目で動きがイメージできますから、バグが発生しにくいし、直しやすいです。

それを意識した上で、よく考えてください。
より合理的なのは、メモリの効率ではなく 開発の効率 を上げることではないでしょうか。

outとreturnのどちらが効率がいいのでしょうか?

C# のメモリ管理 を学ばれることを強くお勧めします。

それを踏まえて回答すると  funcA と funcB はインスタンスの生成をしていない ため、strList やその要素の値が新しくメモリに割り当てられることはありません。(すなわち、メモリの使用率は増加しません)

強いていうならば、for で使われているカウンタ変数 i 分のメモリ (int型なので4byte) が割り当てられるぐらいでしょうか。しかし、ガベージコレクションによって割り当てられたメモリはいずれ解放されます。

また、strList から string 型の要素を削除しています。この処理については、むしろメモリの使用量が減るはずです。

以上のメモリ事情の観点から、funcA と funcB のメモリに関する効率に大差はないでしょう。


そもそもの話、C# でメモリの効率を気にしても仕方がありません。 

C# におけるメモリ管理を知ること自体は大切です。しかし、よほどメモリを短時間で大量に割り当てたり解放したりするようなコードを書かなければ大きな問題になることはありません。
ですから、通常のアプリケーション開発では深くまで考慮する必要はありません。

しかし、それ以上にメモリの効率を気にするのであれば、他の言語を検討に入れることも考えてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/05 12:39 編集

    また、余談にはなりますが、今回の例題の funcA と funcB の場合、LINQが使えます。
    わざわざこれ等のメソッドを定義する必要はありません。

    var strList = new List<string>();
    var list = funcA(strList);
    というコードは
    var strList = new List<string>();
    var list = strList.Where(x => x != "abc").ToList();
    と書く事ができ、どちらも list には pokudama さんが期待しているであろう結果が返ります。

    これも開発効率を上げるための1つの手段です。

    キャンセル

0

以下の2つのコードはメモリ使用に関して、どちらの方が効率(合理的)がいいのでしょうか?

メモリ使用の効率化云々の話はどちらも関係ないと思いますけど。

どういう違いがあるかと言うと:

List<string> funcA(List<string> strList)

引数の渡し方は C# のメソッドのデフォルトの値渡し(別の入れ物に中身をコピーして渡す)。

引数の List<string> は参照型なので渡すのはオブジェクト(インスタンス)を指すアドレス情報。値渡しなのでメソッド側で用意した別の入れ物 strList にアドレス情報をコピーして渡している。

つまり、呼び出し元の変数とメソッド側の変数の中身は同じアドレス情報即ち単一のオブジェクトを指している。

なので、メソッド側で変数が指すオブジェクトを変更すると、呼び出し元の変数が指すオブジェクトでも同じ結果となる。return するのは意味がない。

void funcB(ref List<string> strList)

引数の渡し方は参照渡し(入れ物を中身ごと渡す)。

基本は上記と同じだが以下の点が異なる。

呼び出し側の入れ物とメソッドに渡された入れ物 strList は同じになるので、メソッド側で新しくオブジェクト(インスタンス)を生成してそれのアドレス情報を入れ物に代入すると、呼び出し元の入れ物の中身が指すオブジェクトも、メソッド側で生成した新しいオブジェクトになる。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る