Изменение значения элемента в списке структур

StackOverflow https://stackoverflow.com/questions/51526

  •  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 до выполнения кода или что они могут ожидать. делай с этим потом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top