2010-03-26 170 views
6

我想將一個對象的值複製到另一個對象。類似於通過價值傳遞但具有分配的東西。c#拷貝構造函數生成器

例如:

PushPin newValPushPin = oldPushPin; //I want to break the reference here. 

有人告訴我,寫這個拷貝構造函數。但是這個類有很多屬性,可能需要一個小時才能手動編寫一個拷貝構造函數。

  1. 有沒有更好的方法來按值分配一個對象到另一個對象?
  2. 如果不是,是否有複製構造函數生成器?

注意:ICloneable在Silverlight中不可用。

+0

@Brian,手寫複製構造函數耗時過長的原因是我需要爲主類(** PushPin **)使用的其他類編寫複製構造函數。 – 2010-03-26 09:11:11

回答

6

如果您可以將要克隆的對象標記爲可序列化,那麼您可以使用內存中序列化來創建副本。請檢查以下代碼,它的優點是它也可以用於其他類型的對象,並且每次添加,刪除或更改屬性時都不必更改複製構造函數或代碼複製:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var foo = new Foo(10, "test", new Bar("Detail 1"), new Bar("Detail 2")); 

      var clonedFoo = foo.Clone(); 

      Console.WriteLine("Id {0} Bar count {1}", clonedFoo.Id, clonedFoo.Bars.Count()); 
     } 
    } 

    public static class ClonerExtensions 
    { 
     public static TObject Clone<TObject>(this TObject toClone) 
     { 
      var formatter = new BinaryFormatter(); 

      using (var memoryStream = new MemoryStream()) 
      { 
       formatter.Serialize(memoryStream, toClone); 

       memoryStream.Position = 0; 

       return (TObject) formatter.Deserialize(memoryStream); 
      } 
     } 
    } 

    [Serializable] 
    public class Foo 
    { 
     public int Id { get; private set; } 

     public string Name { get; private set; } 

     public IEnumerable<Bar> Bars { get; private set; } 

     public Foo(int id, string name, params Bar[] bars) 
     { 
      Id = id; 
      Name = name; 
      Bars = bars; 
     } 
    } 

    [Serializable] 
    public class Bar 
    { 
     public string Detail { get; private set; } 

     public Bar(string detail) 
     { 
      Detail = detail; 
     } 
    } 
+3

這通常不起作用,因爲可能會引用不應該被複制的對象。手工完成是唯一正確的方法(因爲這可以讓你定義複製的真正含義)。如果課程足夠大,這是一個真正的問題,他們太大了。 – 2010-03-26 10:57:23

+0

@Donal - 更不用說可能會引用不可序列化的類。也就是說,如果OP可以完全控制正在複製的類,從而序列化,那麼對於它們來說,這絕對是一個適當的解決方案。 – Rob 2010-03-26 11:03:58

+0

讓我們看看...用Silverlight標記,所以可能會得到一些對不受控制的類實例的引用。手動複製更好(編寫自定義序列化器是另一種選擇,但這比複製更困難)。 – 2010-03-26 22:33:23

2

要做到這一點,並且正確地做到這一點,唯一的方法就是自己實現副本。舉個例子:

public class FrobAndState 
{ 
    public Frob Frobber { get; set;} 
    public bool State { get; set; } 
} 
public class Frob 
{ 
    public List<int> Values { get; private set; } 
    public Frob(int[] values) 
    { 
    Values = new List<int>(values); 
    } 
} 

在這個例子中你需要知道 FROB是如何實現的,即你需要調用構造函數是隻讀的創建一個副本作爲價值的事實,能夠複製給定的FrobAndState實例。

而且 - 你不能只是實現FrobAndState.Copy正是如此:

public class FrobAndState 
{ 
    // ... Properties 

    public FrobAndState Copy() 
    { 
    var new = new FrobAndState(); 
    new.State = this.State; 
    new.Frobber = this.Frobber; 
    } 
} 

因爲無論你在叫.Copy()的FrobAndState的實例,新的實例就都有的一個參考Frobber的同一個實例。

總之,複製東西是和任何複製實現是很難得到正確的。

0

我想從一個對象 複製到另一個對象的值。類似的東西 傳遞值,但與分配。

你是什麼意思的「與轉讓」?如果你的意思是你希望人們能夠說:

a = b; 

如果b是一個不同類型a和你」你界定什麼=手段,你可以做,在C#中是唯一的出路我們定義了一個隱式轉換(或者更簡單的說,如果a代表x.Y的形式,其中Y是一個包含setter的屬性)。您不能覆蓋=以獲取C#中相同類型之間的簡單分配。

我被告知要爲此寫一個拷貝構造函數 。但是這個類有很多 屬性,它可能需要 小時才能通過 手寫一個拷貝構造函數。

如果那是真的,那麼我會猜測你有一個不同的問題。你的班級太大了。

0

如果你讓你的班級Serializable你可以SerializeMemoryStreamDeserialize到一個新的實例。

0

如果你想複製作業,你應該使用struct而不是class。但要小心,容易犯下微妙的錯誤。強烈建議所有的工作人員儘量減少出錯的機會。

3

有一個稱爲保護成員 「MemberwiseClone」,你可以在你的類寫這個...

public MyClass Clone(){ 
    return (MyClass)this.MemberwiseClone(); 
} 

,那麼你可以訪問..

MyClass newObject = oldObject.Clone(); 
0

雖然,這可能不是回答你的問題直接,但增加一分;通常術語Cloneshallow copy(被引用的對象)相關聯。要獲得深層副本,我相信您需要查看一些創建模式(prototype?)。 this question的答案可能會有所幫助。

0

您實現了Justin Angel在Silverlight中克隆對象的方法

using System;

using System.Reflection;

using System.Windows;

命名空間JustinAngelNet.Silverlight.Framework

{

public static class SilverlightExtensions 

{ 

    public static T Clone<T>(T source) 
    { 
     T cloned = (T) Activator.CreateInstance(source.GetType()); 

     foreach (PropertyInfo curPropInfo in source.GetType().GetProperties()) 
     { 
      if (curPropInfo.GetGetMethod() != null 
       && (curPropInfo.GetSetMethod() != null)) 
      { 
       // Handle Non-indexer properties 
       if (curPropInfo.Name != "Item") 
       { 
        // get property from source 
        object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] {}); 

        // clone if needed 
        if (getValue != null && getValue is DependencyObject) 
         getValue = Clone((DependencyObject) getValue); 

        // set property on cloned 
        if (getValue != null) 
        curPropInfo.GetSetMethod().Invoke(cloned, new object[] {getValue}); 
       } 
        // handle indexer 
       else 
       { 
        // get count for indexer 
        int numberofItemInColleciton = 
         (int) 
         curPropInfo.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(source, new object[] {}); 

        // run on indexer 
        for (int i = 0; i < numberofItemInColleciton; i++) 
        { 
         // get item through Indexer 
         object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] {i}); 

         // clone if needed 
         if (getValue != null && getValue is DependencyObject) 
          getValue = Clone((DependencyObject) getValue); 
         // add item to collection 
         curPropInfo.ReflectedType.GetMethod("Add").Invoke(cloned, new object[] {getValue}); 
        } 
       } 
      } 
     } 

     return cloned; 
    } 
} 

}

然後就可以做到這一點

MyClass的NEWOBJECT = SilverlightExtensions.Clone(的OLDobject);