Crate Duke
Crate Duke

Reputation: 98

Clone object without changing the values of the original object C#

I need to make a copy of a MyGame class and use it in my simulation for game trials before I select a move to play.

For example :

public class MyGame
  {
     private int Start;
     private Board board;

     //Constructor 

     public void Play()
     {
         //play game 
     }

     public object Clone()
     {     


     }

  }

 public class Board
    {
        private int Count; 

        //Constructor

        //Some methods and properties

     public object Clone()
     {     

     }

    }

Writing code for the method Clone() I have tried

  1. MemberwiseClone()
  2. (Board) this.MemberwiseClone()
  3. Board b = (Board) this.Board

I have read alot of articles and forums about this topic. The answer most people use is Deep cloning objects in C#, I tried samples with respect to my project but I still get my simulation modifying the original object (MyGame Class) and not the copy.

Upvotes: 0

Views: 5715

Answers (5)

Matthias Meid
Matthias Meid

Reputation: 12521

Here I have an example for a deep copy, which deeply copies all reference type objects that are used with a copy constructor:

public sealed class MyGame
{
    private int start;
    private Board board;

    public MyGame(MyGame orig)
    {
        // value types - like integers - can easily be
        // reused
        this.start = orig.start;

        // reference types must be clones seperately, you
        // must not use orig.board directly here
        this.board = new Board(orig.board);
    }
}

public sealed class Board
{
    private int count;

    public Board(Board orig)
    {
        // here we have a value type again
        this.count = orig.count;

        // here we have no reference types. if we did
        // we'd have to clone them too, as above
    }
}

I think your copy might be somehow shallow and re-use some references (like for instance this.board = orig.board instead of creating a new board). This is a guess though, as I can't see your cloning implementation.

Furthermore, I used copy constructors instead of implementing ICloneable. The implementation is almost the same. One advantage though is that you simplify dealing with subclasses:

Suppose you had a MyAwesomeGame : MyGame, not overriding MyGame.Clone. What would you get from myAwesomeGame.Clone()? Actually, still a new MyGame because MyGame.Clone is the method in charge. One may carelessly expect a properly cloned MyAwesomeGame here, however. new MyGame(myAwesomeGame) still copies somehow incompletely, but it's more obvious. In my example I made the classes sealed to avoid this failures. If you can seal them, there's good change it will make your life simpler.

Implementing ICloneable is not recommended in general, see Why should I implement ICloneable in c#? for more detailed and general information.

Here I have an ICloneable approach anyway, to make things complete and enable you to compare and contrast:

public class MyGame : ICloneable
{
    private int start;
    private Board board;

    public object Clone()
    {
        var copy = new MyGame();
        copy.start = this.start;
        copy.board = (Board)this.board.Clone();
        return copy;
    }
}

public class Board : ICloneable
{
    private int count;

    public object Clone()
    {
        var copy = new Board();
        copy.count = this.count;
        return copy;
    }
}

Upvotes: 4

Sameer Singh
Sameer Singh

Reputation: 1366

I have two extension methods that I use to achieve this. Demo code below:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace SimpleCloneDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var person = new Person { Id = 1, FirstName = "John", Surname = "Doe" };

            var clone = person.Clone();
            clone.Id = 5;
            clone.FirstName = "Jane";

            Console.WriteLine(@"person: {0}", person);
            Console.WriteLine(@"clone: {0}", clone);

            if (Debugger.IsAttached)
                Console.ReadLine();
        }
    }

    public class Person
    {
        public int Id { get; set; }

        public string FirstName { get; set; }

        public string Surname { get; set; }

        public override string ToString()
        {
            return string.Format("Id: {0}, Full Name: {1}, {2}", Id, Surname, FirstName);
        }
    }

    public static class ObjectExtensions
    {
        public static T Clone<T>(this T entity) where T : class
        {
            var clone = Activator.CreateInstance(entity.GetType());
            var entityPropValueDictionary = entity.AsPropValueDictionary();
            foreach (var prop in clone.GetType().GetProperties())
            {
                clone.GetType().GetProperty(prop.Name).SetValue(clone, entityPropValueDictionary[prop.Name]);
            }
            return clone as T;
        }

        public static IDictionary<string, object> AsPropValueDictionary<T>(this T instance, params BindingFlags[] bindingFlags)
        {
            var runtimeBindingFlags = BindingFlags.Default;

            switch (bindingFlags.Count())
            {
                case 0:
                    runtimeBindingFlags = BindingFlags.Default;
                    break;
                case 1:
                    runtimeBindingFlags = bindingFlags[0];
                    break;
                default:
                    runtimeBindingFlags = bindingFlags.Aggregate(runtimeBindingFlags, (current, bindingFlag) => current | bindingFlag);
                    break;
            }

            return runtimeBindingFlags == BindingFlags.Default
                ? instance.GetType().GetProperties().ToDictionary(prop => prop.Name, prop => prop.GetValue(instance))
                : instance.GetType().GetProperties(runtimeBindingFlags).ToDictionary(prop => prop.Name, prop => prop.GetValue(instance));
        }
    }
}

Result:

SimpleCloneDemo Result Screenshot

I wrote these quick-and-dirty extension methods in a hurry so there are probably some issues with it and they are probably horribly inefficient, but they seemed to work for my use case. They may help you, too.

Upvotes: 0

Adriano Repetti
Adriano Repetti

Reputation: 67148

MemberwiseClone() creates a stupid shallow clone of each member of an object. This works fine when members are value types but in case of reference types it fails because it'll clone pointers and not pointed objects.

Starting from your code a memberwise clone is something like this:

public object Clone()
{
    MyGame cloned = new MyGame();
    cloned.Start = this.Start; // Copied (cloned) because value type
    cloned.Board = this.Board; // This is not a copy, just a reference!
}

A better solution for a deep clone would be to implement ICloneable (for example, otherwise a copy constructor approach is also good) for each reference type, let's suppose Board is cloneable too:

public object Clone()
{
    MyGame cloned = new MyGame();
    cloned.Start = this.Start;
    cloned.Board = (Board)this.Board.Clone();
}

Please note that in your example Board can implement Clone() using MemberwiseClone() because its members are all value types.

If you can't manage this (for example because code is not accesible) or you need a quick/dirty solution you may consider to user serializaiton (in memory). Which serializer is a big question, each one has some limitations (about what's serialized and how). For example XML serializer won't serialize private fields (it won't serialize fields at all). Faster one is binary formatter but you need to mark each class with a proper attribute.

Change according serializer you prefer (according to your requirements), in this case I assume you marked MyGame and Board as [Serializable] for the quick binary serialization:

public object Clone()
{
    using (var stream = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(stream, this);
        stream.Seek(0, SeekOrigin.Begin);

        return formatter.Deserialize(stream);
    }
}

Upvotes: 1

epitka
epitka

Reputation: 17657

Try this

   public static T DeepCopy<T>(this T obj)
        {
            T result;
            var serializer = new DataContractSerializer(typeof(T));
            using (var ms = new MemoryStream())
            {

                serializer.WriteObject(ms, obj);
                ms.Position = 0;

                result = (T)serializer.ReadObject(ms);
                ms.Close();
            }

            return result;
        }

Upvotes: 0

Gusdor
Gusdor

Reputation: 14332

The simplest and most reliable way to implement deep cloning is to serialize, and then deserialize your objects. This can have a large performance cost associated with it. Consider classes from this namespace for serialization http://msdn.microsoft.com/en-us/library/System.Xml.Serialization.aspx

Deep cloning requires recursively creating a new instance of every property that is not a value type. Cloning MyGame would require a new instance of MyGame and a new instance of Board, both populated with the same Start and Count values as their originals. This is fiddly and a nightmare to maintain. As you can guess, it is not an automatic process out of the box but it can be, using reflection (which is how the xml serialization above works.

MemberwiseClone only creates a new instance of the object you called it on - all references remain the same.

Upvotes: 1

Related Questions