Christoffer
Christoffer

Reputation: 7787

Copy object or new object with copied properties?

I'm making a filebackup-program using profiles to save settings etc. At the moment the structure is like this:

The MainForm has a ProfileHandler that has a bunch of Profile-objects

When you make any changes in the program (adding files to a list, changing destination path etc.) - the settings are saved in a temporary profile. So far, so good...

But when I want to save the profile, I can't really decide which way to go. Right now I pass the temporary profile-object to an AddProfile method in the ProfileHandler. The method then creates a new profile object, using the properties from the temporary profile object as parameters (except for a List<string>, which just copies the content with Addrange).

It works, but is it good?

public bool AddProfile(Profile tempProfile)
{
    if (tempProfile != null)
    {
        Profile profile = new Profile(tempProfile.Name, tempProfile.TargetPath);
        profile.Filelist.Clear();
        profile.Filelist.AddRange(tempProfile.Filelist);
        _profiles.Add(profile);
    }
    return (tempProfile != null);
}

Is there a slicker way of doing it? Somehow I feel there must be a simple way to just create a new instance of the Profile object that is a simple copy of the tempProfile. In my case the Profile class only has three fields which makes it easy, but what if it had loads of them?

I hope I'm not too unclear. I'm a bit new at this.

Upvotes: 1

Views: 217

Answers (3)

devgeezer
devgeezer

Reputation: 4189

You might check out the protected object.MemberwiseClone method. This method has been around since the early days.

void Main()
{
    Profile p = new Profile("bob", @"c:\foo");
    p.FileList.Add("bar.txt");

    Profile copy = p.DeepCopy();
    copy.FileList.Clear();
    copy.FileList.Add("baz.log");

    p.Dump("p");
    copy.Dump("copy");
}

public class Profile
{
    public Profile(string name, string targetPath)
    {
        this.Name = name;
        this.TargetPath = targetPath;
        this.FileList = new List<string>();
    }

    public Profile DeepCopy()
    {
        Profile copy = (Profile)this.MemberwiseClone(); // this takes care of Name & TargetPath
        copy.FileList = new List<string>(this.FileList);
        return copy;
    }

    public string Name { get; private set; }
    public string TargetPath { get; private set; }
    public List<string> FileList { get; private set; }
}

You can think of MemberwiseClone as making a shallow bitwise copy. This is generally okay for value-types like int, double, float and even for immutable reference types like string. However, you'll want to make copies of mutable reference types like List<string> in the profile class so that mutations of the copy don't also change the original. It's kind of a fine-line in terms of maintenance - if you add a new mutable property, you have to remember to make a copy in the deep-copy method after the cloning happens.

Depending on your design, there could be other things like event registrations that you might not want copied. You can think of these as transients. You need to be cautious too if your app is multi-threaded, you will have to take the same pains during your copy that you do in other places to maintain your object's integrity (for example taking locks.)

I don't have performance numbers. Regardless, whatever advice you take, you're best suited to do some performance tests of your own to see if it's fast-enough.

Upvotes: 1

Jimmy Hoffa
Jimmy Hoffa

Reputation: 5967

Just create a constructor that takes a Profile and does the copy like so:

public Profile(Profile profileToCreateFrom)
    : this(profileToCreateFrom.Name, profileToCreateFrom.TargetPath)
{
    FileList = profileToCreateFrom.FileList.Select(file => file).ToList();
}

Then you add the new one with:

_profiles.Add(new Profile(tempProfile));

Upvotes: 1

Ankush
Ankush

Reputation: 2554

You can make a deep copy of your tempProfile and save that. Here is sample code Source How do you do a deep copy of an object in .NET (C# specifically)?

public static T DeepClone<T>(T obj)
{
 using (var ms = new MemoryStream())
 {
   var formatter = new BinaryFormatter();
   formatter.Serialize(ms, obj);
   ms.Position = 0;

   return (T) formatter.Deserialize(ms);
 }
}

Upvotes: 1

Related Questions