¿Puedo cambiar un campo de solo lectura privado en C# usando reflexión?


Me pregunto, ya que se pueden hacer muchas cosas usando reflexión, ¿puedo cambiar un campo de solo lectura privado después de que el constructor complete su ejecución?
(nota: solo 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
Author: Jon Seigel, 2009-06-01

8 answers

Puedes:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);
 136
Author: Philippe Leybaert,
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
2009-06-01 14:16:17

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 reglas diferentes, curiosamente - tienes que establecer explícitamente el Field para ser accesible, y solo funcionará, por ejemplo, los campos de todos modos.)

 52
Author: Jon Skeet,
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
2009-06-01 13:59:56

Estoy de acuerdo con las otras respuestas en que funciona generalmente y especialmente con el comentario de E. Lippert de que esto no es un comportamiento documentado y por lo tanto no es un código a prueba de futuro.

Sin embargo, también notamos otro problema. Si ejecuta su código en un entorno con permisos restringidos, es posible que obtenga una excepción.

Acabamos de tener un caso en el que nuestro código funcionó bien en nuestras máquinas, pero recibimos un VerificationException cuando el código se ejecutó en un entorno restringido. El culpable fue una llamada de reflexión al armador de un campo de solo lectura. Funcionó cuando eliminamos la restricción de solo lectura de ese campo.

 10
Author: Andreas,
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
2013-02-26 11:42:40

Preguntaste por qué querrías romper la encapsulación así.

Uso una clase entity helper para hidratar entidades. Esto usa la reflexión para obtener todas las propiedades de una nueva entidad vacía, y hace coincidir el nombre de la propiedad / campo con la columna en el resultset, y lo establece usando propertyinfo.setvalue().

No quiero que nadie más pueda cambiar el valor, pero tampoco quiero tomar todo el esfuerzo para personalizar métodos de hidratación de código para cada entidad.

Mi muchos de mis procs almacenados devuelven conjuntos de resultados que no se corresponden directamente con tablas o vistas, por lo que la generación de código nothing no hace nada por mí.

 4
Author: Necroposter,
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
2010-01-27 21:20:41

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

¿Por qué querrías hacerlo? Romper la encapsulación intencionalmente me parece una horrible mala idea.

Usar la reflexión para cambiar un campo de solo lectura o constante es como combinar la Ley de Consecuencias No Deseadas con la Ley de Murphy.

 2
Author: Powerlord,
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
2009-06-01 14:21:04

No hagas esto.

Acabo de pasar un día arreglando un error surrealista donde los objetos podrían no ser de su propio tipo declarado.

Modificar el campo readonly funcionó una vez. Pero si intentaras modificarlo de nuevo, obtendrías 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 hagas.

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

 2
Author: Tynan Sylvester,
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
2014-05-12 17:52:33

Otra forma sencilla de hacer esto usando unsafe (o puede pasar el campo a un método C a través de DllImport y establecerlo 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);
        }
    }
}
 2
Author: zezba9000,
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-02-21 23:19:41

Solo quiero agregar que si necesitas hacer esto para pruebas unitarias, entonces puedes usar:

A) El PrivateObject clase

B) Todavía necesitará una instancia PrivateObject, pero puede generar objetos "Accessor" con Visual Studio. Cómo Regenerar los Accesos privados

Si está configurando campos privados de un objeto en su código fuera de las pruebas unitarias, eso sería una instancia de "olor a código" Creo que tal vez la única razón por la que querría hacer esto si está tratando con una biblioteca de terceros y no puede cambiar el código de la 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 satisfacer su necesidad.

 0
Author: Dudeman3000,
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
2013-05-24 14:40:00