tags:

views:

614

answers:

9

I want to copy values from one object to another object. Something similar to pass by value but with assignment.

For example:

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

I was told to write a copy constructor for this. But this class has a lot of properties, it will probably take an hour to write a copy constructor by hand.

  1. Is there a better way to assign an object to another object by value?
  2. If not, is there a copy constructor generator?

Note: ICloneable is not available in Silverlight.

+1  A: 

C# does not have a copy constructor. There are different ways to tackle this. At the OOP level you could use inheritance or aggregation. AutoMapper might also be worth a try.

Darin Dimitrov
+2  A: 

The only way (that I'm aware of) to do this, and do it correctly, is to implement the copy yourself. Take for example:

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);
  }
}

In this example you'd need to know how Frob was implemented, i.e. the fact that you need to call the constructor to create a copy of it as Values is read-only, to be able to make a copy of a given instance of FrobAndState.

Also - you couldn't just implement FrobAndState.Copy thusly:

public class FrobAndState
{
  // ... Properties

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

Because both the instance of FrobAndState that you called .Copy() on, and the new instance would both have a reference to the same instance of Frobber.

In short, copying things is hard and any Copy implementation is difficult to get right.

Rob
A: 

I want to copy values from one object to another object. Something similiar to pass by value but with assignment.

What do you mean by "with assignment"? If you mean that you want people to be able to say:

a = b;

And for you to define what = means, the only way you can do that in C# is if b is a different type to a and you've defined an implicit conversion (or more tenuously, if a stands for something of the form x.Y where Y is a property with a setter). You can't override = for a simple assignment between identical types in C#.

I was told to write a copy constructor for this. But this class has alot of properties, it will probably take an hour to write a copy constructor by hand.

If that's really true, then I would guess that you have a different problem. Your class is too big.

Daniel Earwicker
+6  A: 

If you can mark the object that is to be cloned as Serializable then you can use in-memory serialization to create a copy. Check the following code, it has the advantage that it will work on other kinds of objects as well and that you don't have to change your copy constructor or copy code each time an property is added, removed or changed:

    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;
        }
    }
Thomas
That's not going to work in general, because there will probably be references to objects which shouldn't be copied. Doing it by hand is the only way to get it right (because that lets you define what copying really means). If classes are large enough that that's a real problem, they're too large.
Donal Fellows
@Donal - not to mention there may be classes referenced which aren't serializable. That said, if the OP has full control over the classes being copied, thus serialized, it's definately an adequate solution for them.
Rob
Let's see... Tagged with Silverlight, so probably got some references to instances of classes not under control. Manual copying is better (writing a custom serializer is the other alternative, but that's harder than doing the copying).
Donal Fellows
A: 

If you make your class Serializable you could Serialize it to a MemoryStream and Deserialize to a new instance.

Peter
A: 

If you want copy-on-assignment you should be using a struct instead of a class. But be careful, it is easy to make subtle mistakes. It is highly recommended that all stucts be immmutable to reduce the chance for error.

Jonathan Allen
+2  A: 

There is a protected member called "MemberwiseClone", you can write this in your class...

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

then you can access..

MyClass newObject = oldObject.Clone();
Akash Kava
A: 

Though, this may not answer your question directly, but to add a cent; usually the term Clone is linked with shallow copy(referenced objects). To have a deep copy, I believe you will need to look into the some creational pattern(prototype?). The answer to this question might help.

KMan
A: 

You implement Justin Angel's method of cloning objects in Silverlight

using System;

using System.Reflection;

using System.Windows;

namespace 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;
    }
}

}

Then you can do this

MyClass newObject = SilverlightExtensions.Clone(oldObject);

retornam