我的同事告诉我,可以将
List<BaseClass>
转换为 List<DerivedClass>
(他们没有向我展示工作示例)。我认为不可能将父类转换为子类(但我确实知道可以反过来做)。我看到了几个相关的问题,有人说它不能完成,有人说它可以 - 所提出的解决方案对我来说根本不起作用。我总是得到:
System.InvalidCastException.
Unable to cast object of type 'BaseClass' to 'ChildClass'
这绝对可能吗?如果是这样,我做错了什么?这是我的代码(为了这个问题的目的而简化):
家长班:
public class ParentClass
{
public string Name = "";
}
少儿班:
public class ChildClass: ParentClass
{
public string Date = "";
}
转换:
List<ParentClass> parentList = ServiceApi.GetParentClassList().ToList();
List<ChildClass> childList = parentList.Cast<ChildClass>().ToList();
我的实际代码要复杂得多,但应该应用相同的原则。
我的同事告诉我,可以将
转换为List<BaseClass>
(他们没有向我展示工作示例)。我没想到可以将父类转换为子类List<DerivedClass>
从某种意义上来说,你们都是对的。
假设您有类型
Animal
、Cat
(继承 Animal)和 Dog
(继承 Animal)。 List<Animal>
可以同时包含猫和狗,但 List<Dog>
不能包含猫。如果您有一个同时包含 Cat 和 Dog 对象的 List<Animal>
,那么无论您做什么都不会让您生成包含原始列表中所有内容的 List<Dog>
。
所以从这个意义上说,你是对的,你不能从
List<DerivedClass>
产生 List<ParentClass>
。
但有时,作为程序员,您拥有编译器无法获得的有关如何使用对象的信息。如果(且仅当)您可以知道特定的
List<Animal>
实例仅包含 Dog
对象,您始终可以编写如下内容:
List<Dog> dogList = myAnimalListWithOnlyDogs.Cast<Dog>().ToList();
再次强调:只有当您可以保证列表实例中的每个对象都可以转换为 Dog 对象时,这才有效。如果 Cat 对象以某种方式进入您的列表,您将在运行时遇到异常。尽管如此,这样的代码并不罕见。
从这个意义上来说,这位同事是对的。当涉及到真正的工作代码时,人们无论如何都会取得很好的效果,并且很好地摆脱它,尽管纯粹主义者可能会认为这是一种代码味道,表明需要使用组合而不是继承。
此外,有时
List<Animal>
实例可能同时包含 Cat 和 Dog 对象,而您只关心 Dog
对象。在这种情况下,你可以这样做:
List<Dog> dogList = myAnimalList.OfType<Dog>().ToList();
我经常看到这种模式的使用,尤其是在使用 WinForms 控件集时。
在上面的两个示例中,您正在使用“新序列”。在新序列中添加或删除对象不会改变原始序列。从这个意义上说,您“没有”转换原始文件,而是创建了替代品。但是,该序列包含相同的对象,因此在新序列中编辑 Dog 对象的属性将更改原始序列中的该对象,因为它是相同的对象实例。
Cast<T>
ParentClass p = ...
ChildClass c = (ChildClass)p;
除非为
p
分配了
ChildClass
或其子类之一的实例,否则这将不起作用。在您的情况下,从服务器 API 返回的数据似乎包含
ParentClass
或其子类之一而不是 ChildClass
的对象。假设您从服务器获得了足够的信息,您可以通过构造
ChildClass
实例来解决此问题:
List<ChildClass> childList = parentList
.Select(parent => new ChildClass(parent.Name, ... /* the remaining fields */))
.ToList();
如果您愿意在施放失败时丢失物品,可以使用
OfType()
var children = parentList.OfType<ChildClass>().ToList();
未经测试,但这应该有效:
如果没有帮助,从基类到派生类是不可能的。我使用反射来解决这个问题。在派生类中包含一个使用反射从基类填充的函数。
public class DerivedClass : BaseClass
{
public DerivedClass PopulateBaseClass(object baseClass)
{
var parentProperties = baseClass.GetType().GetProperties();
var childProperties = this.GetType().GetProperties();
foreach (var parentProperty in parentProperties)
{
foreach (var childProperty in childProperties)
{
if (parentProperty.Name == childProperty.Name && parentProperty.PropertyType == childProperty.PropertyType)
{
childProperty.SetValue(this, parentProperty.GetValue(baseClass));
break;
}
}
}
return this;
}
public string SomeDerivedClassProperty { get; set; }
}
将 BaseClass 列表转换为 DerivedClass 列表,如下所示
List<BaseClass> items = new List<BaseClass>();
// populate some base class items
List<DerivedClass> derivedItems = items.Select(i => new DerivedClass().PopulateBaseClass(i)).ToList();
或者,如果您的派生类未使用
JsonProperty
定义备用属性名称,您可以使用 Json 来处理繁重的工作
List<DerivedClass> derivedItems = JsonConvert.DeserializeObject<List<DerivedClass>>(JsonConvert.SerializeObject(items));