新しいオブジェクトを作成するか、すべてのプロパティをリセットしますか?


29
 public class MyClass
    {
        public object Prop1 { get; set; }

        public object Prop2 { get; set; }

        public object Prop3 { get; set; }
    }

のオブジェクトmyObjectMyClassあり、そのプロパティをリセットする必要があるとします。新しいオブジェクトを作成するか、各プロパティを再割り当てする方が良いでしょうか?古いインスタンスで追加の使用法がないと仮定します。

myObject = new MyClass();

または

myObject.Prop1 = null;
myObject.Prop2 = null;
myObject.Prop3 = null;


14
コンテキストなしでは本当に答えられません。
CodesInChaos

2
@CodesInChaos、確かに。新しいオブジェクトの作成が好ましくない可能性がある特定の例:オブジェクトプーリングとGCを処理するための割り当てを最小限に抑えることを試みます。
XNargaHuntress

XGundam05しかし、その後も、それはOPで提案この技術はそれに対処する適切な方法である可能性は非常に低いです@
ベン・アーロンソン

2
Suppose I have an object myObject of MyClass and I need to reset its properties-オブジェクトのプロパティをリセットする必要がある場合、または問題のあるドメインをモデル化するのに役立つ場合はreset()、MyClassのメソッドを作成してください。以下のHarrisonPaineの回答を参照してください
ブランディン

回答:


49

新しいオブジェクトをインスタンス化することは常に優れており、プロパティ(コンストラクター)を初期化する場所が1つあり、それを簡単に更新できます。

クラスに新しいプロパティを追加することを想像してください。すべてのプロパティを再初期化する新しいメソッドを追加するのではなく、コンストラクタを更新することをお勧めします。

現在、オブジェクトを再利用したい場合があります。プロパティを再初期化するのに非常にコストがかかり、それを保持したい場合です。ただし、これはより専門的であり、他のすべてのプロパティを再初期化する特別な方法があります。この状況でも、新しいオブジェクトを作成したい場合があります。


6
考えられる3番目のオプション:myObject = new MyClass(oldObject);。ただし、これは新しいインスタンスの元のオブジェクトの正確なコピーを意味します。
-AmazingDreams

new MyClass(oldObject)初期化が高価な場合に最適なソリューションだと思います。
rickcnagy

8
コピーコンストラクターは、よりC ++スタイルです。.NETは、正確なコピーである新しいインスタンスを返すClone()メソッドを好むようです。
エリック

2
コンストラクターは簡単にpublic Initialize()メソッドを呼び出すだけなので、初期化の単一ポイントがあり、任意のポイントを呼び出して「新しい」新鮮なオブジェクトを効果的に作成できます。オブジェクトプールで使用でき、パフォーマンスのために再利用できるオブジェクトのIInitializeインターフェイスのようなライブラリを使用しました。
チャドショーギンズ

16

あなたは間違いなく、新しいオブジェクトを作成することを好むべきで広大な例多数。すべてのプロパティの再割り当てに関する問題:

  • すべてのプロパティにパブリックセッターが必要です。これにより、提供できるカプセル化のレベルが大幅に制限されます。
  • 古いインスタンスをさらに使用するかどうかを知ることは、古いインスタンスが使用されていることをどこでも知る必要があることを意味します。クラスAとクラスのB両方がクラスのインスタンスに渡される場合C、同じインスタンスが渡されるかどうか、もしそうであれば、もう一方がまだそれを使用しているかどうかを知る必要があります。これは、そうでなければ理由がないクラスを緊密に結合します。
  • gbjbaanbが示したように、コードを繰り返します。コンストラクターにパラメーターを追加すると、コンストラクターを呼び出すすべての場所でコンパイルが失敗し、スポットを見落とす危険はありません。パブリックプロパティを追加するだけの場合は、オブジェクトが「リセット」されるすべての場所を手動で見つけて更新する必要があります。
  • 複雑さが増します。ループ内でクラスのインスタンスを作成して使用していると想像してください。メソッドを使用する場合は、ループを初めて使用するとき、またはループが開始する前に個別の初期化を行う必要があります。どちらの方法でも、これら2つの初期化方法をサポートするために記述する必要がある追加のコードです。
  • 無効な状態からクラスを保護できないことを意味します。あなたがFraction分子と分母でクラスを書き、それが常に減らされることを強制したいと想像してください(すなわち、分子と分母のgcdは1でした)。人々が分子と分母を公的に設定できるようにしたい場合、無効な状態を遷移して有効な状態から別の状態に移行する可能性があるため、これをうまく行うことは不可能です。例:1/2(有効)-> 2/2(無効)-> 2/3(有効)。
  • あなたが働いている言語にとってまったく慣用的なことではなく、コードを保守している人の認知摩擦を増大させます。

これらはすべて非常に重要な問題です。そして、あなたが作成する余分な仕事の見返りに得るものは...何もありません。一般に、オブジェクトのインスタンスの作成は非常に安価であるため、パフォーマンス上の利点はほとんど常に無視できます。

他の回答で述べたように、パフォーマンスが問題になるのは、クラスが建設にかなりの費用をかける場合だけです。ただし、その場合でも、この手法が機能するには、リセットするプロパティから高価な部分を分離できる必要があるため、代わりにフライウェイトパターンなどを使用できます。


補足として、上記の問題のいくつかは、セッターを使用せず、代わりにpublic Resetにコンストラクターと同じパラメーターメソッドをクラス。何らかの理由でこのリセットルートを使用したい場合は、おそらくそれがはるかに優れた方法です。

それでも、追加の複雑さと繰り返しは、それが対処していないという上記の点とともに、特に存在しない利点と比較検討する場合、それを行うことに対する非常に説得力のある議論です。


新しいオブジェクトを作成するのではなく、リセットするための非常に重要なユースケースは、実際にオブジェクトをリセットする場合です。IE。オブジェクトを指す他のクラスに、リセット後に新しいオブジェクトを表示させる場合。
-Taemyr

@Taemyr可能性がありますが、OPは質問でそれを明示的に除外しています
ベンアーロンソン

9

非常に一般的な例を考えると、わかりにくいです。ドメインの場合に「プロパティのリセット」がセマンティックな意味を持っている場合、クラスのコンシューマーが呼び出すことはより意味があります。

MyObject.Reset(); // Sets all necessary properties to null

より

MyObject = new MyClass();

クラスの消費者に電話をかける必要はない

MyObject.Prop1 = null;
MyObject.Prop2 = null; // and so on

クラスがリセット可能なものを表す場合、Reset()コンストラクターの呼び出しやそのプロパティの手動設定に依存するのではなく、メソッドを通じてその機能を公開する必要があります。


5

Harrison PaineとBrandinが示唆するように、私は同じオブジェクトを再利用し、Resetメソッドでプロパティの初期化を分解します。

public class MyClass
{
    public MyClass() { this.Reset() }

    public void Reset() {
        this.Prop1 = whatever
        this.Prop2 = you name it
        this.Prop3 = oh yeah
    }

    public object Prop1 { get; set; }

    public object Prop2 { get; set; }

    public object Prop3 { get; set; }
}

まさに私が答えたかったこと。これは両方の世界のベストです-ユースケースに応じて、唯一の実行可能な選択肢(インスタンスプール)
ファルコ

2

クラスの意図された使用パターンが、単一の所有者が各インスタンスへの参照を保持することである場合、他のコードは参照のコピーを保持せず、所有者が何度も必要とするループを持つことは非常に一般的です、空のインスタンスを埋め、一時的に使用し、それを再び必要としない(このような基準を満たす一般的なクラスはStringBuilder)ので、クラスのパフォーマンスの観点から、インスタンスを次のようにリセットするメソッドを含めると便利です。新規条件。代わりに数百のインスタンスを作成するだけでよい場合、このような最適化はあまり価値がありませんが、数百または数十億のオブジェクトインスタンスを作成するコストは追加できます。

関連する注意事項として、オブジェクト内のデータを返す必要があるメソッドに使用できるパターンがいくつかあります。

  1. メソッドは新しいオブジェクトを作成します。参照を返します。

  2. メソッドは、可変オブジェクトへの参照を受け入れ、それを埋めます。

  3. メソッドはrefパラメーターとして参照型変数を受け入れ、適切であれば既存のオブジェクトを使用するか、変数を変更して新しいオブジェクトを識別します。

多くの場合、最初のアプローチは意味的に最も簡単です。2番目は呼び出し側では少し厄介ですが、呼び出し側が1つのオブジェクトを頻繁に作成して何千回も使用できる場合、パフォーマンスが向上する可能性があります。3番目のアプローチは、意味的に少し厄介ですが、メソッドが配列でデータを返す必要があり、呼び出し元が必要な配列サイズを知らない場合に役立ちます。呼び出しコードが配列への唯一の参照を保持している場合、その参照をより大きな配列への参照で書き換えることは、配列を単純に大きくすることと意味的に同等です(意味的には望ましい動作です)。使用している間List<T>、多くの場合、手動でリサイズアレイを使用するよりも立派であってもよいし、構造体の配列は、より良い意味論および構造のリストよりも優れたパフォーマンスを提供します。


1

新しいオブジェクトの作成に賛成する人のほとんどは、重要なシナリオであるガベージコレクション(GC)を見逃していると思います。GCは、多くのオブジェクトを作成するアプリケーション(ゲームや科学アプリケーションなど)で実際のパフォーマンスに影響を与える可能性があります。

数式を表す式ツリーがあり、内部ノードは関数ノード(ADD、SUB、MUL、DIV)であり、リーフノードは末端ノード(X、e、PI)であるとします。ノードクラスにEvaluate()メソッドがある場合、子ノードを再帰的に呼び出し、データノードを介してデータを収集する可能性があります。少なくとも、すべてのリーフノードはDataオブジェクトを作成する必要があり、最終的な値が評価されるまで、ツリーの上位でそれらを再利用できます。

今、これらのツリーが何千もあり、それらをループで評価しているとしましょう。これらのすべてのデータオブジェクトはGCをトリガーし、パフォーマンスヒットを引き起こします。これは大きなものです(アプリの一部の実行で最大40%のCPU使用率の損失-データを取得するためにプロファイラーを実行しました)。

可能な解決策は?それらのデータオブジェクトを再利用し、それらの使用が完了したら、それらの.Reset()を呼び出すだけです。リーフノードは「new Data()」を呼び出さなくなり、オブジェクトのライフサイクルを処理するFactoryメソッドを呼び出します。

私はこれを知っています。なぜなら、この問題にぶつかったアプリケーションがあり、これで解決したからです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.