Reputation: 54097
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
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
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
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
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
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
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
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