15

object一般的な処理のためにキャストされた値 (構造体インスタンス) があります。値のコピーを作成する必要があります。私はそれを持っているだけTypeで、コンパイル時にそれが何であるかわからないため、これを明示的に行うことはできません。

デフォルトでは、参照のコピーを取得します: var copy = objectOfMyStruct;. 明示的な浅いコピーを作成することを考えましたMemberwiseClone()が、保護されたメソッドであり、変更できないため、これを行うことはできませんMyStruct

Convert.ChangeType(objectOfMyStruct, typeOfMyStruct)内部で変換 (実際には変換なし) が発生し、オブジェクトが再び返されるため、役に立ちません。

どうすればこれを行うことができますか?

編集:

元の値を保持するためにコピーを作成し、それを逆シリアル化して OnChangeHandler に渡す必要があります。簡略化された実装は次のとおりです。

var oldValue = type.GetValue(reference);
var newValue = oldValue; // something better is needed here
Deserialize(type, stream, ref newValue);
OnChange(oldValue, newValue);
type.SetValue(reference, newValue);

デルタ (変更) のみが送信されるため、コピーが作成され、元の値に適用する必要があります。

編集2:

この実装はプリミティブ型に対しては正常に機能するため、int もボックス化されていますが、参照をコピーする代わりにコピーしています。私はこれを理解していません。


必要なものの例を次に示します。

LINQPadでテストできるこの例では、実装されたインターフェイスを介した呼び出しによって変更されたときに、元の構造体のみが変更されるように、構造体のクローンを作成し、ボックス化されていない型にキャストし直す必要があります。したがって、問題は次のとおりです。その Clone メソッドをどのように記述すればよいですか?

void Main()
{
    object original = new Dummy { Property = 42, Field = "Meaning of life" };
    object clone = Clone(original);

    ((IDummy)original).Mutate(); // will modify the boxed struct in-place
    original.Dump();

    // should output different if Clone did its job
    clone.Dump();
}

static object Clone(object input)
{
    return input;
}

public interface IDummy
{
    void Mutate();
}

public struct Dummy : IDummy
{
    public int Property { get; set; }
    public string Field;

    public void Mutate()
    {
        Property = 77;
        Field = "Mutated";
    }
}
4

4 に答える 4

5

コピーを作成するだけでなく、そのコピーを実際に使用できるようにしたいと考えています。

そして、それを使用するには、適切な型にキャスト (ボックス化解除) する必要があります。これにより、効果的にコピーが作成されます。実際、ボックスに値を入力しても、すでにコピーが作成されています。

したがって、(たとえば) これらのオブジェクトが int または float であることがわかっている場合は、次のようにすることができます。

if (obj is int)
{
    int i = (int) obj;
    // do something with the copy in i
}
else if (obj is float)
{
    float f = (float) obj;
    // do something with the copy in f
}

評価する型が多数ある場合は、switchステートメントまたはDictionary<Type,Action<object>>.

コンパイル時に知らない型 (ある種のプラグイン メカニズムを介して動的に追加された型) を処理する必要がある場合、これは不可能ですが、何もすることもできません。オブジェクトと (インターフェースを介さない場合)。

アップデート:

質問を変更したので、より良い答えがあります。コピーを作成する必要はありません。構造体をボックス化することで作成されています。

例:

int i = 42;

// make a copy on the heap
object obj = i;

// modify the original
i = i + 1;

// copy is not modified
Debug.Assert((int)obj == 42);

明らかに、int便宜上ここで使用していますが、すべての構造体に当てはまります。構造体がインターフェイスを実装している場合は、オブジェクトをそのインターフェイスにキャストして (2 番目のコピーは作成されません)、それを使用できます。ボックス内のコピーを操作しているため、元の値は変更されません。

更新 2:

非常に明確にするために、これはすべての構造体で機能します。例えば:

interface IIncrementor
{
    void Increment();
}

struct MyStruct : IIncrementor
{
    public int i;

    public void Increment()
    {
        this.i = this.i + 1;
    }

    public override string ToString()
    {
        return i.ToString();
    }
}

// in some method:

MyStruct ms = new MyStruct();
ms.i = 42;

Console.Writeline(ms); // 42

object obj = ms;

IIncrementable ii = (IIncrementable) obj;
ii.Increment();

Console.Writeline(ms); // still 42

Console.Writeline(ii); // 43

もう1つの更新:

それ以外の

object original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = Clone(original);

書きます

Dummy original = new Dummy { Property = 42, Field = "Meaning of life" };
object clone = original;

あなたは大丈夫です。

于 2013-10-25T13:42:22.797 に答える
4

LINQPad の例に感謝します。それはあなたの質問を非常に明確にし、解決策を考え出すための出発点を与えてくれました。

これは非常に強引な解決策ですが、誰かがよりエレガントな答えを思いつくまで、目的に役立つかもしれません:

static object Clone(object input)
{
    IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(input));
    try
    {
        Marshal.StructureToPtr(input, p, false);
        return Marshal.PtrToStructure(p, input.GetType());
    }
    finally
    {
        Marshal.FreeHGlobal(p);
    }
}

これがどのように機能するかです:

  • 構造体を保持するのに十分な大きさのアンマネージ メモリを割り当てます。
  • StructureToPtr入力をボックス化解除し、アンマネージ メモリにコピーします。

    構造体が値型の場合は、ボックス化またはボックス化解除できます。ボックス化されている場合は、コピーする前にボックス化解除されます。

  • PtrToStructure新しい構造を作成し、それをボックス化して返します:

    このオーバーロード メソッドに値の型を渡すことができます。この場合、返されるオブジェクトはボックス化されたインスタンスです。

  • アンマネージ メモリが解放されます。

于 2013-10-25T14:37:53.757 に答える
2

別の答えは次のとおりです。

static object Clone(object input) =>
    typeof(object)
    .GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance)
    .Invoke(input, null);

Object.MemberwiseCloneメソッドを使用します。このメソッドは保護されているため、リフレクション経由で呼び出す必要があります。

このアプローチは、列挙型と、[StructLayout(LayoutKind.Auto)].

于 2016-04-22T17:52:43.003 に答える