remarks generate example c# initialization

generate - params comments c#



Comprender los requisitos de inicialización del campo C# (4)

La inicialización del campo viene antes de la llamada del constructor de la clase base, por lo que no es un objeto válido. Cualquier método llamado con this como argumento en este punto conduce a un código no verificable y arroja VerificationException si no se permite el código no verificable. Por ejemplo: en código transparente de seguridad.

  • 10.11.2 Inicializadores de variables de instancia
    Cuando un constructor de instancia no tiene un inicializador de constructor, o tiene un inicializador de constructor de la base de formulario (...), ese constructor realiza implícitamente las inicializaciones especificadas por los inicializadores de variable de los campos de instancia declarados en su clase. Esto corresponde a una secuencia de asignaciones que se ejecutan inmediatamente después de la entrada al constructor y antes de la invocación implícita del constructor de la clase base directa. Los inicializadores de variable se ejecutan en el orden textual en el que aparecen en la declaración de clase.
  • 10.11.3 Ejecución del constructor
    Los inicializadores de variables se transforman en sentencias de asignación, y estas sentencias de asignación se ejecutan antes de la invocación del constructor de instancia de la clase base. Este orden asegura que todos los campos de instancia sean inicializados por sus inicializadores de variables antes de que se ejecuten las sentencias que tienen acceso a esa instancia.

Teniendo en cuenta el siguiente código:

public class Progressor { private IProgress<int> progress = new Progress<int>(OnProgress); private void OnProgress(int value) { //whatever } }

Esto da el siguiente error en la compilación:

Un inicializador de campo no puede hacer referencia al campo, método o propiedad no estáticos ''Progressor.OnProgress (int)''

Entiendo la restricción de la que se queja, pero no entiendo por qué es un problema, pero el campo se puede inicializar en el constructor de la siguiente manera:

public class Progressor { private IProgress<int> progress; public Progressor() { progress = new Progress<int>(OnProgress); } private void OnProgress(int value) { //whatever } }

¿Cuál es la diferencia en C # con respecto a la inicialización de campo frente a la inicialización del constructor que requiere esta restricción?


La respuesta es más o menos, los diseñadores de C # lo prefirieron de esa manera.

Dado que todos los inicializadores de campo se traducen en instrucciones en el constructor que va antes que cualquier otra instrucción en el constructor, no hay ninguna razón técnica por la que esto no sea posible. Entonces es una elección de diseño.

Lo bueno de un constructor es que deja en claro en qué orden se realizan las asignaciones.

Tenga en cuenta que con static miembros static , los diseñadores de C # eligieron de manera diferente. Por ejemplo:

static int a = 10; static int b = a;

está permitido, y diferente de esto (también permitido):

static int b = a; static int a = 10;

que puede ser confuso

Si tu haces:

partial class C { static int b = a; }

y en otro lugar (en otro archivo):

partial class C { static int a = 10; }

Ni siquiera creo que esté bien definido lo que sucederá.

Por supuesto, para su ejemplo particular con delegados en un inicializador de campo de instancia:

Action<int> progress = OnProgress; // ILLEGAL (non-static method OnProgress)

realmente no hay problema ya que no es una lectura o una invocación del miembro no estático. Más bien se usa la información del método, y no depende de ninguna inicialización. Pero de acuerdo con la Especificación del lenguaje C #, sigue siendo un error en tiempo de compilación.


Sección 10.5.5.2: Inicialización del campo de instancia describe este comportamiento:

Un inicializador variable para un campo de instancia no puede hacer referencia a la instancia que se está creando. Por lo tanto, es un error en tiempo de compilación hacer referencia a this en un inicializador de variable , ya que es un error en tiempo de compilación para un inicializador variable hacer referencia a cualquier miembro de instancia a través de un nombre simple

Este comportamiento se aplica a su código porque OnProgress es una referencia implícita a la instancia que se está creando.


Todo en mi respuesta es solo mis pensamientos sobre ''por qué sería peligroso permitir ese tipo de acceso''. No sé si esa es la verdadera razón por la que fue restringido.

La especificación de C # dice que la inicialización del campo ocurre en el orden en que los campos se declaran en la clase:

10.5.5.2. Inicialización de campo de instancia

Los inicializadores de variable se ejecutan en el orden textual en el que aparecen en la declaración de clase.

Ahora, supongamos que el código que ha mencionado es posible: puede llamar al método de instancia desde la inicialización de campo. Haría posible el siguiente código:

public class Progressor { private string _first = "something"; private string _second = GetMyString(); private string GetMyString() { return "this is really important string"; } }

Hasta aquí todo bien. Pero abusemos un poco de ese poder:

public class Progressor { private string _first = "something"; private string _second = GetMyString(); private string _third = "hey!"; private string GetMyString() { _third = "not hey!"; return "this is really important string"; } }

Entonces, _second get se inicializó antes de _third . GetMyString ejecuta, _third get", "no está bien". valor asignado, pero más tarde se ejecuta su propia inicialización de campo, y se establece en `" ¡Hola! ". No es realmente útil ni legible, ¿verdad?

También puede usar _third dentro del método GetMyString :

public class Progressor { private string _first = "something"; private string _second = GetMyString(); private string _third = "hey!"; private string GetMyString() { return _third.Substring(0, 1); } }

¿Cuál esperarías que fuera el valor de _second ? Bueno, antes de que la inicialización del campo se ejecute, todos los campos obtienen valores predeterminados. Para string , sería null , por lo que obtendrá NullReferenceException inesperada.

Entonces, los diseñadores decidieron que es más fácil evitar que la gente cometa ese tipo de errores.

Podrías decir: OK, no permitamos el acceso a las propiedades y a los métodos de llamada, pero permitamos usar los campos que fueron declarados por encima del que quieres acceder. Algo como:

public class Progressor { private string _first = "something"; private string _second = _first.ToUpperInvariant(); }

pero no

public class Progressor { private string _first = "something"; private string _second = _third.ToUpperInvariant(); private string _third = "another"; }

Eso parece útil y seguro. ¡Pero todavía hay una manera de abusar de eso!

public class Progressor { private Lazy<string> _first = new Lazy<string>(GetMyString); private string _second = _first.Value; private string GetMyString() { // pick one from above examples } }

Y todos los problemas con los métodos vuelven nuevamente.