A Quick Preface
A shallow copy of an object is one that points to the same place in memory as the original. Any changes made to the copy also effect the original because the copy references the same place in memory as the original. (see accessing by reference)
A deep copy of an object is one that points to a completely separate place in memory, yet contains all the same values as the original. Any changes made to the copy do not effect the original object, nor do any changes to the original effect the copy.
I was recently tasked with our business wanting to audit changes when a customer updates their profile. If the customer changes their phone number, address, or any other property of their account, the business wanted a record of that detailing the old value, the new value, and when the changes were made.
I thought the best plan of attack would be to:
1) Create a deep copy of the customer object before changes.
2) Make any requested updates to the original customer object.
3) Save the customer triggered updates to the database.
4) Compare the copied object to the modified object and record all changes in a separate place for the business.
The .NET framework, surprisingly, does not contain a method to create a deep copy of an object. You must either roll your own helper method or use an existing 3rd party library, so I decided on AutoMapper.
The Code
//define our original person object var originalPerson = new Person() { FirstName = "Jasper", LastName = "Showers", }; //ideally this would be done in a config file at app startup Mapper.Initialize(cfg => cfg.CreateMap<Person, Person>()); //create the deep copy using AutoMapper var deepCopy = Mapper.Map<Person>(originalPerson); //change properties on the original originalPerson.FirstName = "Ronald"; originalPerson.LastName = "McDonald"; //record any property changes for the two objects //leaving this part out for the sake of the example and return both objects return new List<Person>() { originalPerson, deepCopy, };
This will output the following expected results. Notice that the copied object is in fact a deep copy and was not changed by reference.
[ { "FirstName":"Ronald", "LastName":"McDonald" }, { "FirstName":"Jasper", "LastName":"Showers" } ]
You can download the Visual Studio solution used in this example here.
Your solution (and Automapper) does not actually create a deep copy.
This happens to work in your case because your mapping type (Person) only contains strings. Strings in C# are immutable, so any time you do something that looks like you’re changing the string, you aren’t. A completely new string gets created, the reference is pointed at it, and the old one gets thrown away.
As soon as you add more complex types to Person they will be copied as references and if you change the properties of the original, the copy will also contain the changes.
You are correct about strings being immutable, however, it is irrelevant to creating deep copies of objects using Automapper.
Automapper functions by creating a new target object in memory, then reflecting through both the source and the target object properties, assigning similar values to the target from the source. There are no references held to the original and therefore the target is a true deep copy containing all the values from the original yet not referencing any of the same memory.
How can I ignore 1 property being deep copied?
When setting up the config for the objects, you can specify any properties you would like to be ignored like:
CreateMap().ForMember(x => x.Blarg, opt => opt.Ignore());
From:
https://stackoverflow.com/questions/4987872/ignore-mapping-one-property-with-automapper
Deep copy usually means cloning all top level *and* recursively cloning all objects and their objects all the way down the hierarchy. Whereas shallow copy is only to clone the top level properties of an object.
What you are doing is *cloning* your object. And AutoMapper is not a cloning solution, it is a mapping solution where you are mapping properties between similar objects, typically but not always that is between levels of a solution such as an API to business layer or business to persistence.
Even the author of AutoMapper says not to use it for cloning even going so far as to say it is a a “lousy solution” for cloning.
https://github.com/AutoMapper/AutoMapper/issues/340
https://github.com/AutoMapper/AutoMapper/issues/405 and
Ah – good to know, thank you for the comment!