Inicializar campos de clase en constructor o en declaración?


He estado programando en C# y Java recientemente y tengo curiosidad por saber dónde es el mejor lugar para inicializar mis campos de clase.

¿Debo hacerlo en la declaración?:

public class Dice
{
    private int topFace = 1;
    private Random myRand = new Random();

    public void Roll()
    {
       // ......
    }
}

O en un constructor?:

public class Dice
{
    private int topFace;
    private Random myRand;

    public Dice()
    {
        topFace = 1;
        myRand = new Random();
    }

    public void Roll()
    {
        // .....
    }
}

Tengo mucha curiosidad sobre lo que algunos de ustedes veteranos piensan que es la mejor práctica. Quiero ser coherente y atenerme a un enfoque.

 343
Author: Robert Columbia, 2008-08-23

14 answers

Mis reglas:

  1. No inicializar con los valores predeterminados en la declaración (null, false, 0, 0.0...).
  2. Prefiera la inicialización en la declaración si no tiene un parámetro constructor que cambie el valor del campo.
  3. Si el valor del campo cambia debido a un parámetro constructor ponga la inicialización en los constructores.
  4. Sea consistente en su práctica (la regla más importante).
 251
Author: kokos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-16 09:28:15

En C# no importa. Las dos muestras de código que das son totalmente equivalentes. En el primer ejemplo el compilador de C# (o es el CLR?) construirá un constructor vacío e inicializará las variables como si estuvieran en el constructor. Si ya hay un constructor entonces cualquier inicialización "arriba" se moverá a la parte superior de la misma.

En términos de mejores prácticas, el primero es menos propenso a errores que el segundo, ya que alguien podría agregar fácilmente otro constructor y olvidarse de encadenar se.

 134
Author: Quibblesome,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-24 16:04:02

La semántica de C# difiere ligeramente de Java aquí. En C# la asignación en la declaración se realiza antes de llamar al constructor de superclase. En Java se hace inmediatamente después de lo cual permite 'esto' para ser utilizado (particularmente útil para clases internas anónimas), y significa que la semántica de las dos formas realmente coinciden.

Si puede, haga que los campos sean definitivos.

 14
Author: Tom Hawtin - tackline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-05 20:22:08

Creo que hay una advertencia. Una vez cometí tal error: Dentro de una clase derivada, traté de "inicializar en la declaración" los campos heredados de una clase base abstracta. El resultado fue que existían dos conjuntos de campos, uno es "base" y otro son los recientemente declarados, y me costó bastante tiempo depurar.

La lección: para inicializar campos heredados, lo haría dentro del constructor.

 12
Author: xji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-11 09:23:17

Asumiendo el tipo en su ejemplo, definitivamente prefiere inicializar los campos en el constructor. Los casos excepcionales son:

  • Campos en clases/métodos estáticos
  • Campos escritos como static/final/et al

Siempre pienso en el campo que aparece en la parte superior de una clase como la tabla de contenido (lo que se contiene aquí, no cómo se usa), y el constructor como la introducción. Los métodos, por supuesto, son capítulos.

 5
Author: Noel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-23 21:56:29

¿Y si te lo digo, depende?

En general inicializo todo y lo hago de manera consistente. Sí, es demasiado explícito, pero también es un poco más fácil de mantener.

Si estamos preocupados por el rendimiento, bueno, entonces inicializo solo lo que hay que hacer y lo coloco en las áreas que da más por el dinero.

En un sistema en tiempo real, me pregunto si incluso necesito la variable o constante en absoluto.

Y en C++ a menudo hago junto a ninguna inicialización en colocarlo y moverlo a una función Init (). ¿Por qué? Bueno, en C++ si inicializas algo que puede generar una excepción durante la construcción de objetos, te abres a fugas de memoria.

 3
Author: Dan Blair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-10 19:46:15

En Java, un inicializador con la declaración significa que el campo siempre se inicializa de la misma manera, independientemente del constructor que se use (si tiene más de uno) o los parámetros de sus constructores (si tienen argumentos), aunque un constructor puede cambiar posteriormente el valor (si no es final). Por lo tanto, el uso de un inicializador con una declaración sugiere al lector que el valor inicializado es el valor que tiene el campo en todos los casos, independientemente del constructor se utiliza e independientemente de los parámetros pasados a cualquier constructor. Por lo tanto, use un inicializador con la declaración solo si, y siempre si, el valor para todos los objetos construidos es el mismo.

 2
Author: Raedwald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-17 20:13:38

Hay muchas y diversas situaciones.

Solo necesito una lista vacía

La situación es clara. Solo necesito preparar mi lista y evitar que se lance una excepción cuando alguien agrega un elemento a la lista.

public class CsvFile
{
    private List<CsvRow> lines = new List<CsvRow>();

    public CsvFile()
    {
    }
}

Conozco los valores

Sé exactamente qué valores quiero tener por defecto o necesito usar alguna otra lógica.

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = new List<string>() {"usernameA", "usernameB"};
    }
}

O

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = GetDefaultUsers(2);
    }
}

Lista vacía con posibles valores

A veces espero una lista vacía por defecto con la posibilidad de agregar valores a través de otro constructor.

public class AdminTeam
{
    private List<string> usernames = new List<string>();

    public AdminTeam()
    {
    }

    public AdminTeam(List<string> admins)
    {
         admins.ForEach(x => usernames.Add(x));
    }
}
 2
Author: Miroslav Holec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-18 18:42:07

Establecer el valor en la declaración supone un ligero beneficio para el rendimiento. Si lo configura en el constructor, en realidad se está configurando dos veces (primero al valor predeterminado, luego se restablece en el ctor).

 1
Author: John Meagher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 15:47:24

El diseño de C# sugiere que se prefiere la inicialización en línea, o no estaría en el lenguaje. Siempre que pueda evitar una referencia cruzada entre diferentes lugares en el código, generalmente estará mejor.

También existe la cuestión de la consistencia con la inicialización de campos estáticos, que necesita estar en línea para un mejor rendimiento. Las Directrices de Diseño del Marco para El Diseño del Constructor dicen esto:

✓ CONSIDERE inicializar los campos estáticos en línea en lugar que el uso explícito de constructores estáticos, porque el tiempo de ejecución es capaz de optimizar el rendimiento de los tipos que no tienen un constructor estático definido explícitamente.

"Considerar" en este contexto significa hacerlo a menos que haya una buena razón para no hacerlo. En el caso de los campos del inicializador estático, una buena razón sería si la inicialización es demasiado compleja para ser codificada en línea.

 1
Author: Edward Brey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-14 05:46:59

Ser consistente es importante, pero esta es la pregunta que debes hacerte: "¿Tengo un constructor para algo más?"

Normalmente, estoy creando modelos para transferencias de datos que la clase en sí no hace nada excepto trabajar como alojamiento para variables.

En estos escenarios, normalmente no tengo ningún método o constructor. Sería una tontería para mí crear un constructor con el exclusivo propósito de inicializar mis listas, especialmente porque puedo inicializarlas en línea con el declaración.

Así que como muchos otros han dicho, depende de su uso. Mantenlo simple, y no hagas nada extra que no tengas que hacer.

 1
Author: MeanJerry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-24 20:27:19

Considere la situación en la que tiene más de un constructor. ¿La inicialización será diferente para los diferentes constructores? Si serán los mismos, entonces ¿por qué repetir para cada constructor? Esto está en línea con la declaración kokos, pero puede no estar relacionado con los parámetros. Digamos, por ejemplo, que desea mantener una bandera que muestre cómo se creó el objeto. Entonces ese indicador se inicializaría de manera diferente para diferentes constructores independientemente de los parámetros del constructor. Por el otro a mano, si repite la misma inicialización para cada constructor, deja la posibilidad de que (involuntariamente) cambie el parámetro de inicialización en algunos de los constructores pero no en otros. Por lo tanto, el concepto básico aquí es que el código común debe tener una ubicación común y no ser potencialmente repetido en diferentes ubicaciones. Por lo tanto, yo diría que siempre lo incluyas en la declaración hasta que tengas una situación específica en la que ya no funcione para ti.

 1
Author: Joe Programmer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-20 21:05:40

Normalmente intento que el constructor no haga nada más que obtener las dependencias e inicializar los miembros de instancia relacionados con ellas. Esto te hará la vida más fácil si quieres probar unitariamente tus clases.

Si el valor que va a asignar a una variable de instancia no se ve influenciado por ninguno de los parámetros que va a pasar a su constructor, asígnelo en el momento de la declaración.

 0
Author: Iker Jimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-24 15:59:11

No es una respuesta directa a su pregunta sobre la mejor práctica pero un punto de actualización importante y relacionado es que en el caso de una definición de clase genérica, o bien dejar que el compilador inicialice con valores predeterminados o tenemos que usar un método especial para inicializar los campos a sus valores predeterminados (si eso es absolutamente necesario para la legibilidad del código).

class MyGeneric<T>
{
    T data;
    //T data = ""; // <-- ERROR
    //T data = 0; // <-- ERROR
    //T data = null; // <-- ERROR        

    public MyGeneric()
    {
        // All of the above errors would be errors here in constructor as well
    }
}

Y el método especial para inicializar un campo genérico a su valor predeterminado es el siguiente:

class MyGeneric<T>
{
    T data = default(T);

    public MyGeneric()
    {           
        // The same method can be used here in constructor
    }
}
 0
Author: user1451111,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-08 12:32:06