C#按值复制数组

24

我有一个类型化数组 MyType[] types;,我想制作一个独立的数组副本。我尝试了这个:

MyType[] types2 = new MyType[types.Length] ;

types2 = types ;
但这样会创建对第一个对象的引用。然后我尝试了
Array.Copy( types , types2 , types.Length ) ;

但是我也有同样的问题:修改第一个数组中的值同时也会改变副本中的值。

如何制作完全独立或深度复制一个数组、列表或可枚举对象?

8个回答

17

根据第一个帖子,他只需要这个“数组的独立副本”。对shallowCopy数组本身的更改不会出现在types数组中(意味着元素赋值,尽管他上面说的是“深拷贝”)。如果这符合您的需求,它将具有最佳性能。

MyType[] shallowCopy = (MyType[])types.Clone();

他还提到了“深拷贝”,对于那些不是原始类型递归值聚合的可变类型,深拷贝将会有所不同。如果MyType实现了ICloneable,那么这对于深拷贝非常有效:

MyType[] deepCopy = (MyType[])Array.ConvertAll(element => (MyType)element.Clone());

14

如果你急于求成:

newarray = new List<T>(oldarray).ToArray();

1
你为什么要先创建一个列表,然后再调用 .ToArray() 而不是 oldarray.ToArray()?你的方法将在列表构造函数中调用 CopyTo,并在 ToArray 中调用 Copy,我不确定我看到了什么意义。 - CervEd

10

使用受保护的方法MemberwiseClone(执行浅复制)或使用深度克隆技术,在MyType上实现一个克隆方法。您可以让它实现ICloneable,然后编写几个扩展方法来克隆相应的集合。

interface ICloneable<T>
{
    T Clone();
}

public static class Extensions
{
    public static T[] Clone<T>(this T[] array) where T : ICloneable<T>
    {
        var newArray = new T[array.Length];
        for (var i = 0; i < array.Length; i++)
            newArray[i] = array[i].Clone();
        return newArray;
    }
    public static IEnumerable<T> Clone<T>(this IEnumerable<T> items) where T : ICloneable<T>
    {
        foreach (var item in items)
            yield return item.Clone();
    }
}

由于使用Array.Copy时会创建一个新的数组并复制引用而不是引用的对象,因此您需要这样做。每种类型都负责自己的复制。

1
Microsoft表示:我们建议不要在公共API中实现ICloneable,因为它没有指定深度复制语义和浅复制语义。最好创建一个自己的IDeepCopyable接口。 - Wilbert

7
如果您的类型是可序列化的,您可以使用序列化技术来获取数组的副本(包括项目的深层副本):
private static object GetCopy(object input)
{
    using (MemoryStream stream = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, input);
        stream.Position = 0;
        return formatter.Deserialize(stream);
    }
}

使用方法:

MyType[] items = new MyType[2];
// populate the items in the array

MyType[] copies = (MyType[])GetCopy(items);

我的类型不可序列化 =( - JOBG

4

我也想做同样的事情:通过值复制数组,用于像排序这样的操作,以便稍后可以使用原始源数组重新初始化另一个临时数组。经过研究,我发现这并不是那么简单就能实现的。因此,我想出了一个解决方法。下面是我的代码:

string[] planetNames = new string[] { "earth", "venus", "mars" };

string[] tempNames = new string[planetNames.Length];

for (int i = 0; i < planetNames.Length; i++)
{
     tempNames[i] = planetNames[i];
}

planetNames是我的原始数组。 tempNames是我将独立于planetNames进行排序的数组。 我已经测试过了,当我尝试对tempNames进行排序时,这段代码不会对planetNames进行排序,这正是我想要实现的。


3

如果你想创建一个只包含原始数组中对象引用的副本(或者你有一个值类型对象的数组),最简单的解决方案是:

var newArray = oldArray.ToArray()

如果您想进行深层复制,您应该有一个方法来复制您类型的单个对象(例如public MyType Copy(MyType obj))。然后解决方案将如下所示。
var newArray = oldArray.Select(x => Copy(x)).ToArray()

请澄清一下,Copy(x) 是自定义实现的,对吗? - Thor
1
是的,没有一种方法可以对任何类型的对象执行深度复制。在任何对象中都有MemberwiseClone方法,但它是受保护的,并且仅创建浅表副本(对象本身的副本,但不创建引用类型字段的新副本),而不是深度副本。 - renadeen

0

我发现如果你只想要一个简单的字符数组复制,你可以通过使用 char 来欺骗 C# 进行按值复制:

char[] newchararray = new char[desiredchararray.Length];

for (int k = 0; k < desiredchararray.Length; k++)
{

   char thecharacter = newchararray[k];
   newchararray[k] = thecharacter;
   oldchararray[k] = oldchararray[k] + 1;

}

对我来说似乎可以工作,但如果有人持不同意见,请告诉我 :)


2
char是一个值类型。对于引用类型,情况会有所不同。 - Odrade

0

在这种情况下,复制数组的值是数字。

int[] arr = { 1, 2, 3, 4, 5 };
int[] copyArr = new int[arr.Length];
for (int i = 0; i <arr.Length; i++)
{
    copyArr[i]=arr[arr.Length-i-1];
}
Console.WriteLine(string.Join(" ",arr));**

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接