c# - que - La mejor manera de crear una instancia de un objeto secundario a partir del objeto principal
que es una clase en programacion (7)
Desafortunadamente, no hay una manera fácil de hacer esto. Como dijiste, tendrías que usar la reflexión o crear un método de "Clonación" que generaría un nuevo objeto secundario usando un objeto principal como entrada, como por ejemplo:
public class MyObjectSearch : MyObject {
// Other code
public static MyObjectSearch CloneFromMyObject(MyObject obj)
{
var newObj = new MyObjectSearch();
// Copy properties here
obj.Prop1 = newObj.Prop1;
return newObj;
}
}
Pase lo que pase, o bien terminará escribiendo un código de reflexión (que es lento) o escribiendo cada propiedad a mano. Todo depende de si desea o no la capacidad de mantenimiento (reflexión) o la velocidad (copia manual de la propiedad).
Estoy creando un objeto hijo a partir de un objeto padre. Entonces, el escenario es que tengo un objeto y un objeto secundario que agrega una propiedad de distancia para los escenarios en los que quiero buscar. He elegido usar la herencia ya que mi interfaz de usuario funciona de manera equivalente con un objeto de búsqueda o una lista de objetos que no es el resultado de una búsqueda de ubicación. Entonces, en este caso, la herencia parece una opción sensata.
Como presente, necesito generar un nuevo objeto MyObjectSearch
desde una instancia de MyObject
. Actualmente, estoy haciendo esto manualmente en el constructor configurando las propiedades una por una. Podría usar la reflexión pero esto sería lento. ¿Hay una mejor manera de lograr este tipo de mejora de objetos?
Esperemos que mi código de abajo ilustra el escenario.
public class MyObject {
// Some properties and a location.
}
public class MyObjectSearch : MyObject {
public double Distance { get; set; }
public MyObjectSearch(MyObject obj) {
base.Prop1 = obj.Prop1;
base.Prop2 = obj.Prop2;
}
}
Y mi función de búsqueda:
public List<MyObjectSearch> DoSearch(Location loc) {
var myObjectSearchList = new List<MyObjectSearch>();
foreach (var object in myObjectList) {
var distance = getDistance();
var myObjectSearch = new MyObjectSearch(object);
myObjectSearch.Distance = distance;
myObjectSearchList.add(myObjectSearch);
}
return myObjectSearchList;
}
Hay bibliotecas para manejar esto; pero si solo desea una implementación rápida en algunos lugares, definitivamente me gustaría un "constructor de copia" como se sugirió anteriormente.
Un punto interesante que no se menciona es que si un objeto es una subclase, ¡entonces puede acceder a las variables privadas del niño desde dentro del padre!
Entonces, en el padre principal agregue un método CloneIntoChild
. En mi ejemplo:
-
Order
es la clase padre -
OrderSnapshot
es la clase hijo -
_bestPrice
es un miembro privado no de lectura enOrder
. PeroOrder
puede configurarlo paraOrderSnapshot
.
Ejemplo:
public OrderSnapshot CloneIntoChild()
{
OrderSnapshot sn = new OrderSnapshot()
{
_bestPrice = this._bestPrice,
_closed = this._closed,
_opened = this._opened,
_state = this._state
};
return sn;
}
NOTA: Las variables miembro de solo lectura DEBEN establecerse en el constructor, por lo que tendrá que usar el constructor secundario para configurar estos ...
Aunque no me gusta el "aumento de tamaño" en general, utilizo mucho este enfoque para las instantáneas analíticas ...
La clase base necesita definir un constructor de copia:
public class MyObject
{
protected MyObject(MyObject other)
{
this.Prop1=other.Prop1;
this.Prop2=other.Prop2;
}
public object Prop1 { get; set; }
public object Prop2 { get; set; }
}
public class MyObjectSearch : MyObject
{
public double Distance { get; set; }
public MyObjectSearch(MyObject obj)
: base(obj)
{
this.Distance=0;
}
public MyObjectSearch(MyObjectSearch other)
: base(other)
{
this.Distance=other.Distance;
}
}
De esta manera, la configuración de las propiedades se maneja para todas las clases derivadas por la clase base.
Parece natural que el objeto base tenga un constructor con parámetros para sus propiedades:
public class MyObject
{
public MyObject(prop1, prop2, ...)
{
this.Prop1 = prop1;
this.Prop2 = prop2;
}
}
Entonces, en tu objeto descendiente puedes tener:
public MyObjectSearch(MyObject obj)
:base(obj.Prop1, obj.Prop2)
Esto reduce la duplicación relacionada con las tareas. Podría usar la reflexión para copiar automáticamente todas las propiedades, pero de esta manera parece más legible.
Tenga en cuenta también que si sus clases tienen tantas propiedades que está pensando en automatizar la copia de las propiedades, entonces es probable que violen el Principio de Responsabilidad Única , y debería considerar cambiar su diseño.
Puedes usar la reflexión para copiar propiedades.
public class ChildClass : ParentClass
{
public ChildClass(ParentClass ch)
{
foreach (var prop in ch.GetType().GetProperties())
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(ch, null), null);
}
}
}
Si una copia superficial es suficiente, puede usar el método MemberwiseClone
.
Ejemplo:
MyObject shallowClone = (MyObject)original.MemberwiseClone();
Si necesita una copia profunda, puede serializar / deserializar de esta forma: https://.com/a/78612/1105687
Un ejemplo (suponiendo que escriba un método de extensión como se sugiere en esa respuesta, y lo llame DeepClone)
MyObject deepClone = original.DeepClone();
Una solución genérica sería serializarlo a json y volver. En la cadena json no hay información sobre el nombre de la clase a partir de la cual se serializó. La mayoría de la gente hace esto en javascript.
Como ves, funciona bien para los objetos pocco, pero no garantizo que funcione en todos los casos complejos. Pero sí ocurre para las clases no heredadas cuando las propiedades coinciden.
using Newtonsoft.Json;
namespace CastParentToChild
{
public class Program
{
public static void Main(string[] args)
{
var p = new parent();
p.a=111;
var s = JsonConvert.SerializeObject(p);
var c1 = JsonConvert.DeserializeObject<child1>(s);
var c2 = JsonConvert.DeserializeObject<child2>(s);
var foreigner = JsonConvert.DeserializeObject<NoFamily>(s);
bool allWorks = p.a == c1.a && p.a == c2.a && p.a == foreigner.a;
//Your code goes here
Console.WriteLine("Is convertable: "+allWorks + c2.b);
}
}
public class parent{
public int a;
}
public class child1 : parent{
public int b=12345;
}
public class child2 : child1{
}
public class NoFamily{
public int a;
public int b = 99999;
}
// Is not Deserializeable because
// Error ''NoFamily2'' does not contain a definition for ''a'' and no extension method ''a'' accepting a first argument of type ''NoFamily2'' could be found (are you missing a using directive or an assembly reference?)
public class NoFamily2{
public int b;
}
}