派生类显式转换为基类失败问题求助
首先得明确一个C#核心概念:引用类型的类型转换只是改变了编译时的类型标识,运行时对象的实际类型不会发生变化。你把PlanningListViewModel实例转成PlanningListModel,只是告诉编译器“把这个对象当作基类来处理”,但内存里它依然是PlanningListViewModel实例,所以调用Plannings属性时,还是会执行派生类里重写的get方法——这就是你遇到问题的根本原因。
为什么类型转换没用?
看你这段代码:
PlanningListViewModel A = new PlanningListViewModel (); PlanningListModel B = (PlanningListModel)A; typeof(B); // 返回PlanningListViewModel
typeof(B)返回的是对象的运行时类型,因为B引用的本质还是PlanningListViewModel实例。类型转换不会改变对象本身,只是改变了变量的编译类型标识而已。
另外还要注意:你的PlanningListViewModel里重复定义了和基类一模一样的私有字段(_date、_Plannings、_isSaving),这会导致基类和派生类各有一套独立的字段,极易引发数据不一致问题——比如你在ViewModel里修改_Plannings,基类的_Plannings完全不会同步,这是严重的设计缺陷,必须去掉ViewModel里重复的私有字段,直接复用基类的字段。
解决方案
根据你的核心需求(序列化时获取基类原始的Plannings数据),推荐以下几种可行方案:
方案1:避免重写基类属性,新增过滤专用属性
这是最符合设计原则的做法,清晰区分“原始数据”和“过滤后的数据”:
public class PlanningListModel : INotifyPropertyChanged { private DateTime _date; private List<PlanningEntryModel> _Plannings; private bool _isSaving = false; // 原始数据,专供序列化使用 public List<PlanningEntryModel> Plannings => _Plannings; // 其他属性和INotifyPropertyChanged实现... } public class PlanningListViewModel : PlanningListModel, INotifyPropertyChanged { // 去掉所有重复的私有字段,直接复用基类的成员 // 新增过滤后的属性,用于UI或业务逻辑展示 public List<PlanningEntryModel> FilteredPlannings { get { if (App.Data == null || App.Data.User == null || App.Data.IsReplicating) return base.Plannings.ToList(); // 根据需求决定是否复制列表 switch (App.Data.CurrentList) { case 1: return base.Plannings.FindAll(x => x.Volonter.ID == App.Data.User.ID); case 2: return base.Plannings.FindAll(x => x.User.Referent == App.Data.User.ID); default: return base.Plannings.ToList(); } } } // 其他ViewModel专属逻辑... }
这样序列化时直接用基类的Plannings就能拿到原始数据,ViewModel里用FilteredPlannings做逻辑过滤,完全互不干扰。
方案2:在基类提供原始字段的访问入口
如果必须保留重写属性的结构,可以在基类里加一个受保护的入口,让序列化逻辑能直接获取原始数据:
public class PlanningListModel : INotifyPropertyChanged { private List<PlanningEntryModel> _Plannings; // 供序列化或派生类调用的原始数据入口 protected List<PlanningEntryModel> RawPlannings => _Plannings; // 对外的可重写属性 public virtual List<PlanningEntryModel> Plannings => _Plannings; // 其他代码... } public class PlanningListViewModel : PlanningListModel { public override List<PlanningEntryModel> Plannings { get { // 你的过滤逻辑... } } }
序列化时,不要直接调用Plannings,而是通过RawPlannings获取原始数据(如果序列化工具支持访问受保护成员,或者你可以写一个专门的序列化方法)。
方案3:手动创建基类实例并复制数据
如果你需要一个真正的PlanningListModel实例(而非派生类的引用),可以手动复制基类的字段值:
PlanningListViewModel A = new PlanningListViewModel(); // 创建全新的基类实例 PlanningListModel B = new PlanningListModel { Date = A.Date, // 用基类的原始字段入口获取数据,避免引用共享可以复制一份 Plannings = A.RawPlannings.ToList(), IsSaving = A.IsSaving }; typeof(B); // 现在返回PlanningListModel
这种方式的缺点是需要手动维护字段复制,后续基类新增字段时要同步修改,适合字段较少的场景。
内容的提问来源于stack exchange,提问作者jack

