¿Son malos los métodos que modifican los parámetros del tipo de referencia?

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

  •  05-07-2019
  •  | 
  •  

Pregunta

He visto métodos como este:

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

¿Es esta una buena práctica modificar parámetros en un método?

¿No sería esto mejor?

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

Parece que el primer ejemplo tiene efectos secundarios inesperados.

¿Fue útil?

Solución

En el ejemplo que has dado, el primero me parece mucho más agradable que el segundo. Si veía un método que aceptaba una lista y también devolvía una lista, mi primera suposición sería que estaba devolviendo una nueva lista y no tocaba la que se le había dado. El segundo método, por lo tanto, es el que tiene efectos secundarios inesperados.

Siempre que sus métodos se nombren adecuadamente, hay poco peligro en modificar el parámetro. Considera esto:

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

Con un nombre como " Rellenar " puedes estar bastante seguro de que el método modificará la lista.

Otros consejos

Francamente, en este caso, ambos métodos hacen más o menos lo mismo. Ambos modificarán la List que se pasó.

Si el objetivo es tener listas inmutables por dicho método, el segundo ejemplo debe hacer una copia de la List que se envió y luego realizar la Add operación en la nueva List y luego devuélvala.

No estoy familiarizado con C # ni .NET, por lo que supongo que sería algo así como:

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

De esta forma, el método que llama al método Foo obtendrá la nueva List recién creada, y la List original que se pasó no sería tocado.

Esto realmente depende del " contrato " de sus especificaciones o API, por lo que en los casos en que las List s pueden modificarse, no veo ningún problema con el primer enfoque.

Estás haciendo exactamente lo mismo en ambos métodos, solo uno de ellos está devolviendo la misma lista.

Realmente depende de lo que estás haciendo, en mi opinión. Solo asegúrese de que su documentación sea clara sobre lo que está sucediendo. Escriba precondiciones y postcondiciones si le gustan ese tipo de cosas.

En realidad no es tan inesperado que un método que toma una lista como parámetro modifique la lista. Si desea un método que solo lea de la lista, usaría una interfaz que solo permita la lectura:

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

Al usar una interfaz como esta, no solo prohíbe que el método cambie la lista, sino que también se vuelve más flexible, ya que puede usar cualquier colección que implemente la interfaz, como una matriz de cadenas, por ejemplo.

Algunos otros idiomas tienen una palabra clave const que se puede aplicar a los parámetros para prohibir que un método los cambie. Como .NET tiene interfaces que puede usar para esto y cadenas que son inmutables, realmente no hay necesidad de parámetros const .

La llegada de los métodos de extensión ha facilitado un poco el manejo de los métodos que introducen efectos secundarios. Por ejemplo, en su ejemplo se vuelve mucho más intuitivo decir

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

y llámalo con

mylist.AddBar();

lo que aclara que algo le está sucediendo a la lista.

Como se menciona en los comentarios, esto es más útil en las listas ya que las modificaciones a una lista pueden ser más confusas. En un objeto simple, tendería a modificar el objeto en su lugar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top