如何通过基类单方法初始化不同ViewModel的各类属性?
解决方案:通过基类统一初始化ViewModel属性
核心思路
让所有ViewModel继承同一个基类,基类提供可复用的Initialize方法,区分无私有字段的依赖存储属性和带私有字段的资源属性,通过gameVersion参数控制资源加载逻辑,同时支持游戏重置和界面切换时的状态保留。
步骤实现
1. 定义基类ViewModel
基类集成INotifyPropertyChanged实现属性通知,包含通用属性的初始化逻辑,同时预留抽象方法供子类扩展:
public abstract class BaseViewModel : INotifyPropertyChanged { // 依赖存储属性(直接绑定全局状态,无需私有字段) public int CurrentGame { get => GameStats.CurrentGame; set { GameStats.CurrentGame = value; OnPropertyChanged(nameof(CurrentGame)); } } public int CurrentWord { get => GameStats.CurrentWord; set { GameStats.CurrentWord = value; OnPropertyChanged(nameof(CurrentWord)); } } // 带私有字段的资源属性(基类定义,子类继承) private string _imageSource; public string ImageSource { get => _imageSource; set { _imageSource = value; OnPropertyChanged(nameof(ImageSource)); } } private string _videoSource; public string VideoSource { get => _videoSource; set { _videoSource = value; OnPropertyChanged(nameof(VideoSource)); } } // 通用游戏状态属性 private int _level; public int Level { get => _level; set { _level = value; OnPropertyChanged(nameof(Level)); } } private int _lifes; public int Lifes { get => _lifes; set { _lifes = value; OnPropertyChanged(nameof(Lifes)); } } private int _score; public int Score { get => _score; set { _score = value; OnPropertyChanged(nameof(Score)); } } private int _bestScore; public int BestScore { get => _bestScore; set { _bestScore = value; OnPropertyChanged(nameof(BestScore)); } } // 命令属性(基类定义,子类实现具体逻辑) public ICommand NextPage { get; protected set; } public ICommand PreviousPage { get; protected set; } public ICommand UpdateStats { get; protected set; } // 统一初始化入口 public async Task Initialize(string gameVersion = "Bronze") { InitializeGameState(gameVersion); await InitializeResources(); InitializeCommands(); } // 根据游戏版本初始化状态 protected virtual void InitializeGameState(string gameVersion) { switch (gameVersion.ToLower()) { case "gold": Level = 3; Lifes = 5; break; case "silver": Level = 2; Lifes = 3; break; case "bronze": default: Level = 1; Lifes = 2; break; } Score = 0; BestScore = GameStats.GetBestScore(gameVersion); } // 初始化资源(子类可重写自定义逻辑) protected virtual async Task InitializeResources() { ImageSource = await GameLevels.GetImageSource(CurrentGame, CurrentWord); VideoSource = await GameLevels.GetVideoSource(CurrentGame, CurrentWord); } // 初始化命令(子类必须实现) protected abstract void InitializeCommands(); // INotifyPropertyChanged实现 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 子类ViewModel继承基类
子类只需重写InitializeCommands方法,实现页面特定的命令逻辑,无需重复编写初始化代码:
public class SpellViewModel : BaseViewModel { protected override void InitializeCommands() { NextPage = Navigation.NextPage(nameof(SpellPage)); PreviousPage = Navigation.PreviousPage(); UpdateStats = new Command(UpdateStatss); } private void UpdateStatss() { // 自定义业务逻辑 } }
3. 调用初始化方法
- 界面切换时:直接调用
Initialize(),依赖存储属性保留全局状态,资源和命令重新初始化 - Start New Game时:先重置全局状态,再传入指定版本调用初始化
// 开始新游戏 public async void StartNewGame(string selectedGameVersion) { GameStats.ResetCurrentGame(); await Initialize(selectedGameVersion); } // 界面加载时初始化 public async void OnPageLoaded() { await Initialize(); }
4. 修复之前的泛型方法问题
你之前的泛型方法抛出NullReferenceException,是因为methodInfo.Invoke的第一个参数应该是ViewModel实例,而非methodInfo本身。正确的反射调用方式如下(不推荐,优先用继承方案):
public async Task InitializeLevelReaderResources(object viewModelInstance, int currentGame, int currentWord, string gameVersion = "bronze") { var method = viewModelInstance.GetType().GetMethod("InitializeLevelReaderResources", BindingFlags.NonPublic | BindingFlags.Instance); if (method != null) { await (Task)method.Invoke(viewModelInstance, new object[] { currentGame, currentWord, gameVersion }); } }
关键优势
- 消除重复代码:所有ViewModel共享基类初始化逻辑,迭代时只需修改基类
- 灵活扩展:子类可重写特定方法实现自定义逻辑
- 状态清晰:依赖存储属性保留全局状态,私有字段属性按需初始化
- 易维护:统一初始化入口,降低后续版本迭代的维护成本
内容的提问来源于stack exchange,提问作者ThisQRequiresASpecialist
相关产品推荐
相关产品推荐

