【问题标题】:How to clone multidimensional array without reference correctly?如何正确克隆没有引用的多维数组?
【发布时间】:2015-01-28 06:41:44
【问题描述】:

我的程序中有 3 维数组。我想调试其中的值,然后将此数组放入另一个数组中,我想在其中找到差异。

我试过这个:

Weights weights = new Weights(); // init inside
List<Weights> weightsDebug = new List<Weigts>();
weightsDebug.Add(weights); // I'd put first weigths into the list 
for(int i = 0; i < 100; i++) {
    // do some bad things with weights;
    weightsDebug.Add(weights);
}

在那之后,我在 weigthsDebug 的所有 99 个元素中得到了相同的权重值。我尝试调试并意识到,weigts 数组正在更改。我知道问题出在引用中(通过链接复制,而不是通过值复制)——所有推送到 weightsDebug 的数组元素都与主循环中的 weights 链接。

我用谷歌搜索了一下,发现了一些如何复制一维数组的方法。接下来我尝试了:

我在 Weights 类中添加了 Clone 方法:

double[][][] values;
public Weights Clone() {
    Weights clone = new Weights();
    clone.values = (double[][][]) this.values.Clone();
    return clone;
}

public Weights()
{
    Console.WriteLine("Constructor WITHOUT arguments fired");
}

现在我在复制权重时这样做:

Weights weights = new Weights(); // init inside
List<Weights> weightsDebug = new List<Weigts>();
weightsDebug.Add(weights); // I'd put first weigths into the list 
for(int i = 0; i < 100; i++) {
    // do some bad things with weights;
    Weights weightsClone = weights.Clone();
    weightsDebug.Add(weightsClone);
}

我仍然在整个调试数组中获得最后更改的值。我该如何纠正它?

【问题讨论】:

    标签: c# arrays multidimensional-array clone deep-copy


    【解决方案1】:

    您真正需要的是深度克隆,而不是您正在实施的当前浅层克隆。

    很久以前,我们意识到通过创建 Serializable 类,我们可以使用 .Net serialization 类快速、轻松、正确地对任何对象进行深度克隆。

    这是我们用于这种深度克隆的一种方法(还有一个使用 DataContractSerializer 的 SilverLight 变体):

        /// <summary>
        /// This method clones all of the items and serializable properties of the current collection by 
        /// serializing the current object to memory, then deserializing it as a new object. This will 
        /// ensure that all references are cleaned up.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public T CreateSerializedCopy<T>(T oRecordToCopy)
        {
            // Exceptions are handled by the caller
    
            if (oRecordToCopy == null)
            {
                return default(T);
            }
    
            if (!oRecordToCopy.GetType().IsSerializable)
            {
                throw new ArgumentException(oRecordToCopy.GetType().ToString() + " is not serializable");
            }
    
            var oFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    
            using (var oStream = new MemoryStream())
            {
                oFormatter.Serialize(oStream, oRecordToCopy);
                oStream.Position = 0;
                return (T)oFormatter.Deserialize(oStream);
            }
        }
    

    要使用它,首先确保使用 Serializable 属性标记 Weights 类:

    [Serializable]
    public class Weights
    

    然后你可以根据需要克隆:

            var weights = new List<Weights>();
            weights.Add(new Weights());
    
            var debugWeights = CreateSerializedCopy<List<Weights>>(weights);
    
            if (debugWeights[0] == weights[0])
            {
                System.Diagnostics.Debug.WriteLine("Did not serialize correctly");
            }
    

    【讨论】:

    • 坦率地说,使用像序列化这样的重量级功能来解决像简单地复制一些数组这样的轻量级需求完全是矫枉过正。对于其他场景来说,这是一个很好的通用解决方案,但在这里只是浪费CPU和内存资源。
    • @PeterDuniho:问题是这样的想法经常让开发人员陷入困境并在未来产生严重的实现问题。例如,当另一个开发人员在路上向该类添加另一个属性但没有意识到它不是克隆时会发生什么?我已经看到这种情况发生了很多次,并给客户带来了实际的生产问题,因此使用的任何额外 CPU 都非常值得该解决方案提供的稳定性。如果 CPU 使用率真的很重要,那么首先可能不应该使用克隆。
    【解决方案2】:

    这里的问题是您没有真正的多维数组,其中只有一个对象。您有一个“锯齿状”多维数组,其中对象实际上是数组数组的数组。这意味着要克隆对象,您需要做更多的工作:

    clone.values = new double[values.Length][][];
    
    for (int i = 0; i < values.Length; i++)
    {
        clone.values[i] = new double[values[i].Length][];
    
        for (int j = 0; j < values[i].Length; j++)
        {
            clone.values[i][j] = (double[])values[i][j].Clone();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-05-11
      • 2018-02-11
      • 2013-04-17
      • 1970-01-01
      • 2021-10-23
      • 2021-10-06
      • 2011-01-18
      相关资源
      最近更新 更多