2015-01-09 48 views
1

從克隆的對象分配我還該dot net Fiddle對象複製時未更新/在C#

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 |喬金

100 |鮑勃·史密斯

但是,如果我想撤消原來的對象上的變化,我改變了我的方法DoSomething的( )到此(這是Fiddle

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

    _objA = _objACopy; 
} 

但結果是錯誤的;

99 |喬金

100 |鮑勃·史密斯

正確的結果應該是如下;

99 |喬金

99 |喬金

所以通過分配對象到對象不工作,但如果我分配給每個個人的財產像這樣,它確實有效(這是Fiddle

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

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

有人可以幫助我瞭解什麼是錯誤的,爲什麼我可以打電話

_objA = _objACopy;

返回到原始對象。

+1

我真的很困惑。你爲什麼要複製修改原件? –

+0

這個例子來自一個更大的項目。基本上這個對象被傳入一個Wi​​nform(表單)。如果用戶進行了更改,並且想要撤消它們,我可以將它們還原。這就是爲什麼我要這樣展示,以幫助解釋我遇到的問題 – Tommassiov

+2

@ Tommo1977我不認爲這是一種撤消功能的好方法。你是否考慮過不可變的對象和堆棧? –

回答

2

這裏的問題是,在C#該對象變量爲引用類型

引用類型變量是變量,只保存對象存儲在內存中的引用。如果您現在將一個對象分配給另一個對象,它只會將該內存地址分配給您的變量。

所以,你現在有兩個變量objAobjACopy,它們都指向你記憶中的同一個物體。如果您現在更改IdobjA,那麼它也將在objACopy中更改。

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

如果你想改變objA,在不影響objACopy你要複製,而不是隻在內存ADRESS的整個對象。爲此,您可以使用關鍵字new創建一個新對象,並從其他對象複製其屬性。這樣你最終得到2個不同的對象,每個對象都有自己的內存空間,不會相互影響。

+0

也許考慮使用像Automapper – iwayneo

+0

我想通過調用Object.Clone (objA);我正在創建一個新的對象 – Tommassiov

+0

@ Tommo1977,這是正確的,但是通過'_objA = _objACopy;'您再次覆蓋變量'_objA'併爲其指定'_objACopy'。通過這個你對'_objA'的id和名字所做的所有更改都不再有效。您將變量'_objA'的指針從其自己的對象更改爲'_objACopy',從而導致使用相同的內存對象。所以最後'_objA'和'_objACopy'具有相同的屬性。 – RononDex

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 

,而不是「qux」在最後一行,因爲你只能這樣做,如果你做的簽名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