如何仅在ReactiveCollection中PlayerBuildings指定属性变更时订阅?
精准订阅ReactiveCollection的特定属性变化
当然可以实现!我们可以借助ReactiveExtensions(Rx)的操作符,精准过滤出你关心的两类变化信号,避免不必要的计算和订阅触发。核心思路是只监听与目标资源相关的属性变更,然后在这些变更发生时重新计算生产总和。
关键逻辑拆解
我们需要捕捉三类触发更新的场景:
- 任意建筑的
ProducedResourceId被设置为myResource(或从myResource改为其他值,因为这也会影响总和) - 已经生产
myResource的建筑,其ProductionPerHour发生变化 - 集合中添加/移除了生产
myResource的建筑(这一步容易被忽略,但同样会影响总和)
伪代码实现(基于ReactiveUI)
假设你的PlayerBuilding是继承自ReactiveObject的响应式对象(这是ReactiveUI的标准做法):
// 定义PlayerBuilding类 public class PlayerBuilding : ReactiveObject { private string _producedResourceId; public string ProducedResourceId { get => _producedResourceId; set => this.RaiseAndSetIfChanged(ref _producedResourceId, value); } private double _productionPerHour; public double ProductionPerHour { get => _productionPerHour; set => this.RaiseAndSetIfChanged(ref _productionPerHour, value); } } // 你的ViewModel中的核心逻辑 public class YourViewModel : ReactiveObject { public ReactiveCollection<PlayerBuilding> PlayerBuildings { get; } = new(); public string MyResource { get; } = "wood"; // 你的目标资源ID private string _textValue; public string TextValue { get => _textValue; set => this.RaiseAndSetIfChanged(ref _textValue, value); } public YourViewModel() { // 构建只包含我们关心的变化的可观察流 var relevantChangeSignals = Observable.Merge( // 监听集合的添加/移除操作(避免漏掉新加入或移除的目标资源建筑) PlayerBuildings.Changed.Select(_ => Unit.Default), // 遍历集合中每个建筑,监听其关键属性变化 PlayerBuildings.SelectMany(building => Observable.Merge( // 场景1:ProducedResourceId变更,且变更涉及目标资源 building.WhenAnyValue(b => b.ProducedResourceId) .Skip(1) // 跳过初始值,只处理后续的变更 .Where(newId => newId == MyResource || building.ProducedResourceId == MyResource) .Select(_ => Unit.Default), // 场景2:生产目标资源的建筑,其ProductionPerHour变更 building.WhenAnyValue(b => b.ProductionPerHour) .Skip(1) .Where(_ => building.ProducedResourceId == MyResource) .Select(_ => Unit.Default) )) ); // 订阅信号,计算总和并更新TextValue relevantChangeSignals .Throttle(TimeSpan.FromMilliseconds(10)) // 可选:防止短时间多次变更导致频繁计算 .Select(_ => CalculateTotalProduction()) .Subscribe(total => TextValue = $"总生产:{total:N2}/小时"); } // 计算目标资源的总生产效率 private double CalculateTotalProduction() { return PlayerBuildings .Where(b => b.ProducedResourceId == MyResource) .Sum(b => b.ProductionPerHour); } }
代码说明
Observable.Merge:把多个独立的信号流合并成一个统一的流,方便订阅。PlayerBuildings.SelectMany:遍历集合中的每个建筑,为每个建筑创建其属性变化的监听流。Skip(1):跳过属性的初始值,只响应后续的变更操作(避免初始化时的不必要计算)。Where:精准过滤出我们关心的变化场景,确保只有相关变更才会触发总和计算。Throttle:可选操作,用于防抖,避免短时间内多次修改导致频繁计算总和(比如用户快速调整多个建筑的生产效率时)。
如果你的项目没有使用ReactiveUI,只要你的集合和属性支持Rx的可观察特性,核心逻辑是完全通用的——只需要调整属性变更的监听方式即可。
内容的提问来源于stack exchange,提问作者Malv20
相关产品推荐
相关产品推荐

