web-dev-qa-db-ja.com

refを使用する場合とC#で不要な場合

プログラムのメモリ状態にあるオブジェクトと、状態を変更するためにオブジェクトを渡す他のワーカー関数もあります。ワーカー関数にrefで渡しています。しかし、私は次の機能に出くわしました。

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

received_sremoteEPの両方が関数から何かを返しているため、私を混乱させます。なぜremoteEPrefを必要とし、received_sは必要ないのですか?

私はCプログラマーでもあるため、頭からポインターを取得するのに問題があります。

編集:C#のオブジェクトは、内部のオブジェクトへのポインターのようです。そのため、オブジェクトを関数に渡すと、ポインターを介してオブジェクトの内容を変更できます。関数に渡されるのはオブジェクトへのポインターだけなので、オブジェクト自体はコピーされません。関数内でダブルポインターのような新しいオブジェクトを切り替えたり作成したりするには、refまたはoutを使用します。

100
Rex Logan

短い答え:私の 引数の引き渡しに関する記事 を読んでください。

長い答え:参照タイプのパラメーターが値で渡される場合、参照のみが渡されますnotオブジェクトのコピー。これは、CまたはC++で(値で)ポインターを渡すようなものです。パラメータ自体の値の変更は呼び出し元には見えませんが、参照が指すオブジェクトの変更はwillに見えます。

パラメータ(任意の種類)が渡された場合by参照、つまり、パラメータの変更は呼び出し元に表示されます-パラメータの変更are変数の変更。

もちろん、この記事ではこれらすべてについて詳しく説明しています:)

有用な答え:ref/outを使用する必要はほとんどありません。これは基本的に別の戻り値を取得する方法であり、メソッドが多すぎることを試みている可能性があるため、通常は正確に回避する必要があります。常にそうとは限りません(TryParseなどはoutの合理的な使用の標準的な例です)が、ref/outを使用することは比較的まれです。

205
Jon Skeet

非参照パラメーターはポインターであり、参照パラメーターは二重ポインターであると考えてください。これは私を最も助けました。

参照によって値を渡すことはほとんどありません。相互運用性の懸念がなければ、.Netチームはそれを元の仕様に含めなかっただろうと思います。 OO refパラメータが解決するほとんどの問題を処理する方法は次のとおりです。

複数の戻り値の場合

  • 複数の戻り値を表す構造体を作成する

メソッド呼び出しの結果としてメソッド内で変化するプリミティブの場合(メソッドはプリミティブパラメータに副作用があります)

  • インスタンスメソッドとしてオブジェクトにメソッドを実装し、メソッド呼び出しの一部としてオブジェクトの状態(パラメーターではなく)を操作する
  • 複数の戻り値のソリューションを使用して、戻り値を自分の状態にマージします
  • メソッドによって操作できる状態を含むオブジェクトを作成し、プリミティブ自体ではなく、そのオブジェクトをパラメーターとして渡します。
26
Michael Meadows

おそらくC#アプリ全体を記述し、参照によってオブジェクト/構造体を渡すことはできません。

これを教えてくれた教授がいました:

Refを使用する唯一の場所は、次のいずれかです。

  1. 大きなオブジェクト(つまり、オブジェクト/構造体の内部にオブジェクト/構造体を複数のレベルに渡す)を渡したい場合、それをコピーするとコストが高くなり、
  2. フレームワーク、Windows API、またはそれを必要とする他のAPIを呼び出しています。

できるからといってそれをしないでください。 paramの値の変更を開始し、注意を払っていない場合、いくつかの厄介なバグが発生する可能性があります。

私は彼のアドバイスに同意し、学校から5年以上経っても、FrameworkまたはWindows APIを呼び出す以外にその必要性はありませんでした。

9
Chris

Received_sは配列であるため、その配列へのポインターを渡します。この関数は、既存のデータを所定の場所で操作し、基礎となる場所やポインターを変更しません。 refキーワードは、実際のポインターを場所に渡し、外部関数でそのポインターを更新しているため、外部関数の値が変更されることを示します。

例えば。バイト配列は、メモリが更新された前後の同じメモリへのポインタです。

エンドポイント参照は、実際には、外部関数のエンドポイントへのポインターを、関数内で生成された新しいインスタンスに更新しています。

3
Chris Hynes

Refは、ポインタを参照渡しすることを意味すると考えてください。 refを使用しないということは、値でポインターを渡すことを意味します。

さらに良いことに、私が言ったことを無視し(特に値の型については誤解を招く可能性が高い)、 このMSDNページ を読んでください。

3
Brian

Jon Skeetの全体的な答えと他のいくつかの答えに同意しますが、refを使用するユースケースがあります。これはパフォーマンスの最適化を強化するためのものです。パフォーマンスプロファイリング中に、メソッドの戻り値を設定するとパフォーマンスにわずかな影響がありますが、引数としてrefを使用して戻り値をそのパラメーターに設定すると、このわずかなボトルネックが削除されます。

これは、最適化の努力が極端なレベルまで行われ、読みやすさ、そしておそらくミリ秒またはスプリットミリ秒を節約するためのテスト容易性と保守性を犠牲にする場合にのみ有用です。

0
Jon Davis

私の理解では、Objectクラスから派生したすべてのオブジェクトはポインターとして渡されますが、通常の型(int、struct)はポインターとして渡されず、refが必要です。文字列についてはわかりません(最終的にはObjectクラスから派生していますか?)

0
Lucas