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

如何通过基类单方法初始化不同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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:40:19