web-dev-qa-db-ja.com

C#/。NETでポインターを使用する場合

C#を使用すると、プログラマーは安全でないコンテキストでポインターにアクセスしたり使用したりすることができます。しかし、これはいつ必要ですか?

どのような状況で、ポインターの使用が不可避になりますか?

パフォーマンス上の理由だけですか?

また、C#が安全でないコンテキストを通じてこの機能を公開し、そこから管理された利点をすべて削除するのはなぜですか?理論的には、管理環境の利点を失うことなくポインターを使用することは可能ですか?

66
Joan Venge

これはいつ必要ですか?どのような状況でポインターを使用することは避けられませんか?

管理された安全なソリューションの正味コストは許容できないが、安全でないソリューションの正味コストは許容できる場合。総費用から総利益を差し引くことにより、純費用または純利益を決定できます。安全でないソリューションの利点は、「正確性を確保するために不要なランタイムチェックに時間を浪費しない」などです。コストは、(1)マネージドセーフティシステムをオフにしても安全なコードを記述する必要があり、(2)ガベージコレクタがアンマネージポインタを持つメモリ内を移動できないため、ガベージコレクターの潜在的な効率を低下させることに対処する必要があるそれ。

または、あなたがマーシャリング層を書いている人なら。

パフォーマンス上の理由のみですか?

パフォーマンス以外の理由で、マネージ言語でポインターを使用するのは不自然なようです。

Marshalクラスのメソッドを使用して、ほとんどの場合、アンマネージコードとの相互運用を処理できます。 (マーシャリングギアを使用して相互運用の問題を解決することが困難または不可能な場合がいくつかありますが、私は知りません。)

もちろん、私が言ったように、あなたがマーシャルクラスを書いている人なら、明らかにあなたは問題を解決するためのマーシャリング層seに到達しません。その場合は、ポインターを使用して実装する必要があります。

C#が安全でないコンテキストを通じてこの機能を公開し、管理された利点をすべて削除するのはなぜですか?

これらの管理された利点には、パフォーマンスコストが伴います。たとえば、配列に10番目の要素を要求するたびに、ランタイムは10番目の要素があるかどうかを確認し、ない場合は例外をスローする必要があります。ポインターを使用すると、ランタイムコストがなくなります。

対応する開発者のコ​​ストは、間違えた場合、エラーの時点でNice clean例外を処理するのではなく、ハードディスクをフォーマットし、1時間後にプロセスをクラッシュさせるメモリ破損バグに対処することです。

理論的には、管理環境の利点を失うことなくポインターを使用できますか?

「利点」とは、ガベージコレクション、型の安全性、参照整合性などの利点を意味すると思います。したがって、あなたの質問は本質的に「理論的には安全システムをオフにすることは可能ですが、それでも安全システムをオンにすることの利点を得ることができますか?」です。いいえ、明らかにそうではありません。あなたはそれがどれくらい高価であるのが好きではないので、あなたがその安全システムをオフにすると、それがオンになっているという利点を得ることができません!

84
Eric Lippert

ポインターは、管理されたガベージコレクションされた環境に対する固有の矛盾です。
生のポインタをいじり始めると、GCは何が起こっているのかわかりません。

具体的には、ポインタがどこにあるのかわからないため、オブジェクトが到達可能かどうかを判断できません。
また、ポインタを壊してしまうため、メモリ内でオブジェクトを移動することもできません。

これらはすべて、GCで追跡されるポインターによって解決されます。それが参照です。

乱雑な高度な相互運用シナリオで、または非常に高度な最適化のためにのみポインタを使用する必要があります。
質問する必要がある場合、おそらくそうすべきではありません。

17
SLaks

GCは参照を移動できます。安全でないものを使用すると、オブジェクトはGCの制御外に保持され、これが回避されます。 「固定」はオブジェクトを固定しますが、GCがメモリを管理できるようにします。

定義により、オブジェクトのアドレスへのポインターがあり、GCがそれを移動すると、ポインターは無効になります。

ポインターが必要な理由について:主な理由は、アンマネージDLLを使用することです。 C++で書かれたもの

また、変数を固定してポインターを使用すると、ヒープの断片化の影響を受けやすくなります。


編集

マネージドコードとアンマネージドコードの中心的な問題について触れましたが、メモリはどのように解放されますか?

記述したとおり、パフォーマンスのためにコードを混在させることができます。マネージド/アンマネージドの境界をポインターと交差させることはできません(つまり、「安全でない」コンテキストの外側でポインターを使用することはできません)。

それらがどのようにきれいになるかについて...あなたはあなた自身の記憶を管理しなければなりません;ポインタが指すオブジェクトは、(できれば)CoTaskMemAlloc()を使用して(通常はC++ DLL内で)作成/割り当てられ、CoTaskMemFree()を呼び出して同じ方法でそのメモリを解放する必要があります。または、メモリリークが発生します。 CoTaskMemAlloc()で割り当てられたメモリのみがCoTaskMemFree()で解放できることに注意してください。

もう1つの方法は、ネイティブC++ dllからポインターを取得して解放するメソッドを公開することです。これにより、DLLがメモリを解放する方法を決定できます。使用するネイティブdllのほとんどは、変更できないサードパーティのdllであり、通常、このような関数を呼び出すことはできません(これまで見てきました)。

here から取得したメモリの解放の例:

string[] array = new string[2];
array[0] = "hello";
array[1] = "world";
IntPtr ptr = test(array);
string result = Marshal.PtrToStringAuto(ptr);
Marshal.FreeCoTaskMem(ptr);
System.Console.WriteLine(result);


その他の資料:

C#IntPtrによって参照されるメモリの割り当て解除 2番目の回答では、さまざまな割り当て/割り当て解除方法について説明しています

C#でIntPtrを解放する方法 メモリが割り当てられたのと同じ方法で割り当てを解除する必要性を強化します

http://msdn.Microsoft.com/en-us/library/aa366533%28VS.85%29.aspx メモリの割り当ておよび割り当て解除のさまざまな方法に関するMSDN公式ドキュメント。

要するに...メモリを解放するには、メモリがどのように割り当てられているかを知る必要があります。


Editあなたの質問を正しく理解していれば、短い答えはイエスです。管理されていないポインタにデータを渡して、それを操作できます安全でないコンテキストで、安全でないコンテキストを終了するとデータが利用可能になります。

重要なのは、fixedブロックで参照している管理対象オブジェクトを固定する必要があることです。これにより、unsafeブロックにいる間、参照しているメモリがGCによって移動されるのを防ぎます。ここにはいくつかの微妙な点が関係しています。固定ブロックで初期化されたポインターを再割り当てすることはできません...独自のコードの管理を本当に設定している場合は、安全でないステートメントおよび修正されたステートメントを参照する必要があります。

とはいえ、自分のオブジェクトを管理し、説明した方法でポインターを使用することの利点は、あなたが思うほどのパフォーマンス向上をもたらさないかもしれません。しない理由:

  1. C#は非常に最適化されており、非常に高速です
  2. ポインタコードはILとして生成されますが、ILを調整する必要があります(この時点で、さらなる最適化が作用します)
  3. ガベージコレクターをオフにするのではなく、GCの範囲外で作業しているオブジェクトを保持しているだけです。したがって、100msごとに、GC まだ コードを中断し、マネージコード内の他のすべての変数に対してその機能を実行します。

HTH、
ジェームス

5
James King

C#でポインターを明示的に使用する最も一般的な理由:

  • パフォーマンスに非常に敏感な低レベルの作業(文字列操作など)を行う、
  • アンマネージAPIとのインターフェース。

ポインターに関連付けられた構文がC#から削除された理由(私の知識と視点によると、Jon Skeetはより良いB-に答えるでしょう)は、ほとんどの状況で余分であることが判明しました。

言語設計の観点から見ると、ガベージコレクターでメモリを管理したら、ポインターでできることとできないことについて厳しい制約を導入する必要があります。たとえば、ポインタを使用してオブジェクトの中央を指すと、GCに重大な問題が発生する可能性があります。したがって、制限が設定されたら、余分な構文を省略して、「自動」参照で終わることができます。

また、C/C++に見られる超有益なアプローチは、エラーの一般的な原因です。マイクロパフォーマンスがまったく問題にならないほとんどの状況では、より厳密なルールを提供し、発見するのが非常に難しいバグを少なくするように開発者を制約する方が適切です。したがって、一般的なビジネスアプリケーションの場合、.NETやJavaなどのいわゆる「管理された」環境は、ベアメタルマシンに対して動作することを想定した言語よりも適しています。

3
Ondrej Tucny

IPC(共有メモリ)を使用して2つのアプリケーション間で通信する場合、データをメモリにマーシャリングし、Windowsメッセージングなどを介してこのデータポインタを他のアプリケーションに渡すことができます。データを取り戻すことができます。

データをメモリにマーシャリングし、win msgingを使用してVB6アプリにポインターを渡し、VB6 copymemory()を使用してマネージドメモリ空間からVB6アプリのアンマネージドメモリにデータを転送する.NETからレガシーVB6アプリにデータを転送する場合にも役立ちますスペース..

2
variable