15

MyStructプライベートメンバーprivate bool[] boolArray;とメソッドを持つ構造体がありChangeBoolValue(int index, bool Value)ます。

MyClass私は、フィールドを持つクラスを持っていますpublic MyStruct bools { get; private set; }

既存のオブジェクトから新しいMyStructオブジェクトを作成し、メソッドChangeBoolValue()を適用すると、参照されたものではなく参照が新しいオブジェクトにコピーされたため、両方のオブジェクトのブール配列が変更されます。例えば:

MyStruct A = new MyStruct();
MyStruct B = A;  //Copy of A made
B.ChangeBoolValue(0,true);
//Now A.BoolArr[0] == B.BoolArr[0] == true

より深いコピーを実装するようにコピーを強制する方法はありますか、それとも同じ問題が発生しないこれを実装する方法はありますか?

MyStructは値型であり、参照が伝播することを望まなかったため、特に構造体にしました。

4

6 に答える 6

13

ランタイムは構造体の高速メモリ コピーを実行します。私が知る限り、独自のコピー手順を導入または強制することはできません。独自のメソッドやコピー コンストラクターを導入することはできCloneますが、それらを使用するよう強制することはできません。

可能であれば、この問題を回避するために、構造体を不変 (または不変のクラス) にするか、一般的に再設計することをお勧めします。あなたが API の唯一の消費者である場合は、特に警戒を怠らないでください。

Jon Skeet (および他の人) はこの問題について説明しており、例外もあるかもしれませんが、一般的に言えば、変更可能な構造体は悪です。 構造体に参照型のフィールドを含めることができます

于 2012-07-05T02:09:21.933 に答える
3

(リフレクションを使用するため)最速ではありませんが、(ディープ)コピーを作成する簡単な方法の 1 つは、 を使用BinaryFormatterして元のオブジェクトを にMemoryStreamシリアル化し、そこからMemoryStream新しい に逆シリアル化することMyStructです。

    static public T DeepCopy<T>(T obj)
    {
        BinaryFormatter s = new BinaryFormatter();
        using (MemoryStream ms = new MemoryStream())
        {
            s.Serialize(ms, obj);
            ms.Position = 0;
            T t = (T)s.Deserialize(ms);

            return t;
        }
    }

クラスと構造体で機能します。

于 2012-07-05T01:39:51.610 に答える
2

回避策として、以下を実装する予定です。

の内容を変更できる構造体には 2 つのメソッドがありますBoolArray。次のように、構造体がコピーされたときに配列を作成するのではなく、BoolArray を変更するための呼び出しが行われたときに、BoolArray が新たに作成されます。

public void ChangeBoolValue(int index, int value)
{
    bool[] Copy = new bool[4];
    BoolArray.CopyTo(Copy, 0);
    BoolArray = Copy;

    BoolArray[index] = value;
}

これは、BoolArray の大幅な変更を伴う使用には適していませんが、構造体の私の使用法は、コピーが多く、ほとんど変更されていません。これは、変更が必要な場合にのみ配列への参照を変更します。

于 2012-07-05T01:58:02.800 に答える
1

奇妙なセマンティクスを避けるために、変更可能な参照型のフィールドを保持するすべての構造体は、次の 2 つのいずれかを行う必要があります。

  1. その観点から、フィールドの内容はオブジェクトを「保持」するのではなく、単にオブジェクトを識別するのに役立つことを非常に明確にする必要があります。たとえば、`KeyValuePair<String, Control>` は完全に合理的な型です。`Control` は可変ですが、そのような型によって参照されるコントロールのIDは不変になるからです。
  2. 変更可能なオブジェクトは、値の型によって作成されたものである必要があり、その外部に公開されることはありません。さらに、不変オブジェクトに対して実行されるすべての変更は、オブジェクトへの参照が構造体のフィールドに格納される前に実行する必要があります。

他の人が指摘したように、構造体が配列をシミュレートできるようにする1つの方法は、配列を保持し、要素が変更されるたびにその配列の新しいコピーを作成することです。もちろん、そのようなことはとてつもなく遅くなります。別のアプローチは、最後のいくつかの変更リクエストのインデックスと値を格納するロジックを追加することです。配列を読み取ろうとするたびに、値が最近書き込まれた値の 1 つであるかどうかを確認し、そうである場合は、配列内の値ではなく、構造体に格納されている値を使用します。構造体内のすべての「スロット」が埋まったら、配列のコピーを作成します。このアプローチは、更新が多くの異なる要素にヒットした場合に配列を再生成するのに対して、せいぜい「のみ」一定の速度向上を提供しますが、更新の大部分が少数の要素にヒットした場合に役立つ可能性があります。

Another approach when updates are likely to have a high special concentration, but hit too many elements for them to fit entirely within a struct, would be to keep a reference to a "main" array, as well as an "updates" array along with an integer indicating what part of the main array the "updates" array represents. Updates would often require regeneration of the "updates" array, but that could be much smaller than the main array; if the "updates" array gets too big, the main array can be regenerated with changes represented by the "updates" array incorporated within it.

これらのアプローチの最大の問題は、struct効率的なコピーを可能にしながら一貫した値型のセマンティクスを提示するように設計することはできますが、構造体のコードを一目見ただけではそれがほとんど明らかではないことです (plain-old と比較して) -data 構造体 (構造体に呼び出された public フィールドがあるという事実により、Fooどのように動作するかが非常に明確にFooなります)。

于 2012-07-05T20:31:49.363 に答える