SO C#の構造体とクラスの違い、およびどちらをいつ使用するかについての質問はいくつもあります。(1文の答え:値が必要な場合は構造体を使用してください)セマンティクス。)どちらを選択するかについては、ガイドラインがたくさんあります。そのほとんどは、次のように要約されます。これらの特定の要件を満たさない限り、クラスを使用してから、構造体を使用します。
これはすべて私には理にかなっています。
しかし、システムで構造体を使用している人々の実際の例を見つけることができないようです。私はC#に(半)慣れていないので、構造体が本当に正しい選択である具体的な状況を想像するのに苦労しています(少なくとも、まだ1つに遭遇していません)。
それで、私はSO world-brainに目を向けます。クラスが機能しなかったであろうシステムで、実際に構造体を使用したいくつかのケースは何ですか?
それでもクラスは機能しますが、私が考えることができる例はポイントのようなものです。 xとyの値であると仮定すると、構造体を使用できます。
struct Point {
int x;
int y;
}
私の考えでは、実際のエンティティが実際にはあまり(またはまったく)動作しない場合にインスタンス化を使用してクラスを使用することを定義するよりも、整数のペアをより単純に表現したいと思います。
ジオロケーションを表すために構造体を使用しました
struct LatLng
{
public decimal Lattitude
{
get;
set;
}
public decimal Longitude
{
get;
set;
}
}
これは単一のエンティティを表します。たとえば、2つのLatLngを一緒に追加したり、この単一のエンティティに対して他の操作を実行したりできます。
構造体タイプは、Point、Rectangle、Colorなどの軽量オブジェクトを表すのに適しています。ポイントをクラスとして表すことは可能ですが、シナリオによっては構造体の方が効率的です。たとえば、1000ポイントオブジェクトの配列を宣言する場合、各オブジェクトを参照するために追加のメモリを割り当てます。この場合、構造体はより安価です。
また、プリミティブ型Int32、decimal、double.. etcを見ると、それらがすべてstructs。これにより、特定の重要なインターフェイスを実装しながら、値型にすることができます。
構造体は通常、グラフィックス/レンダリングシステムでも使用されます。ポイント/ベクトル構造体を作成することには多くの利点があります。
Rico Marianiが優れた 価値ベースのプログラミングに関するクイズ を投稿しました。彼は特定の状況で構造体を好む多くの理由について議論し、それを彼の クイズ結果の投稿 で詳細に説明しました。
Money構造体はおそらく最も一般的なものの1つですが、電話番号や住所も一般的です。
public struct Money
{
public string Currency { get; set; }
public double Amount { get; set; }
}
public struct PhoneNumber
{
public int Extension { get; set; }
public int RegionCode { get; set; }
//... etc.
}
public struct FullName
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
ただし、.NETでは、構造体のメモリフットプリントが16バイトより大きくならないようにする必要があります。これは、構造体が大きくなると、CLRが追加のメモリを割り当てる必要があるためです。
また、構造体はスタック上で「ライブ」であるため(参照型のようにヒープではない)、同じ型のオブジェクトを多数インスタンス化する必要がある場合は、構造体の使用を検討できます。
典型的な例はフレームワークです nullable types
、たとえばint?
。これらは構造体を使用するため、intの値セマンティクスを保持しますが、ボックス化して参照型に変換せずにnullにする方法を提供します。
参照によって何かを渡したくない場合は、構造体を使用します。データのコレクション、または値で渡したいオブジェクトがあるとします(つまり、渡すものはすべて、元のバージョンへの参照ではなく、独自の一意のコピーで機能します)。構造体は次のタイプに適しています。使用する。
これらはObject.GetHashCode()のデフォルトの実装を提供するため、オブジェクトがディクショナリのキーとして使用する非参照型の単純なコレクションである場合は、クラスの代わりに構造体を使用することをお勧めします。
また、データ構造のバイナリレイアウトを正確に制御する必要がある、PInvoke/interopまたは低レベルのネットワークシナリオにも役立ちます。 (構造体を必要とする多くの相互運用コードについては、www.pinvoke.netにアクセスしてください)
しかし、実際には、私はそれらを自分で使用することはありません。それらを使用しないで汗をかかないでください。
基本的には使わないようにしています。彼らはチームの他の開発者を混乱させているので、努力する価値はありません。私はそれを使用するケースを1つだけ見つけました。それは、XMLから生成するためにコードジェネレーターを使用するカスタムの列挙型のようなタイプです。
誰も言及していないとは信じられません [〜#〜] xna [〜#〜] :XNAでは、ほぼすべてはstruct
です。だからあなたがするとき
Matrix rotation = Matrix.CreateRotationZ(Math.PiOver2);
あなたは本当に値型を作成しています。
これは、アプリケーションプログラミングとは異なり、ガベージコレクターの実行中に数ミリ秒のストールが許容されないためです(フレーム全体をレンダリングするのに16.6ミリ秒しかかかりません!)したがって、GCをあまり実行する必要がないように、割り当てをできるだけ避ける必要があります。
これは特にXBox 360に当てはまり、GCはPCの品質にほど遠いです。フレームごとに平均1回の割り当てでも可能です。パフォーマンスを殺す!
私は通常、私のビジネスアプリの「データ密度」には関心がありません。特に必要な場合を除いて、通常は常にクラスを使用します値のセマンティクス
これは、これらの2つを比較し、同じ値の場合は同じように表示したいという状況を予測していることを意味します。クラスでは、これは実際にはmore動作します。これは、==、!=、Equals、およびGetHashcodeをオーバーライドする必要があるためです。これは、resharperが実行しても、余分な不要なコードです。
したがって、私の考えでは、これらを値(この場合はコンポーネント値)で比較する必要があることがわかっている場合を除いて、常にクラスを使用してください。
だから私はあなたがDateTime(構造体)を使ったことがないと思います。
私にとって重要なのは、同じオブジェクトへの参照を維持するかどうかを定義することです。
これは、構造体が別のエンティティの一部である場合に意味を持ちますが、エンティティ自体は意味を持ちます。
たとえば、LatLongを使用した上記の例では、完全な意味を持ちます。同じオブジェクトを参照し続けるのではなく、あるオブジェクトから別のオブジェクトに値をコピーする必要があります。
私は、構造体を使用して大規模なキャッシングとレイテンシーの要件が達成された金融機関で働いてきました。基本的に、構造体は多くの作業のガベージコレクターを節約できます。
次の例を参照してください。
http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/http://00sharp.wordpress.com/2013/07/04/a-case-for-the-structpart-2 /
私はよく構造体を使用して、列挙型として表される可能性のあるドメインモデルの値型を表しますが、任意の無制限の数の離散値が必要です。または、列挙型に追加できない追加の動作(メソッド)を持たせたいです...たとえば、最近のプロジェクトでは、多くのデータ要素が日付ではなく特定の暦月に関連付けられていました。そこで、メソッドを持つCalendarMonth構造体を作成しました。
およびTryParse()メソッド、
およびプロパティ
等.
私が過去に作成したものはStorageCapacityです。それは0バイトからNエクサバイトを表しています(ヨタバイトまで高くなる可能性がありますが、当時はエクサで十分であるように見えました)。私はストレージ管理会社で働いていたので、この構造は理にかなっています。 StorageUnit(列挙型)とQuantity(10進数を使用)を持つ構造体:それはかなり単純だと思うでしょう。ただし、変換、演算子、クラスを追加して、書式設定、解析などをサポートすると、合計されます。
抽象化は、StorageCapacityを取得して、1024で何度も乗算または除算することなく、バイト、キロバイトなどとして表すことができるようにするために役立ちました。
構造体を使用したのは、分数構造体を作成していたときだけでした。
public struct Fraction
{
public int Numerator {get;set;}
public int Denominator {get; set;}
//it then had a bunch of Fraction methods like Reduce, Add, Subtract etc...
}
組み込みの値型と同じように値を表すと感じたので、値型のように振る舞うと、それに対するコーディングがより自然に感じられます。
すでに他の場所で構造体を使用する理由を説明しました( C#で構造体を使用する場合 )。実際のプロジェクトでは、これらの理由で構造体を使用しました。
画像処理で発生する可能性のある、同じアイテムタイプを配列に多数格納する必要がある場合は、パフォーマンス上の理由から構造体を使用することを選択します。
C#とC++の間で構造化データを渡すには構造体を使用する必要があります。
それらを使用する非常に正当な理由がない限り、私はそれらを避けようとします。
値のセマンティクスを実装するためにそれらを使用することを好む人がいることは知っていますが、この動作はクラスの「通常の」割り当て動作(C#)とは非常に異なるため、覚えていないためにバグを追跡するのが困難になります。クラスではなく構造体として実装されたため、割り当て元または割り当て先のオブジェクトがこの動作をしたこと。 (私には何度も起こったので、C#構造体の無害な使用によって実際に火傷を負ったので、この警告を出します。)
パフォーマンスが重要な状況では、構造体(値型でスタックから割り当てられる)がクラス(参照型であるためヒープから割り当てられる)よりも優れている場合があります。 JoeDuffyのブログ投稿 " シングルワードリーダー/ライタースピンロック "は、これの実際のアプリケーションを示しています。
C#の構造体は、その中心にあるのは、ダクトテープで貼り付けられた一連の変数にすぎません。特定のタイプの各変数が、ダクトテープで貼り付けられた、独立しているが関連する変数(ポイントの座標など)の束を表すようにしたい場合は、クラスよりも露出フィールド構造体を使用する方がよい場合がよくあります。 「束」は2つまたは20を意味します。 Microsoftのstruct-versus-classアドバイスは、単一の値をカプセル化するデータ型には問題ありませんが、独立しているが関連する値をカプセル化することを目的とする型には適用できないと見なす必要があることに注意してください。 変数が独立している程度が大きいほど、exposed-field構造体を使用する利点が大きくなります。
クラスを使用して多数の独立変数をカプセル化する場合、2つの方法がありますが、どちらも非常に便利ではありません。不変のクラスを使用できます。その場合、そのクラスタイプのnull以外の保存場所は、それによって識別されたインスタンスによって保持されている値をカプセル化し、ある保存場所を別の保存場所にコピーして、新しい場所に同じ値をカプセル化できます。残念ながら、保存場所によってカプセル化された値の1つを変更するには、通常、値が変更されていることを除いて、古いインスタンスと同じように新しいインスタンスを作成する必要があります。たとえば、タイプ_Immutable3dPoint
_の変数pt
があり、_pt.X
_を1つ増やしたい場合は、次のようにする必要があります。pt = new Immutable3dPoint(pt.X+1, pt.Y, pt.Z);
おそらく型が3つの値のみをカプセル化する場合は許容できますが、非常に多い場合はかなり面倒です。
他のクラスベースのアプローチは、可変クラスを使用することです。これには通常、クラスタイプのすべての格納場所が、そのクラスのインスタンスへのユニバース内の任意の場所で唯一の参照を保持していることを確認する必要があります。保存場所を作成するときは、新しいインスタンスを作成し、そこに参照を保存する必要があります。値を保存場所P
から保存場所Q
にコピーしたい場合は、すべてのフィールドまたはプロパティを1つのインスタンスから別のインスタンスにコピーする必要があります(おそらく、型を実装することによって) CopyFrom
メソッド、およびQ.CopyFrom(P);
と言う。代わりに_Q=P;
_と言うと、うまくいくように見えるかもしれませんが、将来P
を変更しようとすると変更されることに注意してください。 Q
およびその逆。可変クラスが機能する場合があり、効率的な場合もありますが、混乱するのは非常に簡単です。
公開フィールド構造は、不変クラスの便利な値コピーセマンティクスと、可変クラスによって許可される便利な区分的変更を組み合わせたものです。大きな構造は、不変オブジェクトへの参照よりもコピーに時間がかかりますが、公開フィールド構造の一部を変更するコストは、構造全体のサイズではなく、変更の範囲にのみ依存します。対照的に、不変のクラスタイプにカプセル化された1つのデータを変更するコストは、クラスの合計サイズに比例します。
基本的に、私は幾何学的および数学的なデータをモデル化するために、または値ベースのデータ構造が必要な場合にStructsを使用します。
.NetFrameworkは実生活だと思います。 「構造」の下のリストを参照してください。
これがどれだけ使用されているかはわかりませんが、今日、構造体にインスタンスフィールドの初期化子を含めることはできませんが、クラスには含めることができることを発見しました。
したがって、次のコードではコンパイルエラーが発生しますが、「struct」を「class」に変更するとコンパイルされます。
public struct ServiceType
{
public bool backEnd { get; set; }
public bool frontEnd { get; set; }
public string[] backEndServices = { "Service1", "Service2" };
public string[] frontEndServices = { "Service3", "Service4" };
}