Pregunta

Me pregunto, ya que una gran cantidad de cosas se puede hacer usando la reflexión, puedo cambiar un campo de sólo lectura privada después de que el constructor ha completado su ejecución?
(Nota: simplemente curiosidad)

public class Foo
{
 private readonly int bar;

 public Foo(int num)
 {
  bar = num;
 }

 public int GetBar()
 {
  return bar;
 }
}

Foo foo = new Foo(123);
Console.WriteLine(foo.GetBar()); // display 123
// reflection code here...
Console.WriteLine(foo.GetBar()); // display 456
¿Fue útil?

Solución

Es posible:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);

Otros consejos

Lo obvio es probarlo:

using System;
using System.Reflection;

public class Test
{
    private readonly string foo = "Foo";

    public static void Main()
    {
        Test test = new Test();
        FieldInfo field = typeof(Test).GetField
            ("foo", BindingFlags.Instance | BindingFlags.NonPublic);
        field.SetValue(test, "Hello");
        Console.WriteLine(test.foo);
    }        
}

Esto funciona bien. (Java tiene diferentes reglas, curiosamente -. Usted tiene que establecer explícitamente la Field ser accesible, y que sólo funcionará para los campos de instancia de todos modos)

Estoy de acuerdo con las otras respuestas en que funciona general y, especialmente, con el comentario de E. Lippert que esto no se documenta el comportamiento y el código, por tanto, no a prueba de futuro.

Sin embargo, también notamos otro tema. Si está ejecutando su código en un entorno con permisos restringidos podría obtener una excepción.

Nos acabamos de tener un caso en nuestro código funcionó bien en nuestras máquinas, pero recibimos una VerificationException cuando el código corrió en un entorno restringido. El culpable fue un llamado de reflexión a la incubadora de un campo de sólo lectura. Se trabajó cuando hemos eliminado la restricción de sólo lectura de ese campo.

Se le preguntará por qué desea para romper la encapsulación de esa manera.

Yo uso una clase de entidad de ayuda a hidratar las entidades. Este utiliza la reflexión para obtener todas las propiedades de una nueva entidad vacía, y coincide con el nombre de la propiedad / campo a la columna del conjunto de resultados, y ha establecido que el uso de propertyinfo.setvalue ().

No quiero que nadie más sea capaz de cambiar el valor, pero no quiero tomar todo el esfuerzo a los métodos de hidratación de código personalizado para cada entidad tampoco.

Mi muchos de mis procedimientos almacenados conjuntos de resultados que no corresponden directamente a tablas o vistas retorno, por lo que el código de generación de ORM no hacen nada para mí.

La respuesta es sí, pero lo más importante:

¿Por qué quieres? encapsulación intencionadamente romper parece una mala idea terriblemente a mí.

Uso de reflexión para cambiar una de sólo lectura o de campo constante es como la combinación de la ley de consecuencias imprevistas ley de Murphy .

No haga esto.

Me acabo de pasar un día de corregir un fallo surrealista donde los objetos pueden ser no de su propio tipo declarado.

Modificar el campo de sólo lectura funcionó una vez. Pero si se trató de modificar de nuevo, se obtendría situaciones como esta:

SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field;
if( !(mySound is SoundDef) )
    Log("Welcome to impossible-land!"); //This would run

Así que no lo haga.

Esto fue en el tiempo de ejecución Mono (motor de juego de la Unidad).

Otra manera simple de hacer esto utilizando inseguro (o puede pasar al campo a un método C a través DLLImport y la puso allí).

using System;

namespace TestReadOnly
{
    class Program
    {
        private readonly int i;

        public Program()
        {
            i = 66;
        }

        private unsafe void ForceSet()
        {
            fixed (int* ptr = &i) *ptr = 123;
        }

        static void Main(string[] args)
        {
            var program = new Program();
            Console.WriteLine("Contructed Value: " + program.i);
            program.ForceSet();
            Console.WriteLine("Forced Value: " + program.i);
        }
    }
}

Sólo quiero añadir que si lo que necesita hacer esto para las pruebas unitarias, entonces se puede usar:

A) La PrivateObject clase

B) Usted todavía tendrá una instancia PrivateObject, pero se puede generar objetos "Accessor" con Visual Studio. Cómo: Regenera descriptores de acceso privado

Si está configurando los campos privados de un objeto en el código fuera de la unidad de pruebas, eso sería un caso de "olor código" Creo que tal vez la única razón por la que querría hacer esto es si se trata de una tercera parte de la biblioteca y no se puede cambiar el código de clase de destino. Incluso entonces, es probable que desee ponerse en contacto con la tercera parte, explicar su situación y ver si no van a seguir adelante y cambiar su código para acomodar sus necesidades.

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