2010-03-26 167 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);