将List<BaseClass>复制到List<DerivedClass>

32

考虑以下类定义:

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

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

我该如何将一个List<BaseClass>转换成一个List<DerivedClass>

在我的实际场景中,BaseClass有很多属性,我不想逐个复制它们(如果添加了其他属性,则需要记得维护)。

由于这个类是由WCF服务引用定义的,因此不能向BaseClass添加参数化构造函数。


WCF是否在代理代码中生成_partial_类? - DK.
再看一眼,您没有说DerivedClass是否仅是在基类上的功能扩展,还是实际上必须是不同的数据结构,正如代码示例所建议的那样。我认为,在第二种情况下,“转换”是没有意义的。 - DK.
7个回答

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

实际上,当您需要基于原始对象创建新对象时,ConvertAll很好用;当您只需要强制转换时,可以使用以下方法

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

如果你的列表中并非所有项都可以转换为DerivedClass,则使用OfType。

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

1
我会后悔诱惑你回到这里的,是吗? ;) - Jon Skeet
六年后...我不确定为什么我认为这不起作用,但只要listBaseClass中的每个对象都是DerivedClass的实例,它就完美地工作。 - Richard Ev
如果是子类,最好使用.Cast<DerivedClass>(),这样更易读,你觉得呢?感谢您的回复! - Peter Morris
Cast<T> 实际上返回的是 IEnumerable<T>,而不是 List<T>,因此这段代码无法编译。 - MakePeaceGreatAgain
@HimBromBeere 谢谢,我给这两行加上了.ToList()。 - Peter Morris

17

你不能转换实际对象,但是可以轻松创建一个包含转换后内容的新列表:

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

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

或者,如果您没有使用C# 3:

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

这假定原始列表实际上是由DerivedClass的实例充满的。如果不是这种情况,请更改委托以根据给定的BaseClass创建适当的DerivedClass实例。

编辑:我不确定为什么我没有发布一个LINQ解决方案:

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

1
这似乎不起作用。我认为这是因为你不能将基类转换为派生类? - Dan Bailiff
@DanBailiff:正如答案中所述:“这假设原始列表实际上充满了DerivedClass的实例。”在这种情况下,它确实有效。如果您认为它不起作用,您需要提供更多细节。 - Jon Skeet
@Jon 我认为你是 C# 无可争议的王者。 - gmail user
我个人最喜欢LINQ解决方案。 - Luca Cremonesi

3

(重复自此处)

首先,注意您可以在 WCF 类中添加构造函数(和其他代码)- 您只需要在一个分部类中进行(并保留生成的代码)。

听起来列表项的类型需要更改 - 因此我们不能简单地强制转换。反射是一种选择,但速度较慢。由于您使用的是 3.5,我们可以编写一个表达式(Expression)来更有效地进行操作... 沿着这些线路,但也使用第二个类:

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);
        }
    }
}

2
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; }
    }

}

这段代码不仅会转换,还会仅包含派生类的实例,并排除任何不是派生类的实例。


2
正如其他人所建议的那样,如果您有一个 List<BaseClass> 实例,可以使用 ListConvertAll 方法将其转换为 List<DerivedClass>
更一般地说,如果您有任何实现了 IEnumerable<T> 接口(没有 ConvertAll 方法),您可以使用 Linq 的 Cast 来完成相同的操作:
IEnumerable<DerivedClass> result = listBaseClass.Cast<DerivedClass>();

如果你需要返回一个List而不是IEnumerable,你可以在最后添加一个ToList()调用。
正如Jon所说的那样,这一切都假设listBaseClass中的所有条目实际上都是DerivedClass类型。

0

仅供记录,如果存在带参数的构造函数,则需要显式实例化列表中的每个项。

使用 OP 的示例:

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; }
}

在这种情况下,代码行应该是这样的:
List<DerivedClass> result = listBaseClass.ConvertAll(l=> new DerivedClass(l.Param1));

-1
如 Jon Skeet 所指出的,那些使用 ConvertAll(x => (DerivedClass)x) 的解决方案只适用于 List<BaseClass> 的内容实际上是 DerivedClass 的实例。
然而,如果 List<BaseClass> 包含 BaseClass 的实例,则需要为每个元素创建一个新的 DerivedClass 实例。这需要一个构造函数来允许这样做。
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));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接