【问题标题】:Add fake data to a List [duplicate]将假数据添加到列表 [重复]
【发布时间】:2020-05-23 10:12:18
【问题描述】:

所以我尝试创建一个虚假的项目列表

var items = new List<Item>();

var item = new Item 
{
    Name = "A";
} 

for(int i=0; i<3; i++)
{
    items.Add(item);
}

item.Name = "B";

for(int i = 0; i<2; i++)
{
    items.Add(item);
}

我希望它会返回一个包含 3 个项目名称 A 和 2 个项目名称 B 的列表,但它返回 5 个项目名称 B。我错在哪里?

【问题讨论】:

  • 你每次都引用同一个项目
  • 您不会创建新的Item,而是将您创建的单个ItemName 设置为"B"。您添加到列表中的所有内容都是对该单个 Item 的引用。
  • 问题是,将Item 添加到列表时,它没有被复制。相反,对Item 的引用被添加到列表中,这意味着您的变量item 和您的列表入口指向同一个对象,这意味着如果您更改其中一个对象,另一个也会更改。您想要的是单独的项目,因此您需要在每个循环中创建一个新的 Item
  • 你应该这样做item = new Item(); item.Name = "B";
  • @ChetanRanpariya 这不是问题,另外,对象初始化语法优于赋值,Visual Studio 本身甚至向您展示了使用对象初始化的提示 (IDE0017)。加上这个:stackoverflow.com/a/12842511/9363973

标签: c#


【解决方案1】:

你的代码中的问题是

  1. 您创建了一个item 对象,其中Name 属性为“A”
  2. 您将同一个对象插入到集合的三个空间中,但都指向同一个对象
  3. 您将Name 属性更改为“B”。
  4. 您将同一个对象插入到集合的两个新空间中
  5. 由于变量 item 和集合的所有 5 个空格都指向同一个对象,因此对任何这些引用的任何更改都会更改该对象并反映在所有引用中

更多详情谷歌reference typed variables

var items = new List<Item>();



for(int i=0; i<3; i++)
{
    var item = new Item() 
    {
        Name = "A";
    } 
    items.Add(item);
}



for(int i = 0; i<2; i++)
{     
    var item = new Item() 
    {
        Name = "B";
    } 
    items.Add(item);
}

【讨论】:

  • 您可以在此处删除()
  • @aloisdgmovingtocodidact.com 是的,没必要
  • 请注意,当您在循环中声明变量时,每个声明都会产生一个新对象。因此,List&lt;Item&gt; 中的所有 Item 值与原始代码中的值无关。
  • @Kami - 但通常是首选结果
  • 我将在这里扮演魔鬼代言人的角色,但从技术上讲,第 1 点和第 2 点应该合并到同一点。因为 OP 使用对象初始化语法,所以只有在完成每个属性的初始化后才返回对象。所以var item = new Item();(带有一个空的构造函数)不会初始化 Name 字符串,这只会在item.Name = "A"; 完成之后发生。所以从技术上讲,对象没有被初始化,然后名称被更改,但名称将在同一步骤中被初始化。来源:stackoverflow.com/a/12842511/9363973
【解决方案2】:

在 C# 和大多数现代语言中,对象被视为引用类型。这意味着内存中只有一个实际对象的副本,并且该变量的每个实例只是指向该副本的引用。

因此,当对象的引用被修改时,引用它的所有实例都将获得新值。代码

var item = new Item() 
{
    Name = "A";
} 

item.Name = "B";

正在修改同一个对象。因此,列表中的所有值都具有名称 A,然后您将其更改为 B 并将其的 2 个副本分配给数组 - 总共 5 个。

如果您希望拥有 3 个 A 和 2 个 B,那么您需要声明一个新对象并分配它。请尝试以下操作:

var items = new List<Item>();

var itemA = new Item() 
{
    Name = "A";
} 

for(int i=0; i<3; i++)
{
    items.Add(itemA);
}
// item list now has 3 A
var itemB = new Item() 
{
    Name = "B";
} 

for(int i = 0; i<2; i++)
{
    items.Add(itemB);
}
// itme list now has 3 A and 2 B

进一步阅读:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/objects 并与引用类型对比 - 结构或值类型 - https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/structs

【讨论】:

  • 很好的答案,但是,不是每次迭代都创建一个新对象意味着该列表不包含名称为“A”的 3 个项目,而是对名称为“A”的同一项目的 3 个引用"
【解决方案3】:

这里的问题是您正在向列表中添加对您创建的对象 (item) 的引用,而不是不同的对象。因此,您列表中的 5 项和您的 item 变量都是同一个对象,这意味着,如果您更改一项,那么您也会更改对它的所有其他引用。

为了清楚起见,试试这个:

var item = new Item
{
    Name "Hello "
};

var secondItem = item;
item.Name = "World";

// This will print 'WorldWorld'
Console.WriteLine(item.Name + secondItem.Name);

您的列表项正在发生理智的原则,只是您的第二个(以及第三个和第四个...)项目不是单独的变量secondItem,而是不在变量中,而是由列表保存。

为了解决您的问题,您需要在循环的每次迭代中创建一个新对象。像这样:

var items = new List<Item>();
Item item;

for (int i = 0; i < 3; i++)
{
    item = new Item
    {
        Name = "A"
    };

    items.Add(item);
}

for (in i = 0; i < 2; i++)
{
    item = new Item
    {
        Name = "B";
    };

    items.Add(item);
}

这段代码可以减少到几行(具体来说是 6 行),但我把它留了这么长,因为这样更容易阅读,更重要的是更容易理解

【讨论】:

    【解决方案4】:
    var item = new Item() 
    {
        Name = "A";
    }  
    

    创建一个对象。对象具有引用语义,即如果您更改名称,所有引用都将看到相同的名称。 您必须将新对象添加到列表中,例如在循环中创建一个新项目。

    【讨论】:

    • 您可以在此处删除()
    【解决方案5】:

    列表中存储了对变量item 的引用。更改item.Name 会更改名称,因此当您查看列表中的项目时,所有name 都将设置为“B”,因为存在对项目对象的引用,其名称现在为“B”。如果您只想将最后两个项目命名为“B”,而不是

    item.Name = "B";
    

    使用这个:

    var item = new Item{
        Name = "B"
    };
    

    【讨论】:

    • 但是所有的 A 仍然指向同一个项目,所有 B 也一样
    • 是的,但只要你只读名字,没关系,我知道
    猜你喜欢
    • 2018-08-20
    • 1970-01-01
    • 2021-06-26
    • 2015-12-04
    • 2018-10-14
    • 1970-01-01
    • 2017-07-25
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多