Pergunta

Tenho uma lista de estruturas e quero alterar um elemento.Por exemplo :

MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");

Agora quero alterar um elemento:

MyList[1].Name = "bob"

No entanto, sempre que tento fazer isso, recebo o seguinte erro:

Não é possível modificar o valor de retorno de System.Collection.Genic.List.This [int] 'porque não é uma variável

Se eu usar uma lista de classes, o problema não ocorre.

Acho que a resposta tem a ver com o fato de as estruturas serem um tipo de valor.

Então, se eu tiver uma lista de estruturas, devo tratá-las como somente leitura?Se eu precisar alterar os elementos de uma lista, devo usar classes e não estruturas?

Foi útil?

Solução

MyList[1] = new MyStruct("bob");

estruturas em C# quase sempre devem ser projetadas para serem imutáveis ​​(ou seja, não têm como alterar seu estado interno depois de criadas).

No seu caso, o que você deseja fazer é substituir toda a estrutura no índice da matriz especificada, e não tentar alterar apenas uma única propriedade ou campo.

Outras dicas

Não exatamente.Projetar um tipo como classe ou estrutura não deve ser motivado pela necessidade de armazená-lo em coleções :) Você deve observar a 'semântica' necessária

O problema que você está vendo é devido à semântica do tipo de valor.Cada variável/referência de tipo de valor é uma nova instância.Quando voce diz

Struct obItem = MyList[1];

o que acontece é que uma nova instância da struct é criada e todos os membros são copiados um por um.Para que você tenha um clone de MyList[1], ou seja,2 instâncias.Agora, se você modificar obItem, isso não afetará o original.

obItem.Name = "Gishu";  // MyList[1].Name still remains "peter"

Agora tenha paciência comigo por 2 minutos aqui (isso demora um pouco para engolir.Isso aconteceu para mim :) Se você realmente precisa de estruturas para serem armazenadas em uma coleção e modificadas como você indicou em sua pergunta, terá que fazer com que sua estrutura exponha uma interface (No entanto, isso resultará em boxe).Você pode então modificar a estrutura real por meio de uma referência de interface, que se refere ao objeto em caixa.

O trecho de código a seguir ilustra o que acabei de dizer acima

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);
}

HTH.Boa pergunta.
Atualizar: @Hath - você me fez correr para verificar se esqueci algo tão simples.(Seria inconsistente se as propriedades do setter e os métodos não o fizessem - o universo .Net ainda está equilibrado :)
O método setter não funciona
obList2[1] retorna uma cópia cujo estado seria modificado.A estrutura original na lista permanece inalterada.Portanto, Set-via-Interface parece ser a única maneira de fazer isso.

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);
}

Não é que as estruturas sejam "imutáveis".

O verdadeiro problema subjacente é que as estruturas são do tipo Value, não do tipo Reference.Portanto, quando você retira uma "referência" à estrutura da lista, está criando uma nova cópia de toda a estrutura.Portanto, qualquer alteração feita nele altera a cópia, não a versão original da lista.

Como Andrew afirma, você precisa substituir toda a estrutura.Nesse ponto, acho que você deve se perguntar por que está usando uma estrutura em primeiro lugar (em vez de uma classe).Certifique-se de não fazer isso devido a preocupações prematuras de otimização.

Não há nada de errado com estruturas que possuem campos expostos ou que permitem mutação por meio de configuradores de propriedades.Estruturas que sofrem mutação em resposta a métodos ou getters de propriedades, entretanto, são perigosas porque o sistema permitirá que métodos ou getters de propriedades sejam chamados em instâncias de struct temporárias;se os métodos ou getters fizerem alterações na estrutura, essas alterações acabarão sendo descartadas.

Infelizmente, como você observou, as coleções incorporadas ao .net são realmente fracas na exposição de objetos de tipo de valor contidos nelas.Sua melhor aposta geralmente é fazer algo como:

  MyStruct temp = myList[1];
  temp.Name = "Albert";
  myList[1] = temp;

Um tanto irritante e nada seguro.Ainda é uma melhoria em relação a uma lista de um tipo de classe, onde fazer a mesma coisa pode exigir:

  myList[1].Name = "Albert";

mas também pode exigir:

  myList[1] = myList[1].Withname("Albert");

ou talvez

  myClass temp = (myClass)myList[1].Clone();
  temp.Name = "Albert";
  myList[1] = temp;

ou talvez alguma outra variação.Realmente não seria possível saber, a menos que examinasse myClass e também o outro código que coloca as coisas na lista.É perfeitamente possível que alguém não consiga saber se o primeiro formulário é seguro sem examinar o código em assemblies aos quais não se tem acesso.Por outro lado, se Name for um campo exposto de MyStruct, o método que forneci para atualizá-lo funcionará, independentemente do que mais MyStruct contém, ou independentemente do que outras coisas possam ter feito com myList antes da execução do código ou do que eles podem esperar. faça isso depois.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top