c#以奇怪的方式传递列表作为参数

本文关键字:列表 参数 方式传 | 更新日期: 2023-09-27 18:01:44

所以我遇到了这个bug, c#的行为就像我通过引用传递列表而不是通过值,就像它通常做的那样。我来举个例子:

using System;
using System.Collections.Generic;
namespace testprogram
{
    class Program
    {
        static int x;
        static List<Coordinate> coordinates;
        static void Main(string[] args)
        {
            x = 10;
            coordinates = new List<Coordinate>();
            coordinates.Add(new Coordinate(0, 0));
            coordinates.Add(new Coordinate(1, 1));
            testfunction(x, coordinates);
            Console.WriteLine(x);
            foreach (var objekt in coordinates)
            {
                Console.WriteLine(objekt.xpos);
                Console.WriteLine(objekt.ypos);
            }
            Console.Read();
        }
        static void testfunction(int test, List<Coordinate> objects)
        {
            test = 4;
            foreach (Coordinate obj in objects)
            {
                obj.xpos = 4;
                obj.ypos = 4;
            }
        }

    }
    class Coordinate
    {
        public int xpos;
        public int ypos;
        public Coordinate(int new_x, int new_y)
        {
            xpos = new_x;
            ypos = new_y;
        }
    }
}

这段代码输出:

10 
4
4
4
4

但是为什么呢?我期望它是:

10
0
0
1
1

我试图在函数中创建一个额外的列表,并将参数的值分配给它,但即使这样也不起作用。有什么解决办法吗?

c#以奇怪的方式传递列表作为参数

List<T>参数是一个引用类型,通过值传递。

如果您在方法中修改了列表中的任何项,当您离开该方法时,这些项将保持不变。

但是,如果在方法内部重新分配引用,这些更改将在方法外部丢失。如果想通过引用传递参数,可以使用ref关键字:
static void testfunction(int test, ref List<Coordinate> objects)
{
    // This will update objects outside the testfunction method
    objects = new List<Coordinate>();
}

也可以使用"out"关键字,其作用与"ref"关键字类似。唯一的区别是,您不必在调用方法之前初始化作为out参数传入的值,它们必须在离开方法之前初始化

static void Main(string[] args)
{
    // No need to initialise variable passed as "out" parameter
    List<Coordinate> objects;
    testfunction(test, out objects);
}
static void testfunction(int test, out List<Coordinate> objects)
{
    // Removing this line would result in a compilation error
    objects = new List<Coordinate>();
}

你的期望是错误的。列表作为值传递。这意味着名为objects的变量是原始列表变量的值拷贝。但是列表包含引用,我的意思是列表的内容是对Coordinate对象的引用。因此,如果您尝试更改列表变量,如objects = new List<>(),它不会更改原始列表。但是,如果您更改列表中的对象,则更改实际上应用于原始列表。

objects[0] = new Coordinate(5, 5);
objects[0].xpos = 6;

这两个例子都改变了原来的列表。

如果你想有可能安全地改变列表中的任何东西,你必须对它进行深度克隆。您可以使用Clone()方法,但它可能比较棘手,因为您必须手动克隆列表中的每个对象。这里已经有了答案:如何创建List的新深度副本(克隆)?

您传递了对"按值"列表的引用。所以,如果你改变"testfunction"中的引用"object",那么"coordinates"将不会改变(作为指针)。但是改变"object"的元素会影响"coordinates"的元素