Create a cloned copy of a subclass from the base layer

Consider this scenario:

public class Base { public int i; } public class Sub : Base { public void foo() { /* do stuff */} } 

And then I want the Base instance to get the cloned Sub instance (with i = 17 in this case) so that I can call foo in the subclass.

 Base b = new Base { i=17 }; Sub s = CloneAndUpcast(b); s.foo(); 

However, how can I create CloneAndUpcast ?

I think it should be possible to recursively clone all Base members and properties using reflection. But quite some work.

Anyone with better, tidier ideas?

PS. The scenario in which I think about using this is a collection of “simple” classes in a tree structure (without cyclic graphs or the like here), and all classes are simple value holders. The plan is to have a stupid layer containing all the values, and then a similar set of classes (subclasses) that actually contains some business logic, the owners of the values ​​should not know. In general, bad practice, yes. I think this works in this case.

+4
source share
5 answers

Here is one way (out of many possibilities) that you can do something like what you are asking. I'm not sure if this is very pretty and can be disgusting for debugging, but I think this works:

 class BaseClass { public int i { get; set; } public BaseClass Clone(BaseClass b) { BaseClass clone = new BaseClass(); clone.i = bi; return clone; } } class SubClass : BaseClass { public int j { get; set; } public void foo() { Console.WriteLine("in SubClass with value of i = {0}", i.ToString()); } } class Program { static void Main(string[] args) { BaseClass b1 = new BaseClass() { i = 17 }; BaseClass b2 = new BaseClass() { i = 35 }; SubClass sub1 = CloneAndUpcast<SubClass>(b1); SubClass sub2 = CloneAndUpcast<SubClass>(b2); sub1.foo(); sub2.foo(); } static T CloneAndUpcast<T>(BaseClass b) where T : BaseClass, new() { T clone = new T(); var members = b.GetType().GetMembers(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance); for (int i = 0; i < members.Length; i++) { if (members[i].MemberType== MemberTypes.Property) { clone .GetType() .GetProperty(members[i].Name) .SetValue(clone, b.GetType().GetProperty(members[i].Name).GetValue(b, null), null); } } return clone; } } 

Basically, as you suggested, you use reflection to iterate through the properties of the object (I set i and j as public properties) and sets the values ​​accordingly in the cloned object. The key uses generics to tell CloneAndUpcast what type you are dealing with. Once you do that, it's pretty simple.

Hope this helps. Good luck

+7
source

You can use AutoMapper to avoid the boredom of writing copy constructors.

 public class MyClass : MyBase { public MyClass(MyBase source) { Mapper.Map(source, this); } } 

and you need to run it once when you start the application

 Mapper.CreateMap<MyBase, MyClass>(); 

You can download AutoMapper from https://github.com/AutoMapper/AutoMapper

+12
source

In “Gang of Four”: “Layout Composition over Inheritance,” and this is a great reason for this ...

If we have a SuperClass that looks like this:

 public class SuperClass : Person 

SuperClass can easily decorate the properties of the Person class by adding properties not found in the Person class. But what happens if the Superclass scenery is for GUI only? For example, a bool value indicates "Selected." We can still get all the Leaders from the database on the List, but we ran into difficulties trying to create a Superclass and combine the results of the database.

  foreach( var person in myPersonList){ var sc = new SuperClass(); sc.Selected = false; sc=person; } 

The compiler complains because Superclass is not Human for the compiler, it is a Superclass. The only way to populate the properties of the Person subclass is to iterate and set each of them ... like that.

  SuperClass.Name = Person.Name; SuperClass.Id = Person.ID; 

Pretty tiring. But there is a better way .... Don't make a superclass inherit from Person

 public class SuperClass{ public Person ThisPerson {get;set;} public bool Selected {get;set;} } 

This gives us "Containment". The superclass now contains the Person class.

Now we can do this:

 foreach(var person in MyPersonList){ var sc = new Superclass(); sc.Selected = false; sc.Person = person; } 

The consumer of this class should now qualify the properties of the Superclass / Person, like this ...

 forach(var sc in MySuperClassList){ var selected = sc.Selected; var name = sc.Person.Name; } 

The beauty is that in the future you can add any other container that you want and it will NOT affect other containers. You can also convert the Superclass to everything that it contains. If each of the contained classes becomes an interface, then this is one step further down the road.

+3
source

Well, since b not a Sub , we cannot “clone” it as one.

If Base has an appropriate combination of constructor and public properties to allow the constructor in Sub guarantee that its base will have the same state as b , then we could use that.

I think I’ll get around all this. If all we care about is that s has the same state in its base as b , and it does not have another state that we care about (otherwise we would have to pass it to CloneAndUpcast ), then we do you need s at all?

The static method can take Base , and we could just use static public void foo(Base bc) . We could even define it as an extension method of static public void foo(this Base bc) , and then encode the call as b.foo() . The only thing that will not allow us to do this, CloneAndUpcast() allows us to do this, protected members.

+1
source

Clone is bad practice, and your question is the reason for this (cloning a subclass). In general, you should just use copy cotrs instead, and the subclass takes the parent element as a parameter.

public Base () {}

public Base (Base pSource) {}

public Sub () {}

public Sub (pSource base source, other parameters ...) {}

public Sub (Sub pSource) {}

+1
source

Source: https://habr.com/ru/post/1393039/


All Articles