Почему добавление нового значения в список<> перезаписывает предыдущие значения в списке <>

По сути, я пытаюсь добавить несколько элементов в список, но в конце все элементы имеют одинаковое значение, равное последнему элементу.

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, который можно передать в хранимую процедуру Sql Server в качестве параметра табличного значения. Кажется, все работает хорошо; Я могу добавить все строки, и это выглядит красиво. Однако при ближайшем рассмотрении я замечаю, что при добавлении новой строки данные для всех предыдущих строк перезаписываются значением для новой строки. Поэтому, если у меня есть строка со строковым значением "foo", и я добавляю вторую строку со значением "bar", будет вставлена ​​вторая строка (создание DataTable с двумя строками), но обе строки будут иметь значение "bar" ". Кто-нибудь может понять, почему это будет? Вот часть кода, который работает, но был немного упрощен (класс Tag был уменьшен для простоты объяснения).

Ниже приведены классы коллекции:

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 ответа

Решение

Вы используете тот же экземпляр Tag объект внутри цикла, поэтому каждое обновление TagName по той же ссылке. Переместите объявление внутри цикла, чтобы получить новый объект на каждом проходе цикла:

foreach (string t in tagList)
{
    Tag _tag = new Tag(); // create new instance for every iteration

    _tag.tagName = t;
    tags.Add(_tag);
}

Для бонусной части - когда вы меняете Tag от class в struct операция копирования (это происходит при вызове tags.Add(_tag)) копирует весь экземпляр (по сути, создает новый) в отличие от оригинала class случай, когда в параметр вызова копируется только ссылка на один и тот же экземпляр, а затем на элемент списка (см. C# передача по значению или передача по ссылке для объяснения того, как struct перешел на вызовы метода).

В цикле, где вы добавляете теги в коллекцию, вы используете тот же экземпляр объекта Tag. По сути, вы устанавливаете имя тега на первое значение в tagList и добавляете его в коллекцию, затем изменяете имя того же тега на второе значение в tagList и снова добавляете его в коллекцию.

Ваша коллекция тегов содержит несколько ссылок на один и тот же объект Tag! Создавайте _tag внутри цикла for каждый раз перед установкой имени тега и добавлением его в коллекцию.

Другие вопросы по тегам