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 por lo que algunos de ustedes veteranos piensan que es la mejor práctica. Quiero ser coherente y apegarme a un enfoque.
Respuestas:
Mis reglas:
null
,false
,0
,0.0
...).fuente
default(T)
es siempre el valor que tiene una representación binaria interna de0
.foreach
bucle con "esto repite lo siguiente para todos los elementos de la lista", no necesitamos reexpresar constantemente los valores predeterminados de C #. Tampoco necesitamos pretender que C # tenga una semántica no inicializada. Dado que la ausencia de un valor tiene un significado claro, está bien dejarlo fuera. Si es ideal ser explícito, siempre debe usarlonew
al crear nuevos delegados (como se requería en C # 1). ¿Pero quién hace eso? El lenguaje está diseñado para codificadores concienzudos.En C # no importa. Los dos ejemplos de código que da son completamente 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 (hay un ligero matiz de esto que Jon Skeet explica en los comentarios a continuación). Si ya hay un constructor, cualquier inicialización "arriba" se moverá a la parte superior.
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 encadenarlo.
fuente
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 la superclase. En Java, se realiza inmediatamente después de lo cual permite que se use 'esto' (particularmente útil para clases internas anónimas), y significa que la semántica de las dos formas realmente coincide.
Si puede, haga que los campos sean definitivos.
fuente
Creo que hay una advertencia. Una vez cometí tal error: dentro de una clase derivada, intenté "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 recién declarados, y me costó bastante tiempo depurar.
La lección: para inicializar campos heredados , lo harías dentro del constructor.
fuente
derivedObject.InheritedField
, ¿se refiere a su base o derivada?Suponiendo el tipo en su ejemplo, definitivamente prefiera inicializar campos en el constructor. Los casos excepcionales son:
Siempre pienso en la lista de campos en la parte superior de una clase como la tabla de contenido (lo que está contenido aquí, no cómo se usa), y el constructor como la introducción. Los métodos, por supuesto, son capítulos.
fuente
¿Qué pasa si te digo que 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 ubico en las áreas que dan más por el dinero.
En un sistema de tiempo real, me pregunto si incluso necesito la variable o constante.
Y en C ++ a menudo hago casi ninguna inicialización en cualquier lugar y lo muevo a una función Init (). ¿Por qué? Bueno, en C ++ si está inicializando algo que puede generar una excepción durante la construcción del objeto, se abre a las pérdidas de memoria.
fuente
Hay muchas y diversas situaciones.
Solo necesito una lista vacia
La situación es clara. Solo necesito preparar mi lista y evitar que se produzca una excepción cuando alguien agrega un elemento a la lista.
Yo se los valores
Sé exactamente qué valores quiero tener por defecto o necesito usar alguna otra lógica.
o
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.
fuente
En Java, un inicializador con la declaración significa que el campo siempre se inicializa de la misma manera, independientemente de qué constructor se use (si tiene más de uno) o los parámetros de sus constructores (si tienen argumentos), aunque un constructor podría posteriormente cambiar el valor (si no es final). Por lo tanto, el uso de un inicializador con una declaración sugiere a un lector que el valor inicializado es el valor que tiene el campo en todos los casos , independientemente del constructor utilizado y 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.
fuente
El diseño de C # sugiere que se prefiere la inicialización en línea, o no estaría en el lenguaje. Cada vez que puede evitar una referencia cruzada entre diferentes lugares en el código, generalmente está mejor.
También está la cuestión de la coherencia con la inicialización de campo estático, que debe estar en línea para obtener el mejor rendimiento. Las Pautas de diseño del marco para el diseño de constructores dicen esto:
"Considerar" en este contexto significa hacerlo a menos que haya una buena razón para no hacerlo. En el caso de los campos de inicializador estático, una buena razón sería si la inicialización es demasiado compleja para codificarla en línea.
fuente
Ser consistente es importante, pero esta es la pregunta que debe hacerse: "¿Tengo un constructor para otra cosa?"
Por lo general, estoy creando modelos para transferencias de datos que la clase en sí misma no hace nada excepto trabajar como alojamiento de variables.
En estos escenarios, generalmente no tengo ningún método o constructor. Para mí sería una tontería crear un constructor con el exclusivo propósito de inicializar mis listas, especialmente porque puedo inicializarlas en línea con la declaración.
Como muchos otros han dicho, depende de su uso. Hazlo simple y no hagas nada extra que no tengas que hacer.
fuente
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 iguales, ¿por qué repetir para cada constructor? Esto está en línea con la declaración de 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 esa bandera se inicializaría de manera diferente para diferentes constructores, independientemente de los parámetros del constructor. Por otro lado, si repite la misma inicialización para cada constructor, deja la posibilidad de que (sin querer) cambie el parámetro de inicialización en algunos de los constructores pero no en otros. Entonces, el concepto básico aquí es que el código común debe tener una ubicación común y no potencialmente repetirse en diferentes ubicaciones.
fuente
Hay un ligero beneficio de rendimiento al establecer el valor en la declaración. Si lo configura en el constructor, en realidad se está configurando dos veces (primero al valor predeterminado, luego restablecer en el ctor).
fuente
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 poner a prueba 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 al constructor, asígnelo en el momento de la declaración.
fuente
No es una respuesta directa a su pregunta sobre las mejores prácticas, pero un punto de actualización importante y relacionado es que, en el caso de una definición de clase genérica, dejarla en el compilador para inicializar con valores predeterminados o tenemos que usar un método especial para inicializar campos a sus valores predeterminados (si eso es absolutamente necesario para la legibilidad del código).
Y el método especial para inicializar un campo genérico a su valor predeterminado es el siguiente:
fuente
Cuando no necesita algo de lógica o manejo de errores:
Cuando necesita algo de lógica o manejo de errores:
Desde https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html .
fuente