【问题标题】:Why is my variable of type List<Point> being changed even though I'm not using references?为什么即使我没有使用引用,我的 List<Point> 类型的变量也会被更改?
【发布时间】:2013-12-23 16:29:58
【问题描述】:

我有一个名为temp 的变量,其类型为List&lt;Point&gt;。出于某种原因,当我调用sanitize(temp) 时,变量temp 似乎发生了变化。我知道变量temp 发生了变化,因为如果我包含sanitize(temp) 行,我在C:\out.bmp 上得到的输出是不同的。我从消息框得到的输出是“温度没有改变。”

List<Point> original = temp;
sanitize(temp);

if (temp.Equals(original)) {
    MessageBox.Show(@"temp was not changed.");
} else {
    MessageBox.Show(@"temp was changed.");
}

outputPointsOnBitmap(temp.ToArray(), ref windowBitmap, Color.Yellow);
windowBitmap.Save("C:\\out.bmp");

为了好奇,这里有更多的源代码:

private void outputPointsOnBitmap(Point[] points, ref Bitmap bitmap, Color markerColor) {
    foreach (Point point in points) {
        bitmap.SetPixel(point.X, point.Y, markerColor);
    }
}

private List<Point> sanitize(List<Point> crossPoints) {
    SortedSet<int> indexesToDelete = new SortedSet<int>();

    for (int i = 0; i < crossPoints.Count() - 1; i++) {
        if (Math.Abs(crossPoints[i + 1].X - crossPoints[i].X) <= 5 &&
            Math.Abs(crossPoints[i + 1].Y - crossPoints[i].Y) <= 5) {
            indexesToDelete.Add(i);
            indexesToDelete.Add(i + 1);
        }
    }

    foreach (int i in indexesToDelete.Reverse()) {
        crossPoints.RemoveAt(i);
    }

    return crossPoints;
}

这让我发疯了。

【问题讨论】:

  • 正在使用参考资料。您正在传递 value 是的。但是您传递的值是对您列表的 reference
  • @aquinas: 我怎么知道我是在使用对对象的引用还是对象本身?我想所有列表都只是对对象的引用?如果我有一个Point 类型的变量怎么办。那是对象还是对对象的引用?
  • C# 有引用类型和值类型。您应该阅读以下内容:msdn.microsoft.com/en-us/library/490f96s2.aspx 以获得更好的理解。

标签: c# .net


【解决方案1】:

当你这样做时:

List<Point> original = temp;

您使变量original 引用temp 引用的列表。

因此,sanitizetemp 所做的任何更改在original 中也可见。 temp.Equals(original) 返回 true,因为它们是同一个对象。如果您希望original 成为与temp 具有相同项的新列表,请构造一个新的List 对象:

List<Point> original = new List<Point>(temp);

你说你没有使用引用,但 List&lt;T&gt; 是一个引用类型,因为它不继承自 ValueType - 当你分配或传递它时,你总是在使用引用。

以下是 .Net 中值类型和引用类型之间差异的一个很好的细分: http://www.albahari.com/valuevsreftypes.aspx

正如 Jordy Langen 指出的那样,temp.Equals(original) 现在将始终返回 false,因为这两个列表是不同的对象(这就是 Object.Equals 查找引用类型的对象)。如果你想查看sanitize函数是否改变了列表,你可以使用Enumerable.SequenceEqual

if (temp.SequenceEqual(original)) {
    MessageBox.Show(@"temp was not changed.");
} else {
    MessageBox.Show(@"temp was changed.");
}

【讨论】:

  • Equals 方法现在总是返回 false。
  • 一切都继承自对象,而不仅仅是引用类型。
  • 是的。我将其更改为 从 ValueType 继承。
【解决方案2】:

如果您希望original 保持不变,请执行以下操作:

List<Point> original = new List<Point>(temp);
sanitize(temp);

这将使用temp 的内容创建一个新的List&lt;Point&gt;。否则,List&lt;Point&gt; original = temp 只会创建一个新引用(原始),指向包含您的列表(临时)的同一内存位置。

为了检查您的 2 个列表在您的情况下是否不同,请检查它们的计数(因为 Equals 将始终返回 false)。

if( temp.Count != original.Count ) 
{
    // now you know that some items were removed in your sanitize method
}

【讨论】:

    【解决方案3】:

    temp 更改是因为您将对它的引用传递给分配给crossPoints 变量的sanitize。因为它是通过引用传递的,所以对crossPoints 的任何更改(通过对RemoveAt 的调用)也会影响temp,因为它们指向同一个列表。

    当你说:

    List<Point> original = temp;
    

    您正在使 temporiginal 引用同一个列表。它不会创建列表的副本。

    【讨论】:

      【解决方案4】:

      Temp 和 original 不是对象,它们是对对象的引用,在您的情况下,它们都引用同一个列表对象。通常,equals 对对象进行引用比较,所以它会说它们是相等的。无论哪种方式,您都在将列表对象与自身进行比较。在您的 sanitize 方法中,您更改了相同的列表。

      【讨论】:

        【解决方案5】:

        它正在被修改,因为它是作为引用传递的,所以删除操作实际上是在临时变量持有的同一个引用上执行的。如果您执行以下操作,您可能会得到想要的结果:

        private List<Point> sanitize(List<Point> crossPoints) {
            var workset = new List<Point>(crosspoints);
            SortedSet<int> indexesToDelete = new SortedSet<int>();
        
            for (int i = 0; i < workset.Count() - 1; i++) {
                if (Math.Abs(workset[i + 1].X - workset[i].X) <= 5 &&
                    Math.Abs(workset[i + 1].Y - workset[i].Y) <= 5) {
                    indexesToDelete.Add(i);
                    indexesToDelete.Add(i + 1);
                }
            }
        
            foreach (int i in indexesToDelete.Reverse()) {
                workset.RemoveAt(i);
            }
        
            return workset;
        }
        

        当然请记住,这是一个 O(n) 操作,因为您正在复制列表中的引用

        【讨论】:

          【解决方案6】:

          此声明:

          List<Point> original = temp;
          

          original 现在确实与temp 具有相同 引用。含义:您应用于temp 变量的所有更改也将应用于original 变量,因为它们共享相同的引用

          您应该创建一个列表的deep copy 来完成这项工作。还有一件事:

          如果您使用深层副本,Equals 将始终返回 false。 Equals 检查引用是否相等。您很可能需要根据列表中的项目进行检查。

          【讨论】:

            【解决方案7】:

            您的列表temp 是一个对象,并且对象是通过引用传递的,因此您的 sanitize 方法正在更改底层对象。

            但是,您的输出显示它没有更改,因为您分配了 original = temp,这意味着您的变量 original 是指向与变量 temp 相同的对象的指针,因此当您比较两者时,它们是相等的,甚至尽管该对象在被传递给清理之前实际上已经发生了变化。

            【讨论】:

              猜你喜欢
              • 2023-01-08
              • 1970-01-01
              • 2020-01-03
              • 2023-03-20
              • 2011-07-07
              • 2017-08-19
              • 2021-03-11
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多