Richard Ev
Richard Ev

Reputation: 54097

Copying a List<BaseClass> to List<DerivedClass>

Given the following class definitions:

public class BaseClass
{
    public string SomeProp1 { get; set; }
}

public class DerivedClass : BaseClass
{
    public string SomeProp2 { get; set; }
}

How can I take a List<BaseClass> and convert it to a List<DerivedClass>?

In my real-world scenario BaseClass has a whole bunch of properties that I don't want to have to copy over one-by-one (and then remember to maintain if an additional property gets added).

Adding a parameterised constructor to BaseClass is not an option as this class is defined by a WCF service reference.

Upvotes: 32

Views: 17759

Answers (7)

Paul Masri-Stone
Paul Masri-Stone

Reputation: 3139

As pointed out by Jon Skeet, solutions that use ConvertAll(x => (DerivedClass)x) will only work if the contents of List<BaseClass> are actually instances of DerivedClass.

However if List<BaseClass> contains instances of BaseClass, then you need to create a new DerivedClass instance for each element. This requires a constructor to allow this.

public class BaseClass
{
    protected int _intField;
    protected string _stringField;

    public string SomeProp1 { get; set; }
}

public class DerivedClass : BaseClass
{
    public string SomeProp2 { get; set; }

    public DerivedClass(BaseClass b)
    {
        _intField = b._intField;
        _stringField = b._stringField;
        SomeProp1 = b.SomeProp1;

        SomeProp2 = String.Empty;
    }
}

...

var source = new List<BaseClass>();
//... add elements of type BaseClass

var derived = source.ConvertAll(x => new DerivedClass(x));

Upvotes: -1

ehh
ehh

Reputation: 3480

Just for the record, in case there is a constructor with parameters, you need to explicitly instantiate each item in the list.

Using OP example:

public class BaseClass
{
    public BaseClass (string param1)
    {
         Param1 = param1;
    }
    public string SomeProp1 { get; set; }
    public string Param1 { get; private set; }
}

public class DerivedClass : BaseClass
{
    public DerivedClass (string param1) : base(param1){}
    public string SomeProp2 { get; set; }
}

In this case, the line of code should be something like:

List<DerivedClass> result = listBaseClass.ConvertAll(l=> new DerivedClass(l.Param1));

Upvotes: 0

Peter Morris
Peter Morris

Reputation: 23224

List<DerivedClass> result = 
    listBaseClass.ConvertAll(instance => (DerivedClass)instance);

Actually ConvertAll is good when you need to create new objects based on the original, when you just need to cast you can use the following

List<DerivedClass> result = 
    listBaseClass.Cast<DerivedClass>().ToList();

If not all of the items in your list can be cast to DerivedClass then use OfType instead

List<DerivedClass> result =
    listBaseClass.OfType<DerivedClass>().ToList();

Upvotes: 37

Jon Skeet
Jon Skeet

Reputation: 1500215

You can't convert the actual object, but it's easy to create a new list with the converted contents:

List<BaseClass> baseList = new List<BaseClass>(...);
// Fill it here...

List<DerivedClass> derivedList = baseList.ConvertAll(b => (DerivedClass) b);

Or if you're not using C# 3:

List<DerivedClass> derivedList = baseList.ConvertAll<DerivedClass>(delegate
    (BaseClass b) { return (DerivedClass) b; };

This assumes that the original list was actually full of instances of DerivedClass. If that's not the case, change the delegate to create an appropriate instance of DerivedClass based on the given BaseClass.

EDIT: I'm not sure why I didn't just post a LINQ solution:

List<DerivedClass> derivedList = baseList.Cast<DerivedClass>().ToList();

Upvotes: 17

AwesomeTown
AwesomeTown

Reputation: 2880

As others have suggested, if you have an instance of a List<BaseClass> you can convert it to a List<DerivedClass> using the ConvertAll method of List.

More generally, if you have anything that implements IEnumerable<T> (which doesn't have a ConvertAll method) you can use Linq's Cast to do the same thing:

IEnumerable<DerivedClass> result = listBaseClass.Cast<DerivedClass>();

If you need an a List back instead of an IEnumerable, you can just tack a call to ToList() on the end.

As Jon said, though, that's all assuming that all of the entries in listBaseClass are actually of type DerivedClass.

Upvotes: 2

Peter Morris
Peter Morris

Reputation: 23224

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace ConsoleApplication22
{
    class Program
    {
        static void Main(string[] args)
        {
            List<BaseClass> source = new List<BaseClass>();
            source.Add(new DerivedClass { Name = "One" });
            source.Add(new BaseClass());
            source.Add(new DerivedClass { Name = "Three" });

            List<DerivedClass> result =
                new List<DerivedClass>(source.OfType<DerivedClass>());
            result.ForEach(i => Console.WriteLine(i.Name));
            Console.ReadLine();
        }
    }

    public class BaseClass
    {
    }

    public class DerivedClass : BaseClass
    {
        public string Name { get; set; }
    }

}

This code will not only convert, but also only include instances that ARE the derived class and exclude any that are not.

Upvotes: 2

Marc Gravell
Marc Gravell

Reputation: 1062660

(repeated from here)

First - note that you can add constructors (and other code) to WCF classes - you just need to do it in a partial class (and leave the generated code alone).

It sounds like the type of the items in the list need to be changed - so we can't just cast. Reflection is an option, but is slow. Since you are using 3.5, we can perhaps write an Expression to do it for us more efficiently... along these lines, but using the second class too:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
static class Program
{
    class Foo
    {
        public int Value { get; set; }
        public override string ToString()
        {
            return Value.ToString();
        }
    }
    class Bar : Foo {}
    static void Main()
    {
        List<Foo> foos = new List<Foo>();
        for (int i = 0; i < 10; i++) foos.Add(new Foo { Value = i });

        List<Bar> bars = foos.ConvertAll<Bar>(Clone<Foo, Bar>);
    }
    public static TTo Clone<TFrom, TTo>(this TFrom obj) where TTo : TFrom, new()
    {
        return ObjectExtCache<TFrom, TTo>.Convert(obj);
    }
    static class ObjectExtCache<TFrom, TTo> where TTo : TFrom, new()
    {
        private static readonly Func<TFrom, TTo> converter;
        static ObjectExtCache()
        {
            ParameterExpression param = Expression.Parameter(typeof(TFrom), "in");
            var bindings = from prop in typeof(TFrom).GetProperties()
                           where prop.CanRead && prop.CanWrite
                           select (MemberBinding)Expression.Bind(prop,
                               Expression.Property(param, prop));
            converter = Expression.Lambda<Func<TFrom, TTo>>(
                Expression.MemberInit(
                    Expression.New(typeof(TTo)), bindings), param).Compile();
        }
        public static TTo Convert(TFrom obj)
        {
            return converter(obj);
        }
    }
}

Upvotes: 3

Related Questions