【问题标题】:Why does adding a new value to list<> overwrite previous values in the list<>为什么向 list<> 添加新值会覆盖 list<> 中的先前值
【发布时间】:2010-01-28 17:21:44
【问题描述】:

我实际上是在尝试将多个项目添加到列表中,但最后所有项目的值都与最后一个项目相同。

public class Tag
{
    public string TagName { get; set; }
}

List<Tag> tags = new List<Tag>();
Tag _tag = new Tag();
string[] tagList = new[]{"Foo", "Bar"};

foreach (string t in tagList)
{
    _tag.tagName = t; // set all properties
    //Add class to collection, this is where all previously added rows are overwritten
    tags.Add(_tag);
}

上面的代码生成了两个项目的列表,其中TagName 设置为“Bar”,而我希望一个用于"Foo",一个用于"Bar"为什么所有项目在结果列表中都具有相同的属性?

解释为什么将 public class Tag 更改为 public struct Tag 会使此代码按预期工作(不同的项目具有不同的值)。


如果这很重要,我的实际目标是创建派生集合类,但由于问题只是列出它可能是可选的,因此仍然显示我的目标如下。

按照一些教程,我能够成功地创建一个集合类,它继承了创建 DataTable 所需的功能,该 DataTable 可以作为表值参数传递给 Sql Server 的存储过程。一切似乎都运行良好;我可以添加所有行,它看起来很漂亮。但是,经过仔细检查,我注意到当我添加新行时,所有先前行的数据都被新行的值覆盖。因此,如果我有一个字符串值为“foo”的行,并且我添加了一个值为“bar”的第二行,则将插入第二行(创建一个包含两行的 DataTable),但两行的值都为“bar” ”。谁能明白为什么会这样?下面是一些代码,它们可以工作,但经过了一些简化(为了便于解释,Tag 类已被简化)。

以下是 Collection 类的:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using Microsoft.SqlServer.Server;

namespace TagTableBuilder
{
public class TagCollection : List<Tag>, IEnumerable<SqlDataRecord>
{
    IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
    {
        var sdr = new SqlDataRecord(
            new SqlMetaData("Tag", SqlDbType.NVarChar)
            );

        foreach (Tag t in this)
        {
            sdr.SetSqlString(0, t.tagName);

            yield return sdr;
        }
    }
}

public class Tag
{
    public string tagName { get; set; }
}
}

这些调用如下:

//Create instance of collection
TagCollection tags = new TagCollection();

//Create instance of object
Tag _tag = new Tag();

foreach (string t in tagList)
{
    //Add value to class propety 
    _tag.tagName = t;
    //Add class to collection, this is where all previously added rows are overwritten
    tags.Add(_tag);
}

【问题讨论】:

    标签: c#


    【解决方案1】:

    您在循环内使用Tag 对象的相同实例,因此对TagName 的每次更新都指向相同的引用。移动循环内的声明以在循环的每次传递中获取一个新对象:

    foreach (string t in tagList)
    {
        Tag _tag = new Tag(); // create new instance for every iteration
    
        _tag.tagName = t;
        tags.Add(_tag);
    }
    

    对于奖励部分 - 当您将 Tagclass 更改为 struct 复制操作(当您调用 tags.Add(_tag) 时发生)复制整个实例(本质上是创建新实例),这与原始 class 情况不同仅对同一个实例的引用被复制到调用的参数中,然后复制到列表的元素中(有关 struct 如何传递给方法调用的说明,请参阅 C# pass by value vs. pass by reference)。

    【讨论】:

    • 我已经稍微扩展了问题(添加了struct 奖金部分),所以我已经用它更新了你的答案。如果您觉得答案的变化太大,请告诉我,我会创建新的答案。否则将此评论标记为“不再需要”。
    【解决方案2】:

    在将标签添加到集合的循环中,您使用的是相同的 Tag 对象实例。本质上,您将标签的名称设置为 tagList 中的第一个值并将其添加到集合中,然后您将同一标签的名称更改为 tagList 中的第二个值并将其再次添加到集合中。

    您的标签集合包含对同一标签对象的多个引用!每次在设置 Tag 的名称并将其添加到集合之前,在 for 循环中实例化 _tag。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-29
      • 1970-01-01
      • 2019-01-08
      • 2016-11-14
      • 2023-04-01
      • 2019-07-02
      • 2022-01-02
      相关资源
      最近更新 更多