El siguiente código establece el valor de un objeto usando la reflexión. El problema es que está reasignando la memoria al subcampo (Nombre) del objeto principal (refObj). En lugar de establecer el valor en la ubicación de memoria existente.

namespace MemoryAllocation
{
    class Name
    {
        public string name;

        public Name(string n)
        {
            name = n;
        }
    }

    class PersonData
    {
        public Name PersonName;
        public int age;

        public PersonData()
        {
            age = 0;
            PersonName = new Name("");
        }
    }

    class ReflectionPractice
    {
        object localObj;
        Type type;

        public ReflectionPractice( object refObj)
        {
            localObj = refObj;
            type = refObj.GetType();
        }

        public void setValueToObject1()
        {
            FieldInfo fi1 = type.GetField("age");
            FieldInfo fi2 = type.GetField("PersonName");
            Name personName = new Name("This is first name");
            fi1.SetValue(localObj, 34);
            fi2.SetValue(localObj, personName);

        }

        public void setValueToObject2()
        {
            FieldInfo fi1 = type.GetField("age");
            FieldInfo fi2 = type.GetField("PersonName");
            Name personName = new Name("This is second name");
            fi1.SetValue(localObj, 27);
            fi2.SetValue(localObj, personName);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            object refObj = new PersonData();
            Name personName;
            ReflectionPractice reflection = new ReflectionPractice(refObj);

            reflection.setValueToObject1();

            personName = (refObj as PersonData).PersonName;

            Console.WriteLine(personName.name);
            Console.WriteLine((refObj as PersonData).PersonName.name);


            reflection.setValueToObject2();

            Console.WriteLine(personName.name);
            Console.WriteLine((refObj as PersonData).PersonName.name);
        }
    }
}

El resultado esperado debe ser

This is first name
This is first name
This is second name
This is second name

Pero es como

This is first name
This is first name
This is first name
This is second name

Si dentro de la clase ReflectionPractice, muevo el objeto Name fuera del método "setValueToObject" y asigno memoria una vez, entonces la salida es correcta. Pero en mi situación problemática, tengo que asignar memoria a Nombre cada vez que llamo al método "setValueToObject". Cualquier sugerencia de solución será muy apreciada.

Gracias

c#
0
muhayyuddin gillani 28 abr. 2020 a las 11:34

4 respuestas

La mejor respuesta

Si está utilizando C # 7.0, es una solución bastante fácil. Primero por qué sucede esto, si no está claro. Cuando obtiene personName la primera vez, obtiene una dirección para el objeto PersonName, que es la misma que la dirección en refObj. Una vez que llame a setValueToObject2, el Objeto en sí no cambia, pero se genera uno nuevo en una nueva dirección. Luego, la dirección se asigna a refObj.PersonName, pero su referencia local no sabe nada de esto y aún apunta al objeto inicial, que es el comportamiento correcto y esperado.

Para cambiar esto y seguir explícitamente los cambios en la fuente en refObj.PersonName, deberá declarar la variable local personName como ref - Variable. personName ya no apuntará al Objeto en sí, sino a refObj.PersonName y lo que esté detrás de esa Variable, por lo que también se Actualizará cuando cambie esa Variable.

Un ejemplo de trabajo se vería así:

object refObj = new PersonData();
ReflectionPractice reflection = new ReflectionPractice(refObj);

reflection.setValueToObject1();

ref Name personName =  ref (refObj as PersonData).PersonName;

Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);


reflection.setValueToObject2();

Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);

Una explicación más detallada: Cuando asigna un Objeto a una Variable, de hecho asigna una referencia al Objeto, no el Valor del Objeto en sí. Esta referencia es un número, que nos dice dónde buscar los datos del objeto. Entonces, si digo reflection.setValueToObject1(), se genera un nuevo Objeto en la Dirección 1234, por ejemplo. Este número es lo que contendrá la Variable refObj.PersonName, aunque nunca verás ese número. Cuando asigna una variable con una referencia a ese nuevo objeto, lo único que se hace es copiar ese número en la nueva variable. Entonces, después de decir personName = refObj.PersonName, personName ahora también contiene la Dirección 1234 y, por lo tanto, apunta al mismo Objeto que refObj.PersonName.

Como podemos ver, si configuramos refObj.PersonName.Name = "Test", el programa primero buscará en refObj.PersonName y tomará esa dirección. Luego va a dicha dirección y cambia el valor de Name. Lo mismo sucede si cambia personName.Name. Primero busca la dirección en la variable, va a esa dirección y cambia el nombre del campo. Es por eso que verá el cambio de Nombre en ambas Variables en este caso, y esto es lo que significa "tipo de referencia" (Un tipo de valor creará una copia, por lo que esto no sucederá).

Pero; cuando creas un nuevo Objeto, también creas una nueva Dirección para que el Objeto viva, p. 4567. Eso es lo que hace la new palabra clave: asignar algo de memoria y crear un nuevo objeto allí. Esta dirección ahora está asignada a refObj.PersonName, pero no personName (ya que la asignación claramente solo ocurre en el refObj). Entonces personName todavía tiene la Dirección 1234 y, por lo tanto, todavía apunta al Objeto anterior.

Lo que hace ref: cuando crea el refObj, él mismo (y todos los campos) tendrán una determinada Dirección en la memoria. Digamos que refObj está en 9900, y su campo refObj.PersonName está en 9999. Cuando dice personName = refObj.PersonName, el programa mira qué Dirección está almacenada dentro de refObj.PersonName (1234) y la copia en personName, pero cuando dice ref personName = ref refObj.PersonName, toma la Dirección de el campo en sí y copia eso. Entonces ahora personName tiene el valor 9999 en su lugar. Cada vez que accede a personName ahora, primero mira la Dirección 9999 y luego la sigue a la Dirección que contenga (1234 o 4567). Es por eso que se actualizará también cuando cambie refObj.PersonName.

O, si eres más del tipo visual, esto es lo que sucede:

refObj.PersonName = new Name("Name 1")

refObj.PersonName = new Name("Name 1")

personName = refObj.PersonName

personName = refObj.PersonName

refObj.PersonName = new Name("Name 2")

refObj.PersonName = new Name("Name 2")

Compárelo con lo que sucede cuando usa la palabra clave ref:

ref personName = ref refObj.PersonName

ref personName = ref refObj.PersonName

refObj.PersonName = new Name("Name 2")

 refObj.PersonName = new Name("Name 2")

Espero que esto ayude a aclarar esto un poco :)

2
CShark 28 abr. 2020 a las 10:39
 var refObj = new PersonData(); 
 /* You have only a single Name object 
 * There is a single reference to it
 */

 var reflection = new ReflectionPractice(refObj);

 reflection.setValueToObject1(); 
 /* You have two Name objects
 * Where the name is "" there is no reference anymore 
 * Where the name is "This is first name" there is a single reference
 */

 var personName = (refObj as PersonData).PersonName;      
 /* You have two Name objects
 * Where the name is "" there is no reference anymore 
 * Where the name is "This is first name" there are two references
 */

 reflection.setValueToObject2();
 /* You have three Name objects
 * Where the name is "" there is no reference anymore 
 * Where the name is "This is first name" there is one reference (personName variable)
 * Where the name is "This is second name" there is one reference 
 */

Es por eso que su segundo Console.WriteLine(personName.name); imprime "este es su nombre".

0
Peter Csala 28 abr. 2020 a las 09:00

Establecer valores de esta manera solucionó el problema. @CShark

public void setValueToObject1()
        {
            FieldInfo fi1 = type.GetField("age");
            FieldInfo fi2 = type.GetField("PersonName");
            fi1.SetValue(localObj, 34);

            TypedReference reference = __makeref(localObj);
            object obj = fi2.GetValueDirect(reference);
            Type localtype = obj.GetType();
            FieldInfo filocal = localtype.GetField("name");
            object o = "first name";
            filocal.SetValueDirect(__makeref(obj),o);
            fi2.SetValue(localObj, obj);


        }

        public void setValueToObject2()
        {
            FieldInfo fi1 = type.GetField("age");
            FieldInfo fi2 = type.GetField("PersonName");

            TypedReference reference = __makeref(localObj);
            object obj = fi2.GetValueDirect(reference);
            Type localtype = obj.GetType();
            FieldInfo filocal = localtype.GetField("name");
            object o = "second name";
            filocal.SetValueDirect(__makeref(obj), o);
            fi2.SetValue(localObj, obj);
        }
0
muhayyuddin gillani 28 abr. 2020 a las 15:41

La clave aquí es esta parte de su código:

personName = (refObj as PersonData).PersonName;

Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);


reflection.setValueToObject2();

Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);

Aquí asigna el PersonName de la primera llamada a la variable local personName. Como nunca asigna nada diferente a personName, siempre señalará exactamente la misma instancia, que es el Name - objeto de su primera llamada. Al llamar a setValueToObject2, simplemente crea una instancia completamente nueva de la clase Name que no tiene ninguna relación con la primera. Cualquier modificación en esta instancia, por lo tanto, no se refleja en la primera instancia y, por lo tanto, tampoco dentro de personName.

Por lo tanto, personName siempre refleja la primera Name instancia. (reoObj as PersonData).PersonName por otro lado apunta al real (segundo).

Puede solucionarlo fácilmente reasignando personName:

personName = (refObj as PersonData).PersonName;

Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);


reflection.setValueToObject2();
personName = (refObj as PersonData).PersonName; // get the new instance

Console.WriteLine(personName.name);
Console.WriteLine((refObj as PersonData).PersonName.name);
1
Ehsan Sajjad 28 abr. 2020 a las 09:30