为什么要向列表添加新值<>覆盖列表中的先前值<>

时间:2010-01-28 17:21:45

标签: c#

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

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设置为“条形码”,当我期望"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);
}

2 个答案:

答案 0 :(得分:32)

您在循环中使用了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呼叫)。

答案 1 :(得分:3)

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

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