C#奇怪的列表行为

3

这可能是一个初学者的问题,但我似乎找不到任何关于这种行为的信息。

这是代码:

class Cars
{
    public int year = 0;
    public string make = " ";
    public string model = " ";

    public Cars()
    {
    }

    public Cars(int tYear, string tMake, string tModel)
    {
        year = tYear;
        make = tMake;
        model = tModel;
    }

    public string ConvertToString()
    {
        return year.ToString() + make + model;
    }
}

class Program
{
    Cars oneCar = new Cars();
    List<Cars> manyCars = new List<Cars>();

    public void Init()
    {
        manyCars.Add(new Cars(1993, "Nissan", "240SX"));
        manyCars.Add(new Cars(2001, "Isuzu", "Rodeo"));
        manyCars.Add(new Cars(2010, "Ford", "F150"));

        oneCar = manyCars[2];
        oneCar.model = "Pinto";

        for (int i = 0; i < manyCars.Count(); i++)
            Console.WriteLine(manyCars[i].ConvertToString());

        Console.WriteLine(oneCar.ConvertToString());
    }

    static void Main(string[] args)
    {
        Program prg1 = new Program();
        prg1.Init();
    }
}

输出结果如下:

1993年尼桑240SX

2001年五十铃罗迪欧

2010年福特品脱

2010年福特品脱

为什么第三行显示的是Pinto而不是F150?它似乎将oneCar变量设置为指针或其他内容。如何避免这种情况?我以为这就是“new”关键字的作用。
非常感谢您的帮助。

4
请阅读网址 http://pobox.com/~skeet/csharp/references.html。 - Jon Skeet
1
您需要克隆manyCars[2],然后对克隆的对象进行更改。 - d89761
3个回答

5
因为 Cars 是一个对象,而在C#中,对象是引用类型
当你调用这行代码时:
oneCar = manyCars[2];

你将oneCar指向了manyCars[2]的内存地址。它们引用同一个对象。更新oneCar也会更新manyCars[2]这个链接有一篇关于引用类型和值类型的好文章。

非常感谢,您的回答完全解决了我的问题。我之前不知道引用类型和值类型是如何处理的。 - Mike Jones

3

manyCars[2]是福特汽车。所以在赋值后,oneCar也是同样的福特汽车。但请记住类类型是引用类型。这意味着oneCarmanyCars[2]指向同一辆汽车对象!因此,将oneCar.model = "Pinto"设置为与将manyCars[2].model = "Pinto"设置相同。

oneCar = manyCars[2];并不会创建对象内容的副本。您可以在汽车类中添加一个Clone方法。

public Cars Clone()
{
    return (Cars)MemberwiseClone(); // This method is inherited from object.
}

那么这真正创建了一个副本。
oneCar = manyCars[2].Clone();

现在,oneCarmanyCars[2]是两个不同的对象,改变oneCar的型号不会影响manyCars[2]的型号。


2
在c#中,有值类型和引用类型——类是一种引用类型。您上面的代码向ManyCars列表添加了三个新的汽车对象,然后将OneCar变量设置为第3个列表元素的引用,在您的情况下是F150。然后,您覆盖了model属性,改为Pinto,然后遍历了列表。
实际上,您已经将OneCar设置为指向原始Car对象的指针。如果您想制作一个对象的副本,以便可以在不影响列表中的原始对象的情况下进行更改,则需要克隆Car对象,而不仅仅是将其分配给OneCar变量。
谢谢 西蒙

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