How to make a shallow copy of an array?
I pass a two-dimensional array as a property to my user control. There I store this values 开发者_如何学Pythonin another two-dimensional array:
int[,] originalValues = this.Metrics;
Later, I change values in this.Metrics
. But now if I retrieve values from originalValues, I get the changed values from this.Metrics
. How do I make a copy of the elements of this.Metrics
and don't just get the reference of the array?
I don't know where I got this from, but this works well for me.
public static class GenericCopier<T> //deep copy a list
{
public static T DeepCopy(object objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
You can clone an array, which makes a copy of it:
int[,] originalValues = (int[,])this.Metrics.Clone();
The crux of your problem is here:
There I store this values in another two-dimensional array
This is actually inaccurate. You are not creating a new array; you are setting your originalValues
variable to the same array. For a more detailed explanation, see below.
The confusion expressed in the comments to Pieter's answer is due to some uncertainty surrounding the term "deep copy."
When it comes to copying objects, there's deep copying and shallow copying.
Deep copying involves making a copy of all the data belonging to an object, which means that if the object includes members which are themselves complex (for example, instances of user-defined reference types), those objects must be deep-copied as well (along with all of their members, and so on).
Shallow copying involves simply copying all of the fields from one object to another, which means that if the object includes reference types, only the references need to be copied (and so the copied references will be pointing to the same objects).
In the case of the code you've posted:
int[,] originalValues = this.Metrics;
... there's actually no copying of any objects at all. All you've done is copied a single reference, assigning the value of this.Metrics
(a reference) to the variable originalValues
(also a reference, to the very same array). This is essentially the same as a simple value assignment, like this:
int x = y; // No objects being copied here.
Now, the Array.Clone
method makes, in fact, a shallow copy. But as Pieter pointed out, there's really no difference between a "shallow" or "deep" copy of an array of integers, since integers are not complex objects.
If you had something like this:
StringBuilder[,] builders = GetStringBuilders();
StringBuilder[,] builderCopies = (StringBuilder[,])builders.Clone();
..., you'd end up with a whole new array (a copy, yes), but one containing all of the same StringBuilder
objects (so a shallow copy). This is where deep versus shallow copying comes into play; if you wanted a new array containing copies of all of the StringBuilder
objects from builders
, you'd need to make a deep copy.
If the object you are copying is an array, then you can use:
Array.Copy(sourceArray, destinationArray, sourceArray.Count)
This will give you a separate copy of the original array into your destination array.
If you want to deep copy an array of reference types, you can do this methodology:
Implement IClonable
iterface for your class and do your deep copy of all value typed fields inside to another constructed object.
class A: ICloneable {
int field1;
public object Clone()
{
A a= new A();
//copy your fields here
a.field1 = this.field1;
...
}
}
Then you can do the actual copy using
A[] array1 = new A[]{....};
A[] array2 = array1.Select(a => a.Clone()).ToList();
IClonable is great but unless you IClonable
every type in your top level cloned type, you end up with references, AFAIK.
Based on that, unless you want to walk the object and clone each object within, this seems the simplest approach.
It's simple and guarantees a clean break from references of deep objects in the original:
using Newtonsoft.Json;
private T DeepCopy<T>(object input) where T : class
{
var copy = JsonConvert.SerializeObject((T)input); // serialise to string json object
var output = JsonConvert.DeserializeObject<T>(copy); // deserialise back to poco
return output;
}
Usage:
var x = DeepCopy<{ComplexType}>(itemToBeCloned);
Where ComplexType
is anything wanting a break from references.
It takes any Type in, stringifies it, then de-stringifies to a new copy.
Best use example: If you've selected a complex type as a result of a lambda query and want to modify the result without affecting the original.
You can deepcopy a 1d array using LINQ.
var array = Enumerable.Range(0, 10).ToArray();
var array2 = array.Select(x => x).ToArray();
array2[0] = 5;
Console.WriteLine(array[0]); // 0
Console.WriteLine(array2[0]); // 5
With 2d array, this will not work because 2d array doesn't implement IEnumerable.
You need to create a new array. You then need to manually copy the value of each element into the new array. What you are doing in the example given is to create two array variables which both reference the same array.
The problem with the clone method is that it is a shallow copy. In this isntance, because you are using int
, it does not mater. Howver, if you had an array of classes the definition of the ICLonable interface leaves it ambiguous as to how deep the clone will go.
Imagine if you had a class that has properties that are other classes which has properties that are other classes. The clonable interface does not state whether it will also clone the sub members or not. Moreover, many people have different views on what the expected behaviour is.
Hence this is why it is often recommended to define two interfaces, IShallowCopy
and IDeepCopy
.
Here is a fast solution that is almost similar than some answers here, but which mention MemberwiseClone.
I have POCO classes that only contains reference values.
public class MyPoco {
public int x { get; set; }
public int y { get; set; }
public int z { get; set; }
// Add a "Clone" method.
public MyPoco Clone() {
return (MyPoco)this.MemberwiseClone();
}
}
Then use LINQ to build a new array of clones:
var myClone = MyPocoArray.Select(x => x.Clone).ToArray();
精彩评论