从C#中的克隆对象复制/分配时对象未更新

时间:2015-01-09 11:39:37

标签: c#

我在dot net Fiddle

也有以下代码
using System;
using Newtonsoft.Json;

public class Program
{
    public static void Main()
    {
        ObjectA objectA = new ObjectA();

        objectA.Id = 99;
        objectA.Name = "Joe King";

        Console.WriteLine("BEFORE");
        Console.WriteLine(objectA.Id.ToString() + "|" + objectA.Name);

        ObjectB objectB = new ObjectB(objectA);

        objectB.DoSomething();
        Console.WriteLine("AFTER");
        Console.WriteLine(objectA.Id.ToString() + "|" + objectA.Name);

    }
}


public class ObjectB
{
    ObjectA _objA = null;
    ObjectA _objACopy = null;

    public ObjectB(ObjectA objA)
    {
        _objA = objA;
        _objACopy = Object.Clone<ObjectA>(objA);
    }

    public void DoSomething()
    {
        _objA.Id = 100;
        _objA.Name = "Bob Smith";
    }
}


public class ObjectA
{
    public int Id {get;set;}
    public string Name {get;set;}
}

public class Object
{
    public static T Clone<T>(T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }
}

这给出了我的以下结果,这是正确的。

99 | Joe King

100 | Bob Smith

但是,如果我想撤消对原始对象的更改,我将方法DoSomething()更改为this(这是Fiddle

public void DoSomething()
{
    _objA.Id = 100;
    _objA.Name = "Bob Smith";

    _objA = _objACopy;
}

但结果是错误的;

之前

99 | Joe King

AFTER

100 | Bob Smith

正确的结果应如下所示;

之前

99 | Joe King

AFTER

99 | Joe King

因此,通过将Object分配给Object不起作用,但如果我像这样分配每个单独的属性,它确实有效(这是Fiddle

public void DoSomething()
{
    _objA.Id = 100;
    _objA.Name = "Bob Smith";

    _objA.Id = _objACopy.Id;
    _objA.Name = _objACopy.Name;
}

有人可以帮我理解出了什么问题以及为什么我可以打电话

  

_objA = _objACopy;

返回原始对象。

2 个答案:

答案 0 :(得分:2)

问题在于,C#中的对象变量是引用类型

引用类型变量是变量,仅包含对象存储在内存中的位置的引用。如果现在将一个对象分配给另一个对象,它只会将该内存地址分配给您的变量。

所以你现在有两个变量objAobjACopy,它们都指向你记忆中的同一个对象。如果您现在更改了Id的属性objA,那么objACopy也会更改。{/ p>

objACopy = objA;
objACopy.Id = 1111;
// objA.Id is now also changed to 1111, beacuse they point to the same memory address

如果您想更改objA,而不影响objACopy,则必须复制整个对象而不是仅复制内存地址。为此,您可以使用new关键字创建一个新对象,并将其属性从另一个对象复制。这样你最终得到了两个不同的对象,每个对象都有自己的内存空间,并且不会相互影响。

答案 1 :(得分:0)

由于_objA = _objACopy并未更改最初分配给_obja的对象,因此它只会更改_objA引用的对象。

这是重现问题的最小例子:

public class Foo
{
    public string Bar { get; set; }
}

public class Program
{
    public static void Main()
    {
        var foo  = new Foo { Bar = "bar" };
        Console.WriteLine("Before: {0}", foo.Bar);
        ChangeBar(foo);
        Console.WriteLine("After: {0}", foo.Bar);
        ChangeBarReference(foo);
        Console.WriteLine("After reference: {0}", foo.Bar);     
    }

    private static void ChangeBar(Foo foo)
    {
        foo.Bar = "baz";
    }

    private static void ChangeBarReference(Foo foo)
    {
        foo = new Foo { Bar = "qux" };
    }
}

打印:

Before: bar
After: baz
After reference: baz

而不是&#34; qux&#34;在最后一行,因为只有在签名ref Foo foo时才能这样做。

private static void ChangeBarReferenceRef(ref Foo foo)
{
    foo = new Foo { Bar = "tok" };
}   

// ...

ChangeBarReferenceRef(ref foo);
Console.WriteLine("After ref reference: {0}", foo.Bar); 

打印:

Before: bar
After: baz
After reference: baz
After ref reference: tok