Изменение значения элемента в списке структур
-
09-06-2019 - |
Вопрос
У меня есть список структур, и я хочу изменить один элемент.Например :
MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");
Теперь я хочу изменить один элемент:
MyList[1].Name = "bob"
Однако всякий раз, когда я пытаюсь это сделать, я получаю следующую ошибку:
Не может изменить возвращаемое значение System.collections.generic.list.this [int] ', потому что это не переменная
Если я использую список классов, проблема не возникает.
Я предполагаю, что ответ связан с тем, что структуры являются типом значения.
Итак, если у меня есть список структур, следует ли мне относиться к ним как к только для чтения?Если мне нужно изменить элементы в списке, мне следует использовать классы, а не структуры?
Решение
MyList[1] = new MyStruct("bob");
Структуры в C# почти всегда должны быть неизменяемыми (то есть не иметь возможности изменить свое внутреннее состояние после создания).
В вашем случае вам нужно заменить всю структуру в указанном индексе массива, а не пытаться изменить только одно свойство или поле.
Другие советы
Не совсем.Проектирование типа как класса или структуры не должно быть обусловлено необходимостью хранить его в коллекциях :) Вам следует обратить внимание на необходимую «семантику».
Проблема, которую вы видите, связана с семантикой типа значения.Каждая переменная/ссылка типа значения представляет собой новый экземпляр.Когда ты говоришь
Struct obItem = MyList[1];
происходит следующее: создается новый экземпляр структуры и все члены копируются один за другим.Итак, у вас есть клон MyList[1], т.е.2 экземпляра.Теперь, если вы измените obItem, это не повлияет на оригинал.
obItem.Name = "Gishu"; // MyList[1].Name still remains "peter"
Теперь потерпите здесь 2 минуты (чтобы сглотнуть, нужно время..Это сделало для меня :) Если вам действительно нужны структуры, чтобы хранить в коллекции и модифицировать, как вы указали в своем вопросе, вам придется заставить свою структуру разоблачить интерфейс (интерфейс (Однако это приведет к боксу).Затем вы можете изменить фактическую структуру с помощью ссылки на интерфейс, которая ссылается на упакованный объект.
Следующий фрагмент кода иллюстрирует то, что я только что сказал выше.
public interface IMyStructModifier
{
String Name { set; }
}
public struct MyStruct : IMyStructModifier ...
List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));
MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
Console.WriteLine(s.Name);
}
IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
Console.WriteLine(s.Name);
}
ХТХ.Хороший вопрос.
Обновлять: @Хат, ты заставил меня побежать проверить, не упустил ли я что-то такое простое.(Было бы противоречиво, если бы свойства установщика этого не делали, а методы — нет - вселенная .Net все еще сбалансирована :)
Метод установки не работает
obList2[1] возвращает копию, состояние которой будет изменено.Исходная структура в списке остается неизменной.Так что установка через интерфейс кажется единственным способом сделать это.
List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
Console.WriteLine(s.Name);
}
Дело не в том, что структуры «неизменяемы».
Реальная основная проблема заключается в том, что структуры являются типом значения, а не ссылочным типом.Поэтому, когда вы извлекаете «ссылку» на структуру из списка, создается новая копия всей структуры.Таким образом, любые изменения, которые вы вносите в него, изменяют копию, а не исходную версию в списке.
Как утверждает Эндрю, вам необходимо заменить всю структуру.Однако в этот момент я думаю, что вам следует спросить себя, почему вы вообще используете структуру (вместо класса).Убедитесь, что вы не делаете этого из-за проблем с преждевременной оптимизацией.
Нет ничего плохого в структурах, которые имеют открытые поля или допускают мутации через установщики свойств.Однако структуры, которые изменяются в ответ на методы или методы получения свойств, опасны, поскольку система позволяет вызывать методы или методы получения свойств для временных экземпляров структуры;если методы или геттеры вносят изменения в структуру, эти изменения в конечном итоге будут отброшены.
К сожалению, как вы заметили, коллекции, встроенные в .net, действительно неспособны раскрывать содержащиеся в них объекты типа значения.Лучше всего обычно сделать что-то вроде:
MyStruct temp = myList[1]; temp.Name = "Albert"; myList[1] = temp;
Несколько раздражает и совсем не потокобезопасен.Все еще улучшение по сравнению со списком типа класса, где для выполнения того же самого может потребоваться:
myList[1].Name = "Albert";
но может также потребоваться:
myList[1] = myList[1].Withname("Albert");
или, может быть
myClass temp = (myClass)myList[1].Clone(); temp.Name = "Albert"; myList[1] = temp;
или может быть какой-то другой вариант.На самом деле это невозможно было бы узнать, если бы вы не исследовали myClass, а также другой код, который помещает элементы в список.Вполне возможно, что вы не сможете узнать, безопасна ли первая форма, без проверки кода в сборках, к которым у вас нет доступа.Напротив, если Name является открытым полем MyStruct, метод, который я дал для его обновления, будет работать независимо от того, что еще содержит MyStruct, или независимо от того, что другие вещи могли сделать с myList до выполнения кода или что они могут ожидать. делай с этим потом.