2010-09-16 10 views
27

Estoy seguro de que he visto que puedo hacer lo siguiente utilizando un atributo sobre mi método Init(), que le dice al compilador que el método Init() solo debe invocarse desde el constructor, permitiendo así el readonly campo que se establecerá. Sin embargo, olvidé cómo se llama el atributo, y parece que no puedo encontrarlo en google.¿Cómo configuro un campo de solo lectura en un método de inicialización que se llama desde el constructor?

public class Class 
{ 
    private readonly int readonlyField; 

    public Class() 
    { 
     Init(); 
    } 

    // Attribute here that tells the compiler that this method must be called only from a constructor 
    private void Init() 
    { 
     readonlyField = 1; 
    } 
} 

Respuesta

33

Rob's answer es la manera de hacerlo, en mi libro. Si necesita inicializar varios campos que puede hacerlo utilizando out parámetros:

public class Class 
{ 
    private readonly int readonlyField1; 
    private readonly int readonlyField2; 

    public Class() 
    { 
     Init(out readonlyField1, out readonlyField2); 
    } 

    protected virtual void Init(out int field1, out int field2) 
    { 
     field1 = 1; 
     field2 = 2; 
    } 
} 

Personalmente, creemos que esto tiene sentido en ciertos escenarios, como por ejemplo cuando se desea que los campos sean readonly pero también querer estar capaz de establecerlos de manera diferente en una clase derivada (sin tener que encadenar una tonelada de parámetros a través de algún constructor protected). Pero tal vez sea solo yo.

+2

parámetros de salida, yuk! –

+1

@Chuck: Oye, nunca dije que fuera bonita. –

+0

Lo resolví usando Reflection. Ver abajo ... – Derar

6

Esto no se puede hacer. Los campos que están etiquetadas readonly sólo se pueden ajustar desde el constructor

+0

No es que sea completamente incorrecto, pero esta respuesta realmente palidece en comparación con las otras respuestas aquí. Una respuesta simple de una frase "no se puede hacer" deja mucho que desear. Tal vez esta fue una de las primeras respuestas sin embargo? – Assimilater

1

compilador de C# sólo se le permite establecer campos de sólo lectura si se está inicializando en línea:

private readonly int readonlyField = 1; 

o desde el constructor:

public Class() 
{ 
    readonlyField = 1; 
} 
9

la única solución que se me ocurre es devolver el valor a partir del método Init() que el campo readonly necesita ser asignado:

public class Class 
{ 
    private readonly int readonlyField; 

    public Class() 
    { 
     readonlyField = Init(); 
    } 

    private int Init() 
    { 
     return 1; 
    } 
} 
+0

Estaba seguro de haber visto esto hecho .. Mi cerebro debe estar jugando malas pasadas conmigo. Gracias a todos –

+0

@BenAnderson, sé lo que quieres decir: pasé horas hace unos días navegando por la red, ya que estaba seguro de haber leído en algún lugar que SMB2 permite que una copia de archivo de un servidor remoto a otro no se enrute a través de la máquina del cliente que realiza la solicitud. Horas perdidas y no pude encontrar evidencia, aunque estaba * inflexible * ¡Había leído que era posible! : S – Rob

14

No, no se puede. Básicamente, debes realizar la inicialización en el constructor, para eso está disponible.

Para propósitos de reutilización, si tiene múltiples constructores, intente hacer que todos menos uno de ellos encadenen a un constructor "maestro" que hace todo el trabajo real.

Si alguno de los valores requiere un cálculo complejo, puede que no sea apropiado calcularlo en el constructor de todos modos, y si lo es, puede poner la parte de cálculo en un método privado que devuelve el valor, luego llama a ese método desde el constructor.

EDITAR: No incluiría la reflexión como una forma real de hacerlo. Hay todo tipo de declaraciones que puede hacer que no incluyen abusos a través de la reflexión. ¿Quieres que la cadena literal "x" se convierta en "y"? Claro, puedes hacer eso con la reflexión, si tienes los permisos adecuados ... pero no deberías.

+0

se puede resolver a través de Reflection. Ver debajo el ejemplo. – Derar

4

Jared tiene razón; esto no es posible. Las soluciones que puedo pensar son:

  1. Inicialice el campo en la declaración.
  2. Inicialice el campo en el constructor (incorpore manualmente su método Init).
  3. asignar el campo a un valor devuelto por un método, por ejemplo .: _myField = GetInitialMyFieldValue();
  4. Pasar el campo para el método Init, con el modificador de out.Esto puede ser útil si tiene muchos campos para inicializar, que dependen de los parámetros del constructor. P.ej.

private readonly int _x; 
private readonly string _y; 

private void Init(int someConstructorParam, out int x, out string y){ .. } 

public Class(int someConstructorParam) 
{ 
    Init(someConstructorParam, out _x, out _y); 
} 
0

creo que funciona si utilizar la reflexión. En realidad esto funciona para mí:

public class Class 
     { 
      private readonly int readonlyField; 
     public int MyField() 
     { 
      return readonlyField; 
     } 
     public Class() 
     { 
      readonlyField = 9; 
     } 
    } 

y

static void Main(string[] args) 
     { 

      Class classObj = new Class(); 
      Console.WriteLine(classObj.MyField());//9 

      Misc.SetVariableyByName(classObj, "readonlyField", 20);//20 
      Console.WriteLine(classObj.MyField()); 
     } 

esto es SetVariableByName():

public static b 

ool SetVariableyByName(object obj, string var_name, object value) 
      { 
       FieldInfo info = obj.GetType().GetField(var_name, BindingFlags.NonPublic| BindingFlags.Instance); 
       if (info == null) 
       return false; 
      /* ELSE */ 
      info.SetValue(obj, value); 
      return true;   
     } 

lo único es que no es readonlyField pública privada. Sé que puedes editar un campo privado, pero no estoy seguro de por qué no funciona para mí.

+0

funciona ahora en variables de miembros privados .. – Derar

+10

El hecho de que * pueda * hacerlo de esta manera no significa que * deba * ... y, por supuesto, en muchas situaciones no tendrá permiso para hacerlo. –

+0

No creo que esto sea para lo que fue pensado el reflejo. Rendimiento innecesario golpeó ... – clyc

12

En lugar de utilizar un método de inicialización, qué hay de heredar un constructor básico a través de todos sus otros constructores. decir

public class MyClass 
{ 
    readonly int field1; 
    readonly double field2; 
    public MyClass(int field1, double field2) 
    { 
     //put whatever initialization logic you need here... 
     field1 = 10; 
     field2 = 30.2; 
    } 
    public MyClass(int field1, double field2p1, double field2p2) 
     : this(field1, (field2p1 + field2p2)) 
    { 
     //put anything extra in here 
    } 
} 

Esto puede ser un poco tarde para llegar a la persona original en necesidad, pero parece que esto va a resolver el problema limpiamente ... Sin la necesidad de utilizar ningún tipo de reflexión o fuera desagradable parámetros.

+0

Esta es definitivamente una forma más inteligente de compartir código entre constructores. – MikeMurko

Cuestiones relacionadas