Consider this scenario:
public class Base
{
public int i;
}
public class Sub : Base
{
public void foo() { /* do stuff */}
}
And th
Here's one way (out of many possibilities) that you could do something like you're asking. I'm not sure this is very pretty and can be kind of ugly to debug, but I think it works:
class BaseClass
{
public int i { get; set; }
public BaseClass Clone(BaseClass b)
{
BaseClass clone = new BaseClass();
clone.i = b.i;
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 object's properties (I set i
and j
as public properties) and set the values accordingly in the cloned object. The key is using generics to tell CloneAndUpcast what type you're dealing with. Once you do that, it's pretty straightforward.
Hope this helps. Good luck!
You could use AutoMapper to avoid the tedium of writing the copy constructors.
public class MyClass : MyBase
{
public MyClass(MyBase source)
{
Mapper.Map(source, this);
}
}
and you need to run this once when your application starts up
Mapper.CreateMap<MyBase, MyClass>();
You can download AutoMapper from https://github.com/AutoMapper/AutoMapper
Well, since b
isn't a Sub
, we can't "clone" it as one.
If Base
has an appropriate combination of constructor and public properties to let a constructor in Sub
ensure that its base would therefore have the same state as b
, then we could use that.
I think I'd by-pass the whole thing though. If all we care about is that s
have the same state in its base as b
, and it has not other state that we're going to care about (or else we'd have to be passing it through to the CloneAndUpcast
method), then do we need s
at all?
A static method could take a Base
and we could just use static public void foo(Base bc)
. We could even define it as an extension method static public void foo(this Base bc)
and then code the call as b.foo()
. The only thing this won't let us do that CloneAndUpcast()
lets us do is access protected members.
Clone is a bad practice and your question is the reason for that (subclass cloning). In general, you should just use copy cotrs instead and have the subclass accept a parent as a parameter.
public Base(){}
public Base(Base pSource){}
public Sub(){}
public Sub(Base pSource, other parameters...){}
public Sub(Sub pSource){}
Per the "Gang of Four" : "Favor composition over inheritance" and this is a perfect reason to do so...
If we have a SuperClass that looks like this:
public class SuperClass : Person
The SuperClass can easily decorate the Person class adding properties not found in Person class. But what happens if the Superclass decorations are only for the GUI? For example a bool value indicating "Selected". We are still able to get all Persons from the DB in a List but we run into trouble trying to create the Superclass and merge the DB results.
foreach( var person in myPersonList){
var sc = new SuperClass();
sc.Selected = false;
sc=person;
}
The compiler complains because Superclass is not a Person to the compiler it's a Superclass. The only way to fill in the properties of the Person subclass is to iterate and set each one... like this.
SuperClass.Name = Person.Name;
SuperClass.Id = Person.ID;
Pretty tedious indeed. But there's a better way.... Don't make 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 a 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 must 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 of this is that in the future, you can add any other container you want and it will NOT affect any other containers. You can also morph the Superclass to anything it contains. If each of the contained classes become Interfaces, then that's one step futher down the road.