Os métodos que modificam os parâmetros do tipo de referência são ruins?

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

  •  05-07-2019
  •  | 
  •  

Pergunta

Eu já vi métodos como este:

public void Foo(List<string> list)
{
    list.Add("Bar");
}

Esta é uma boa prática para modificar parâmetros em um método?

Isso não seria melhor?

public List<string> Foo(List<string> list)
{
    // Edit
    List<string> newlist = new List<string>(list);
    newlist.Add("Bar");
    return newlist;
}

Parece que o primeiro exemplo tem efeitos colaterais inesperados.

Foi útil?

Solução

No exemplo que você deu, o primeiro me parece muito melhor do que o segundo. Se eu visse um método que aceitasse uma lista e também devolvesse uma lista, minha primeira suposição seria que ela estava retornando uma nova lista e não tocando a que recebeu. O segundo método, portanto, é o com efeitos colaterais inesperados.

Desde que seus métodos sejam nomeados adequadamente, haja pouco perigo na modificação do parâmetro. Considere isto:

public void Fill<T>(IList<T> list)
{
    // add a bunch of items to list
}

Com um nome como "preenchimento", você pode ter certeza de que o método modificará a lista.

Outras dicas

Francamente, neste caso, ambos os métodos fazem mais ou menos a mesma coisa.Ambos modificarão o List que foi passado.

Se o objetivo é ter listas imutáveis ​​por tal método, o segundo exemplo deve fazer uma cópia do List que foi enviado e, em seguida, execute o Add operação no novo List e depois devolva isso.

Não estou familiarizado com C # nem .NET, então meu palpite seria algo como:

public List<string> Foo(List<string> list)
{
    List<string> newList = (List<string>)list.Clone();
    newList.Add("Bar");
    return newList;
}

Dessa forma, o método que chama o Foo método obterá o recém-criado List devolvido e o original List que foi passado não seria tocado.

Isso realmente depende do "contrato" de suas especificações ou API, portanto, nos casos em que Lists podem apenas ser modificados, não vejo problema em seguir com a primeira abordagem.

Você está fazendo exatamente a mesma coisa nos dois métodos, apenas um deles está retornando a mesma lista.

Realmente depende do que você está fazendo, na minha opinião. Apenas certifique -se de que sua documentação esteja clara sobre o que está acontecendo. Escreva pré-condições e pós-condições, se você gosta desse tipo de coisa.

Na verdade, não é tão inesperado que um método que leva uma lista como o parâmetro modifique a lista. Se você deseja um método que apenas lê na lista, você usaria uma interface que apenas permite a leitura:

public int GetLongest(IEnumerable<string> list) {
    int len = 0;
    foreach (string s in list) {
        len = Math.Max(len, s.Length);
    }
    return len;
}

Usando uma interface como essa, você não proíbe apenas o método de alterar a lista, ela também fica mais flexível, pois pode usar qualquer coleção que implemente a interface, como uma matriz de string, por exemplo.

Algumas outras línguas têm um const Palavra -chave que pode ser aplicada aos parâmetros para proibir um método de alterá -los. Como .NET tem interfaces que você pode usar para isso e strings que são imutáveis, não há realmente uma necessidade de const parâmetros.

O advento dos métodos de extensão tornou um pouco mais fácil lidar com métodos que introduzem efeitos colaterais. Por exemplo, no seu exemplo, fica muito mais intuitivo dizer

public static class Extensions
{
  public static void AddBar(this List<string> list)
  {
     list.Add("Bar");
  }
}

e chame com

mylist.AddBar();

O que deixa mais claro que algo está acontecendo com a lista.

Como mencionado nos comentários, isso é mais útil em listas, pois as modificações em uma lista podem tender a ser mais confusas. Em um objeto simples, eu tenderia a modificar o objeto no lugar.

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