24

I currently have a derived class and a base class. How can I make the base class of the derived class equal to a base class that I have? Will a shallow copy work?

class Base
{
    private string name; 
    public string Name { get; set; }
    private string address; 
    public string Address { get; set; }
}

class Derived:Base
{
    private string field; 
    public String field { get; set; }
}

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Base b = new Base();
            b.Address = "Iliff";
            b.Name = "somename"; 

            Derived d = new Derived();
            //How can I make the base class of d equal to b ?

        }
    }
}
Casper_2211
  • 1,075
  • 5
  • 14
  • 25
  • 3
    Do you want to reference the same object, or copy? – andrew Jan 30 '13 at 21:16
  • It could reference the same object – Casper_2211 Jan 30 '13 at 21:18
  • why do you need a 'b' and a 'd'? what's the purpose of either one of them? I am trying to make sense out of your code but I find myself a bit puzzled. why don't you just instantiate 'd' and store it as 'b' if you want to talk to base type? – bas Jan 30 '13 at 21:21
  • 1
    I suggest you declare d and then set b from d via casting – andrew Jan 30 '13 at 21:35

12 Answers12

23

Create a copy constructor for the base class, in doing so you'll also need to create a parameterless one as well as by adding the copy constructor the default constructor will no longer be generated by the compiler. Then in the derived class call the base class's copy constructor.

public class Base
{
    public int Name { get; set; }
    public string Address { get; set; }

    public Base()
    { }

    public Base(Base toCopy)
    {
        this.Name = toCopy.Name;
        this.Address = toCopy.Address;
    }
}

public class Derived : Base
{
    public String Field { get; set; }

    public Derived(Base toCopy)
        : base (toCopy)
    { }

    // if desired you'll need a parameterless constructor here too
    // so you can instantiate Derived w/o needing an instance of Base
    public Derived()
    { }
}
JG in SD
  • 5,427
  • 3
  • 34
  • 46
10

If I understand you correctly, this will work:

class Derived : Base
{
    // all the code you had above, plus this:

    public Derived(Base toCopy)
    {
        this.name = toCopy.name;
        this.address = toCopy.address;
    }
}

Derived d = new Derived(b);
Ben
  • 6,023
  • 1
  • 25
  • 40
  • 10
    What if you have like 100+ properties in the base class and only one in the derived class? There must be a better way to auto match and assign values, after all the base is the same class. – Tyler Jun 21 '17 at 03:52
  • @Samir Did you find a way to do this? – Rescis Mar 05 '18 at 23:19
  • 1
    @Samir I think reflection is the only option https://stackoverflow.com/questions/4823766/derived-and-base-class-can-i-set-the-base-explicitly/19010127#19010127 – maxp Jun 25 '18 at 11:42
10

Another approach would be to map the base class to derived class:

/// <summary>
/// Maps the source object to target object.
/// </summary>
/// <typeparam name="T">Type of target object.</typeparam>
/// <typeparam name="TU">Type of source object.</typeparam>
/// <param name="target">Target object.</param>
/// <param name="source">Source object.</param>
/// <returns>Updated target object.</returns>
public static T Map<T, TU>(this T target, TU source)
{
    // get property list of the target object.
    // this is a reflection extension which simply gets properties (CanWrite = true).
    var tprops = target.GetProperties();

    tprops.Where(x=>x.CanWrite == true).ToList().ForEach(prop =>
    {
        // check whether source object has the the property
        var sp = source.GetType().GetProperty(prop);
        if (sp != null)
        {
            // if yes, copy the value to the matching property
            var value = sp.GetValue(source, null);
            target.GetType().GetProperty(prop).SetValue(target, value, null);
        }
    });

    return target;
}

Example:

var derivedClass = new DerivedClass();
derivedClass.Map(baseClass);
Furtiro
  • 439
  • 5
  • 21
mnyarar
  • 515
  • 1
  • 6
  • 13
  • 2
    I like this solution - there are two errors in the code though: 1) GetProperties() is a method of Type class, so it shall be var tprops = target.GetType().GetProperties() 2) in the lambda function GetProperty() takes string as argument, so it shall read GetProperty(prop.Name) – Simon Jul 18 '20 at 10:04
4

You will have to manually copy the fields of the Base instance to the new Derived instance.

A common way of doing this is by offering a copy constructor:

public Derived(Base other)
{
    if (other == null) {
        throw new ArgumentNullException("other");
    }

    this.name = other.name;
    this.address = other.address;
}

One more note about your code:

private string field; 
public string Field { get; set; }

This does not make much sense (same for the other properties).

public string Field { get; set; } means that a private field will automatically be created by the compiler. Your field field will never be used at all.

Either just write public string Field { get; set; }, as the private field will be created automatically. Or declare the Field property in a way so that your private field will be used:

private string field;

public string Field {
    get {
        return field;
    }
    set {
        field = value;
    }
}
O. R. Mapper
  • 20,083
  • 9
  • 69
  • 114
2

You can always use Object.MemberwiseClone to copy it.

http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

Or implement the IClonable interface: http://msdn.microsoft.com/en-us/library/system.icloneable.aspx

Bauss
  • 2,767
  • 24
  • 28
  • 4
    How you can use the `MemberwiseClone` in this case? It will produce the base class, not the derived one. – VMAtm Dec 29 '14 at 09:02
1

I came up with a pretty good pattern for dealing with this situation.

public class Base
{
    public int BaseField;

    /// <summary>
    /// Apply the state of the passed object to this object.       
    /// </summary>
    public virtual void ApplyState(Base obj)
    {
        BaseField = obj.BaseField;
    }
}

public class Derived : Base
{
    public int DerivedField;

    public override void ApplyState(Base obj)
    {
        var src = srcObj as Derived;

        if (src != null)
        {
            DerivedField = src.DerivedField;
        }

        base.ApplyState(srcObj);        
    }
}

Given any two objects that share type 'Base', you can apply A to B or B to A.

James
  • 1,973
  • 1
  • 18
  • 32
1

Based on mnyarar code, it would be necessary update the way to obtain the properties:

public static T Map<T, TU>(this T target, TU source)
{


    // list of writable properties of the destination
    List<PropertyInfo> tprops = typeof(T).GetTypeInfo().DeclaredProperties
                                        .Where(x => x.CanWrite == true).ToList();

    tprops.ForEach(prop =>
            {
                // check whether source object has the the property
                var sp = source.GetType().GetProperty(prop.Name);
                if (sp != null)
                {
                    // if yes, copy the value to the matching property
                    var value = sp.GetValue(source, null);
                    target.GetType().GetProperty(prop.Name).SetValue(target, value, null);
                }
            });
}
mainmind83
  • 491
  • 4
  • 7
0

I realize that a couple of other answers may have touched on this solution, but I wanted to spell it out more completely.

The solution I found was to populate the base class, then pass that base class into the constructor of the derived class. The constructor of the derived class populates its fields based on the base class.

class Base
{
    private string name; 
    public string Name { get; set; }
    private string address; 
    public string Address { get; set; }
}

class Derived:Base
{
    Derived(Base toCopy)
    {
        this.Name = toCopy.Name;
        this.Address = toCopy.Address;
    }

    private string field; 
    public String field { get; set; }
}

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Base b = new Base();
            b.Address = "Iliff";
            b.Name = "somename"; 

            //You are now passing the base class into the constructor of the derived class.
            Derived d = new Derived(b);


        }
    }
}
CWeaver
  • 41
  • 2
  • 9
0

I found EMIT may help you for this.

For we will spend too long in reflection but we can be fast in Emit.

  private static void CloneObjectWithIL<T>(T source, T los)
    {
        var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
        ILGenerator generator = dynamicMethod.GetILGenerator();

        foreach (var temp in typeof(T).GetProperties().Where(temp=>temp.CanRead&&temp.CanWrite))
        {
            generator.Emit(OpCodes.Ldarg_1);// los
            generator.Emit(OpCodes.Ldarg_0);// s
            generator.Emit(OpCodes.Callvirt,temp.GetMethod);
            generator.Emit(OpCodes.Callvirt, temp.SetMethod);
        }
        generator.Emit(OpCodes.Ret);
        var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
        clone(source, los);
    }

It can be use as this code:

public class Base
{
    public string BaseField;
}

public class Derived : Base
{
    public string DerivedField;
}

Base base = new Base();
//some alother code
Derived derived = new Derived();
CloneObjectWithIL(base, derived);

The more fast code is to cache it.

    // ReSharper disable once InconsistentNaming
    public static void CloneObjectWithIL<T>(T source, T los)
    {
        //See http://lindexi.oschina.io/lindexi/post/C-%E4%BD%BF%E7%94%A8Emit%E6%B7%B1%E5%85%8B%E9%9A%86/
        if (CachedIl.ContainsKey(typeof(T)))
        {
            ((Action<T, T>) CachedIl[typeof(T)])(source, los);
            return;
        }
        var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
        ILGenerator generator = dynamicMethod.GetILGenerator();

        foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
        {
            if (temp.GetAccessors(true)[0].IsStatic)
            {
                continue;
            }

            generator.Emit(OpCodes.Ldarg_1);// los
            generator.Emit(OpCodes.Ldarg_0);// s
            generator.Emit(OpCodes.Callvirt, temp.GetMethod);
            generator.Emit(OpCodes.Callvirt, temp.SetMethod);
        }
        generator.Emit(OpCodes.Ret);
        var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
        CachedIl[typeof(T)] = clone;
        clone(source, los);
    }

    private static Dictionary<Type, Delegate> CachedIl { set; get; } = new Dictionary<Type, Delegate>();
lindexi
  • 4,182
  • 3
  • 19
  • 65
0

There is a very nice tool called AutoMapper https://automapper.org/ It can be very useful to automatically map properties between classes. It may require simple setup to configure mapping between classes, but once done you will be able to map any classes between each other, and in your case code will look like this:

Base a;
var derived = _mapper.Map<Derived>(a);
Eramir
  • 482
  • 1
  • 5
  • 18
0

The original requirement/question was how to shallow copy properties from a baseclass into a derived class.

Below uses Reflection, although not the fastest one. An improved version of the mnyarar version.

public static void Map(this object target, object source)
{
    if (source == null || target == null)
    {
        return;
    }

    // list of writable properties of the destination
    var targetType = target.GetType();
    var sourceType = source.GetType();
    if (!sourceType.IsAssignableFrom(targetType))
    {
        throw new InvalidOperationException("Target type must be a subtype of sourceType");
    }
    foreach(var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public| BindingFlags.SetProperty ))
    {
        // check whether source object has the the property
        var sp = sourceType.GetProperty(prop.Name);
        if (sp != null)
        {
            // if yes, copy the value to the matching property
            var value = sp.GetValue(source, null);
            prop.SetValue(target, value, null);
        }
    };
}
Egbert Nierop
  • 2,066
  • 1
  • 14
  • 16
-7

Just change this.

Derived d = (Derived)b;

Also, your name data type should be string, not int

Joe Chen
  • 100
  • 7