C#でクラス型のメンバー変数を持つ構造体を持つことは可能ですか? もしそうなら、情報はスタック、ヒープ、またはその両方のどこに保存されますか?
4 に答える
はい、できます。クラス メンバー変数へのポインターは、残りの構造体の値と共にスタックに格納され、クラス インスタンスのデータはヒープに格納されます。
構造体には、メンバー (内部クラス) としてクラス定義を含めることもできます。
少なくともコンパイルして実行して、それが可能であることを示す、本当に役に立たないコードを次に示します。
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyStr m = new MyStr();
m.Foo();
MyStr.MyStrInner mi = new MyStr.MyStrInner();
mi.Bar();
Console.ReadLine();
}
}
public class Myclass
{
public int a;
}
struct MyStr
{
Myclass mc;
public void Foo()
{
mc = new Myclass();
mc.a = 1;
}
public class MyStrInner
{
string x = "abc";
public string Bar()
{
return x;
}
}
}
}
クラスのコンテンツはヒープに格納されます。
クラスへの参照 (ポインターとほぼ同じ) は、構造体の内容と共に格納されます。構造体の内容が格納される場所は、それがローカル変数、メソッド パラメーター、またはクラスのメンバーのいずれであるか、およびそれがボックス化されているか、クロージャーによってキャプチャされているかによって異なります。
構造体のフィールドの1つがクラス型である場合、そのフィールドはクラスオブジェクトのIDを保持するか、null参照を保持します。問題のクラスオブジェクトが不変である場合(たとえばstring
)、そのIDを格納すると、その内容も効果的に格納されます。ただし、問題のクラスオブジェクトが変更可能である場合、IDを格納することは、参照がフィールドに格納された後にそれを変更する可能性のあるコードの手に渡らない場合にのみ、コンテンツを格納する効果的な手段になります。
一般に、次の2つの状況のいずれかが当てはまらない限り、構造内に可変クラス型を格納することは避けてください。
- 実際、関心があるのは、コンテンツではなくクラスオブジェクトのIDです。たとえば、「Control」タイプと「Rectangle」タイプのフィールドを保持し、後でコントロールを復元できるようにするために、コントロールが一時的に持っていた「Bounds」を表す「FormerControlBounds」構造を定義できます。以前の位置に。`Control`フィールドの目的は、コントロールの状態のコピーを保持することではなく、位置を復元する必要があるコントロールを識別することです。一般に、構造体は、参照を保持するオブジェクトの可変メンバーへのアクセスを回避する必要があります。ただし、そのようなアクセスが問題のオブジェクトの現在の可変状態を参照していることが明らかな場合を除きます(たとえば、`CaptureControlPosition`または` RestoreControlToCapturedPosition`メソッド、
- フィールドは`private`であり、オブジェクト自体を外部コードに公開せずにプロパティを調べる目的で読み取る唯一のメソッドであり、フィールドを書き込む唯一のメソッドは新しいオブジェクトを作成し、すべてのミューテーションを実行しますそれが発生し、そのオブジェクトへの参照を格納します。たとえば、配列のように動作するが値のセマンティクスを備えた `struct`を設計するには、構造体にプライベートフィールドに配列を保持させ、配列を書き込もうとするたびに、データを使用して新しい配列を作成します。古い配列から新しい配列を変更し、変更した配列をそのフィールドに格納します。配列自体は可変タイプですが、フィールドに格納されるすべての配列インスタンスは事実上不変であることに注意してください。
シナリオ#1はジェネリック型ではかなり一般的であることに注意してください。たとえば、「値」が可変オブジェクトのIDである辞書があることは非常に一般的です。そのディクショナリを列挙すると、KeyValuePair
そのValue
フィールドがその可変型を保持するインスタンスが返されます。
シナリオ#2はあまり一般的ではありません。残念ながら、プロパティセッター以外の構造体メソッドが構造体を変更することをコンパイラーに通知する方法はありません。したがって、読み取り専用コンテキストでの使用は禁止する必要があります。のように動作するList<T>
が、値のセマンティクスを持ち、Add
メソッドを含む構造体を持つことができますが、Add
読み取り専用の構造体インスタンスでは、コンパイラエラーではなく偽のコードが生成されます。さらに、そのような構造体のメソッドとプロパティセッターを変更すると、一般的にパフォーマンスがかなり低下します。このような構造体は、他の方法では変更可能なクラスの不変のラッパーとして存在する場合に役立ちます。そのような構造体がボックス化されていない場合、パフォーマンスはクラスよりも優れていることがよくあります。一度だけボックス化された場合(たとえば、インターフェイスタイプにキャストされた場合)、パフォーマンスは通常、クラスに匹敵します。繰り返し箱に入れると、パフォーマンスはクラスよりもはるかに悪くなる可能性があります。
これを行うことはおそらく推奨される方法ではありません。http: //msdn.microsoft.com/en-us/library/ms229017( VS.85 ).aspxを参照してください。
参照型はヒープに割り当てられ、メモリ管理はガベージコレクターによって処理されます。
値の型はスタックまたはインラインで割り当てられ、スコープ外になると割り当てが解除されます。
一般に、値型は割り当てと割り当て解除の方が安価です。ただし、大量のボクシングとアンボクシングを必要とするシナリオで使用する場合は、参照型と比較してパフォーマンスが低下します。