You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

派生类显式转换为基类失败问题求助

问题分析与解决方案

首先得明确一个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:38:00