構造体とクラスを使用する場合の経験則は何ですか?私はそれらの用語のC#定義を考えていますが、あなたの言語が同様の概念を持っているなら、あなたの意見も聞きたいです。
私はほとんどすべてにクラスを使用する傾向があり、構造が非常に単純化されており、PhoneNumberなどのような値型でなければならない場合にのみ構造体を使用します。しかし、これは比較的マイナーな使用のように思われ、より興味深いユースケースがあることを願っています。
構造体とクラスを使用する場合の経験則は何ですか?私はそれらの用語のC#定義を考えていますが、あなたの言語が同様の概念を持っているなら、あなたの意見も聞きたいです。
私はほとんどすべてにクラスを使用する傾向があり、構造が非常に単純化されており、PhoneNumberなどのような値型でなければならない場合にのみ構造体を使用します。しかし、これは比較的マイナーな使用のように思われ、より興味深いユースケースがあることを願っています。
回答:
従うべき一般的な規則は、構造体は関連するプロパティの小さくシンプルな(1レベルの)コレクションでなければならず、作成後は不変であるということです。それ以外の場合は、クラスを使用します。
C#には、構造体とクラスに定義キーワード以外の宣言の明確な違いがないという点で優れています。そのため、構造体をクラスに「アップグレード」する必要がある、または逆にクラスを構造体に「ダウングレード」する必要があると感じた場合、ほとんどの場合、キーワードを変更するだけです(他にもいくつかの落とし穴があります;構造体は導出できません他のクラスまたは構造体型から、デフォルトのパラメータなしのコンストラクタを明示的に定義することはできません)。
構造体について知っておくべきより重要なことは、それらが値型であるため、クラス(参照型)のように扱うと痛みが半分になるということです。特に、構造のプロパティを変更可能にすると、予期しない動作が発生する可能性があります。
たとえば、AとBという2つのプロパティを持つクラスSimpleClassがあるとします。このクラスのコピーをインスタンス化し、AとBを初期化してから、インスタンスを別のメソッドに渡します。このメソッドはさらにAとBを変更します。呼び出し元の関数(インスタンスを作成した関数)に戻ると、インスタンスのAとBには、呼び出されたメソッドによって値が与えられます。
今、あなたはそれを構造体にします。プロパティは変更可能です。前と同じ構文で同じ操作を実行しますが、メソッドを呼び出した後のAとBの新しい値はインスタンスにありません。どうした?さて、あなたのクラスは構造体になりました。つまり、値型です。メソッドに値タイプを渡す場合、デフォルトでは(outまたはrefキーワードなしで)「値渡し」が渡されます。インスタンスの浅いコピーがメソッドで使用するために作成され、メソッドが終了して最初のインスタンスをそのまま残したときに破棄されます。
参照型を構造体のメンバーとして使用する場合、これはさらに混乱を招きます(許可されていませんが、実質的にすべての場合で非常に悪い習慣です)。クラスは複製されないため(構造体への参照のみ)、構造体への変更は元のオブジェクトには影響しませんが、構造体のサブクラスへの変更は呼び出し元コードからのインスタンスに影響します。これにより、変更可能な構造体が非常に一貫性のない状態になり、実際の問題のある場所から遠く離れた場所でエラーが発生する可能性が非常に高くなります。
このため、C#のほぼすべての機関は、常に構造を不変にすることを推奨しています。消費者がオブジェクトの構築時にのみプロパティの値を指定できるようにし、そのインスタンスの値を変更する手段を提供しないでください。読み取り専用フィールド、または取得専用プロパティがルールです。消費者が値を変更する場合、必要な変更を加えて、古いオブジェクトの値に基づいて新しいオブジェクトを作成するか、同じことを行うメソッドを呼び出すことができます。これにより、構造体の単一のインスタンスを、他のすべてと区別できない(ただし、場合によっては平等な)1つの概念的な「値」として扱うように強制されます。タイプによって保存された「値」に対して操作を実行すると、初期値とは異なる新しい「値」を取得します。
良い例として、DateTimeタイプを見てください。DateTimeインスタンスのフィールドを直接割り当てることはできません。新しいインスタンスを作成するか、新しいインスタンスを生成する既存のメソッドを呼び出す必要があります。これは、日付と時刻が数字の5のように「値」であり、数字の5を変更すると5ではない新しい値になるためです。5+ 1 = 6だからといって、5が6になるわけではありません1を追加したからです。DateTimesも同じように機能します。12:00は12:01に「なる」ことはありませんが、分を追加すると、代わりに12:00とは異なる新しい値12:01を取得します。これがあなたのタイプの論理的な状態である場合(.NETに組み込まれていない良い概念的な例は、お金、距離、重量、および操作が値のすべての部分を考慮する必要があるUOMのその他の量です)、次に、構造体を使用し、それに応じて設計します。オブジェクトのサブアイテムが独立して変更可能である必要がある他のほとんどの場合、クラスを使用します。
答え:「純粋なデータ構造に構造体を使用し、オペレーションを持つオブジェクトにクラスを使用する」は間違いなく間違ったIMOです。構造体が多数のプロパティを保持している場合、クラスはほぼ常に適切です。Microsoftは、効率の観点から、タイプが16バイトを超える場合はクラスである必要があるとよく言います。
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
a class
とa の最も重要な違いstruct
は、次の状況で何が起こるかです。
Thing thing1 = new Thing(); thing1.somePropertyOrField = 5; Thing thing2 = thing1; thing2.somePropertyOrField = 9;
上の最後のステートメントの効果は何thing1.somePropertyOrField
ですか?もしThing
構造体、およびsomePropertyOrField
暴露公共の場、オブジェクトがあるthing1
とthing2
お互いから「デタッチ」となりますので、後者のステートメントは影響しませんthing1
。場合はThing
クラスであり、その後、thing1
そしてthing2
お互いに添付されます、そしてので後者のステートメントが記述しますthing1.somePropertyOrField
。前者のセマンティクスがより理にかなっている場合は構造体を使用し、後者のセマンティクスがより理にかなっている場合はクラスを使用する必要があります。
何かを可変にしたいという願望は、クラスであることを支持する議論であると忠告する人もいますが、私はその逆が真実であることを提案します:データを保持する目的で存在するものが可変になる場合インスタンスが何かにアタッチされているかどうかが明らかでない場合、インスタンスは他の何かにアタッチされていないことを明確にするために、構造体(公開フィールドの可能性が高い)である必要があります。
たとえば、次のステートメントを考えます。
Person somePerson = myPeople.GetPerson( "123-45-6789"); somePerson.Name = "Mary Johnson"; //「Mary Smith」だった
2番目のステートメントは、に格納されている情報を変更しmyPeople
ますか?if Person
が公開フィールド構造体である場合、そうではなく、それが公開フィールド構造体であることの明らかな結果になります。Person
構造体であり、更新したい場合は、myPeople
次のようなことを明確に行う必要がありますmyPeople.UpdatePerson("123-45-6789", somePerson)
。Person
ただし、クラスがの場合、上記のコードがのコンテンツを更新しないかMyPeople
、常に更新するか、更新するかどうかを判断するのははるかに困難です。
構造体は「不変」であるという概念に関しては、一般的には同意しません。「不変」構造体(コンストラクターに不変条件が適用される)には有効な使用例がありますが、構造体の一部が変更されるたびに構造体全体を書き換える必要があるため、単純に公開するよりもバグが発生しやすくなりますフィールドを直接。たとえばPhoneNumber
、フィールド、他のものを含む、AreaCode
およびの構造体を考えExchange
てくださいList<PhoneNumber>
。以下の効果はかなり明確になるはずです。
for(int i = 0; i <myList.Count; i ++) { PhoneNumber theNumber = myList [i]; if(theNumber.AreaCode == "312") { string newExchange = ""; if(new312to708Exchanges.TryGetValue(theNumber.Exchange)、out newExchange) { theNumber.AreaCode = "708"; theNumber.Exchange = newExchange; myList [i] = theNumber; } } }
上記のコードではPhoneNumber
、AreaCode
and 以外のフィールドを認識したり、気にしたりしないことに注意してくださいExchange
。場合はPhoneNumber
、いわゆる「不変」構造体だった、それが必要であろういずれかでそれが実現することをwithXX
示されたフィールドに渡された値を開催し、新たな構造体のインスタンスを返しますか、あるいはそれが必要となる各フィールドのための方法を、上記のようなコードでは、構造体のすべてのフィールドについて知ることができます。必ずしも魅力的ではありません。
ところで、構造体が可変型への参照を合理的に保持できる場合が少なくとも2つあります。
前者のシナリオでは、オブジェクトのIDENTITYは不変です。第二に、ネストされたオブジェクトのクラスは不変性を強制しないかもしれませんが、参照を保持する構造体はそうします。
1つのメソッドが必要な場合にのみ構造体を使用する傾向があるため、クラスを「重く」見えるようにします。したがって、構造体を軽量オブジェクトとして扱うことがあります。注意:これは常に機能するとは限らず、常に最善のこととは限らないため、状況によって異なります。
追加リソース
より正式な説明については、MSDNは次の ように述べています。http : //msdn.microsoft.com/en-us/library/ms229017.aspxこのリンクは、上記で説明した内容を検証します。構造体は、少量のデータを格納するためにのみ使用してください。
純粋なデータ構造体には構造体を使用し、操作を持つオブジェクトにはクラスを使用します。
データ構造にアクセス制御が不要で、get / set以外の特別な操作がない場合は、構造体を使用します。これにより、その構造がすべてデータのコンテナであることが明らかになります。
構造に、より複雑な方法で構造を変更する操作がある場合は、クラスを使用します。クラスは、メソッドへの引数を介して他のクラスと対話することもできます。
レンガと飛行機の違いと考えてください。レンガは構造体です。長さ、幅、高さがあります。それはあまりできません。飛行機には、操縦翼面の移動やエンジン推力の変更など、何らかの形で航空機を変化させる複数の操作があります。クラスとしては良いでしょう。