Почему добавление нового значения в список<> перезаписывает предыдущие значения в списке <>
По сути, я пытаюсь добавить несколько элементов в список, но в конце все элементы имеют одинаковое значение, равное последнему элементу.
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 каждый раз перед установкой имени тега и добавлением его в коллекцию.