tags:

views:

425

answers:

5

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.

A: 
List<DerivedClass> result = 
    listBaseClass.ConvertAll(instance => (DerivedClass)instance);
Peter Morris
I'm going to regret tempting you back here, aren't I? ;)
Jon Skeet
I learned that from your book :-)
Peter Morris
That does not appear to work...
Richard Ev
"Does not appear to work", I'll bet you expect better bug reports from your users don't you?using System.Linq; Do you have that?
Peter Morris
+3  A: 

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.

Jon Skeet
+2  A: 

(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);
        }
    }
}
Marc Gravell
A: 
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.

Peter Morris
A: 

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.

John Price