web-dev-qa-db-ja.com

C#でpinvokeを使用する場合の[In、Out]とrefの違いは何ですか?

C#からC++にパラメーターを渡すときに、[In、Out]を使用することと、単にrefを使用することには違いがありますか?

いくつかの異なるSOの投稿と、MSDNからのいくつかのものもありますが、私の質問に近づいていますが、完全には答えていません。私の推測では、refを安全に使用できます。 [In、Out]を使用するのと同じように、マーシャラーの動作が異なることはありません。私の懸念は、動作が異なることと、C++がC#構造体の受け渡しに満足できないことです。両方を見てきました。私が働いているコードベースで行われたこと...

これが私が見つけて読んでいる投稿です:

配列をマーシャリングするためにP/Invoke [In、Out]属性はオプションですか? [In、Out]を使用する必要があると思わせます。

これらの3つの投稿により、[In、Out]を使用する必要があると思いますが、代わりにrefを使用でき、同じマシンコードになります。それは私が間違っていると私に思わせます-したがって、ここで尋ねます。

20
PerryC

refまたはoutの使用法はnot任意です。ネイティブコードが参照渡し(ポインタ)を必要とする場合、パラメータタイプが値タイプの場合は必須これらのキーワードを使用します。そのため、ジッターは値へのポインターを生成することを認識します。また、パラメータタイプが参照型(クラス)の場合、mustはそれらを省略し、オブジェクトはすでに内部のポインタです。

[In]属性と[Out]属性は、ポインターに関するあいまいさを解決するために必要であり、データフローを指定しません。 [In]は常にピンボークマーシャラーによって暗示されるため、明示的に記述する必要はありません。ただし、ネイティブコードによって構造体またはクラスのメンバーに加えられた変更がコードに戻ってくることが予想される場合は、必須 [Out]を使用してください。 pinvokeマーシャラーは、費用を回避するために自動的にコピーバックすることを回避します。

さらに厄介なのは、[Out]はあまり必要ないということです。値がblittableの場合に発生します。これは、管理対象の値またはオブジェクトのレイアウトがネイティブのレイアウトと同じであることを意味する高価なWordです。その後、pinvokeマーシャラーはショートカットを使用して、オブジェクトを固定し、マネージドオブジェクトストレージへのポインターを渡すことができます。ネイティブコードが管理対象オブジェクトを直接変更しているため、必然的に変更が発生します。

あなたが一般的に強く追求したいことは、それは非常に効率的です。タイプに[StructLayout(LayoutKind.Sequential)]属性を指定すると、CLRがフィールドを再配置して最小のオブジェクトを取得するために使用する最適化が抑制されます。また、単純な値型または固定サイズのバッファーのフィールドのみを使用することにより、その選択肢がない場合もあります。 boolは使用せず、代わりにbyteを使用してください。型が正常に機能していないか、デバッガーを使用してポインター値を比較する以外に、型がブリット可能かどうかを確認する簡単な方法はありません。

明示的にし、必要なときは常に[Out]を使用してください。それが必要でないことが判明した場合、それは何もかかりません。そしてそれは自己文書化です。また、ネイティブコードのアーキテクチャが変更されても、引き続き機能することを実感できます。

22
Hans Passant