Skip to content

Feat/state management core#132

Merged
GeWuYou merged 5 commits into
mainfrom
feat/state-management-core
Mar 23, 2026
Merged

Feat/state management core#132
GeWuYou merged 5 commits into
mainfrom
feat/state-management-core

Conversation

@GeWuYou

@GeWuYou GeWuYou commented Mar 23, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

引入集中式状态管理核心,包括 Store、reducer、中间件和选择工具,并将其集成到核心抽象层、文档和测试中。

New Features:

  • 添加集中式状态容器 Store<TState>,提供 dispatch、reducers、中间件管线、选择结果缓存以及诊断功能。
  • 引入状态管理抽象,包括 IReadonlyStoreIStoreIReducerIStoreMiddlewareIStateSelectorIStoreBuilder,以及与 dispatch 相关的上下文/记录类型。
  • 提供 StoreBuilder<TState> 用于模块化地配置和构建 Store。
  • 添加 StoreSelection<TState,TSelected>StoreExtensions,将 Store 桥接为基于 selector 的视图以及类似 BindableProperty 风格的只读视图。
  • 新增 State Management 文档页面,并从属性、命令和核心包文档中增加交叉链接,包括使用指南和示例。

Enhancements:

  • 更新现有文档,澄清在何种情况下使用 BindableProperty,何种情况下使用 Store/StateMachine,以及描述命令如何与基于 Store 的模型交互。
  • 扩展核心 README 和包索引,列出新的 StateManagement 模块及其职责。

Tests:

  • 为 Store 行为、中间件、选择、BindableProperty 桥接、诊断以及 StoreBuilder 配置添加全面的单元测试。
Original summary in English

Summary by Sourcery

Introduce a centralized state management core with Store, reducers, middleware, and selection utilities, integrating it into the core abstractions, documentation, and tests.

New Features:

  • Add Store centralized state container with dispatch, reducers, middleware pipeline, selection caching, and diagnostics.
  • Introduce state management abstractions including IReadonlyStore, IStore, IReducer, IStoreMiddleware, IStateSelector, IStoreBuilder, and dispatch context/record types.
  • Provide StoreBuilder for modular Store configuration and construction.
  • Add StoreSelection<TState,TSelected> and StoreExtensions to bridge Store into selector-based and BindableProperty-style read-only views.
  • Add a new State Management documentation page and cross-links from property, command, and core package docs, including usage guidance and examples.

Enhancements:

  • Update existing docs to clarify when to use BindableProperty versus Store/StateMachine and to describe command interaction with Store-based models.
  • Extend core README and package index to list the new StateManagement module and its responsibilities.

Tests:

  • Add comprehensive unit tests for Store behavior, middleware, selection, BindableProperty bridging, diagnostics, and StoreBuilder configuration.

GeWuYou added 3 commits March 23, 2026 19:34
- 实现 Store 类作为集中式状态容器,默认支持状态归约和订阅通知
- 添加 IReadonlyStore、IStore、IReducer 等状态管理相关抽象接口
- 实现 StoreExtensions 扩展方法,提供 Select 和 ToBindableProperty 选择器功能
- 添加 StoreSelection 类,支持从完整状态树中投影局部状态视图
- 实现 StoreDispatchContext 和 StoreDispatchRecord 用于分发过程诊断
- 添加 IStoreMiddleware 中间件接口,支持在分发过程中插入日志和审计逻辑
- 实现完整的状态选择器和绑定属性桥接功能,便于现有 UI 代码复用
- 添加 Store 相关单元测试,覆盖状态归约、订阅通知和选择器桥接场景
- 新增 state-management 文档,介绍集中式状态容器方案
- 在 property 文档中补充与 Store 的使用边界说明
- 更新核心功能表格,添加状态管理条目链接
- 在 README 中增加 StateManagement 功能描述
- 添加状态管理相关接口到抽象层文档
- 提供 Store 与 BindableProperty 的选择指导原则
- 引入 StoreBuilder<TState> 支持模块化配置 reducer 和中间件
- 实现状态选择视图缓存机制提升性能
- 重构订阅管理使用精确订阅对象替代委托链
- 增强 SubscribeWithInitValue 方法防止状态变化遗漏
- 添加完整的状态管理文档示例和测试用例
- 更新接口定义支持新的构建器功能
@sourcery-ai

sourcery-ai Bot commented Mar 23, 2026

Copy link
Copy Markdown

审阅者指南

引入一个新的集中式状态管理核心(Store),包括抽象、实现、扩展、测试和文档,并将其与现有的 Property、Command 以及文档结构集成,同时不破坏现有的 BindableProperty 用法。

带有中间件、reducer 和订阅者的 Store 派发流水线时序图

sequenceDiagram
    participant Caller
    participant Store as Store_TState_
    participant Middleware1 as Middleware1
    participant Middleware2 as Middleware2
    participant Reducer1 as Reducer1
    participant Reducer2 as Reducer2
    participant Subscriber as Subscriber

    Caller->>Store: Dispatch_TAction_(action)
    activate Store
    Store->>Store: EnsureNotDispatching()
    Store->>Store: Create StoreDispatchContext_TState_

    Store->>Middleware1: Invoke(context, next)
    activate Middleware1
    Middleware1->>Middleware2: Invoke(context, next)
    activate Middleware2

    Middleware2->>Reducer1: Reduce(currentState, action)
    activate Reducer1
    Reducer1-->>Middleware2: nextState1
    deactivate Reducer1

    Middleware2->>Reducer2: Reduce(nextState1, action)
    activate Reducer2
    Reducer2-->>Middleware2: nextState2
    deactivate Reducer2

    Middleware2->>Store: update context.NextState, HasStateChanged
    deactivate Middleware2

    Middleware1-->>Store: after next()
    deactivate Middleware1

    Store->>Store: compare PreviousState vs NextState
    alt state changed
        Store->>Store: update _state and diagnostics
        Store->>Store: SnapshotListenersForNotification()
        Store-->>Subscriber: onValueChanged(newState)
    else no change
        Store-->>Caller: return
    end

    deactivate Store
Loading

Controller–Command–Model–Store UI 更新流程时序图

sequenceDiagram
    actor User
    participant Controller as PlayerPanelController
    participant Command as DamagePlayerCommand
    participant Model as PlayerPanelModel
    participant Store as Store_PlayerPanelState_
    participant Selection as StoreSelection_PlayerPanelState_int_
    participant View as UI

    User->>Controller: OnDamageButtonClicked()
    Controller->>Command: new DamagePlayerCommand(amount)
    Controller->>Command: Execute()
    activate Command
    Command->>Model: GetModel_PlayerPanelModel_()
    Command->>Store: Dispatch_DamagePlayerAction_(action)
    deactivate Command

    Store-->>Store: apply reducers
    Store-->>Store: update PlayerPanelState

    Store-->>Selection: OnStoreChanged(state)
    Selection-->>View: onValueChanged(health)

    Controller->>Model: subscribe to Name, Health, HealthPercent
    Model->>Selection: GetOrCreateBindableProperty for each key
    Selection-->>Controller: RegisterWithInitValue callbacks
    Controller-->>View: initial render with current values
Loading

新状态管理核心(Store 及相关抽象)的类图

classDiagram
    direction LR

    class IReadonlyStore_TState_ {
        <<interface>>
        +TState State
        +IUnRegister Subscribe(listener)
        +IUnRegister SubscribeWithInitValue(listener)
        +void UnSubscribe(listener)
    }

    class IStore_TState_ {
        <<interface>>
        +void Dispatch(action)
    }

    class IStoreDiagnostics_TState_ {
        <<interface>>
        +int SubscriberCount
        +Type LastActionType
        +DateTimeOffset LastStateChangedAt
        +StoreDispatchRecord_TState_ LastDispatchRecord
    }

    class IReducer_TState_TAction_ {
        <<interface>>
        +TState Reduce(currentState, action)
    }

    class IStoreMiddleware_TState_ {
        <<interface>>
        +void Invoke(context, next)
    }

    class IStateSelector_TState_TSelected_ {
        <<interface>>
        +TSelected Select(state)
    }

    class IStoreBuilder_TState_ {
        <<interface>>
        +IStoreBuilder_TState_ WithComparer(comparer)
        +IStoreBuilder_TState_ AddReducer_TAction_(reducer)
        +IStoreBuilder_TState_ AddReducerFunc_TAction_(reducer)
        +IStoreBuilder_TState_ UseMiddleware(middleware)
        +IStore_TState_ Build(initialState)
    }

    class Store_TState_ {
        +TState State
        +int SubscriberCount
        +Type LastActionType
        +DateTimeOffset LastStateChangedAt
        +StoreDispatchRecord_TState_ LastDispatchRecord
        +Store_TState_(initialState, comparer)
        +IUnRegister Subscribe(listener)
        +IUnRegister SubscribeWithInitValue(listener)
        +void UnSubscribe(listener)
        +void Dispatch_TAction_(action)
        +Store_TState_ RegisterReducer_TAction_(reducer)
        +Store_TState_ RegisterReducerFunc_TAction_(reducer)
        +Store_TState_ UseMiddleware(middleware)
        +StoreSelection_TState_TSelected_ GetOrCreateSelection_TSelected_(key, selector, comparer)
        +StoreSelection_TState_TSelected_ GetOrCreateBindableProperty_TSelected_(key, selector, comparer)
        +static StoreBuilder_TState_ CreateBuilder()
    }

    class StoreSelection_TState_TSelected_ {
        +TSelected Value
        +StoreSelection_TState_TSelected_(store, selector, comparer)
        +IUnRegister Register(onValueChanged)
        +IUnRegister RegisterWithInitValue(action)
        +void UnRegister(onValueChanged)
    }

    class StoreBuilder_TState_ {
        +StoreBuilder_TState_ WithComparer(comparer)
        +StoreBuilder_TState_ AddReducer_TAction_(reducer)
        +StoreBuilder_TState_ AddReducerFunc_TAction_(reducer)
        +StoreBuilder_TState_ UseMiddleware(middleware)
        +IStore_TState_ Build(initialState)
    }

    class StoreDispatchContext_TState_ {
        +object Action
        +Type ActionType
        +TState PreviousState
        +TState NextState
        +bool HasStateChanged
        +DateTimeOffset DispatchedAt
        +StoreDispatchContext_TState_(action, previousState)
    }

    class StoreDispatchRecord_TState_ {
        +object Action
        +Type ActionType
        +TState PreviousState
        +TState NextState
        +bool HasStateChanged
        +DateTimeOffset DispatchedAt
        +StoreDispatchRecord_TState_(action, previousState, nextState, hasStateChanged, dispatchedAt)
    }

    class StoreExtensions {
        <<static>>
        +StoreSelection_TState_TSelected_ Select_TState_TSelected_(store, selector)
        +StoreSelection_TState_TSelected_ SelectWithComparer_TState_TSelected_(store, selector, comparer)
        +StoreSelection_TState_TSelected_ SelectWithStateSelector_TState_TSelected_(store, selector, comparer)
        +IReadonlyBindableProperty_TSelected_ ToBindableProperty_TState_TSelected_(store, selector, comparer)
    }

    class IReadonlyBindableProperty_TSelected_ {
        <<interface>>
        +TSelected Value
        +IUnRegister Register(onValueChanged)
        +IUnRegister RegisterWithInitValue(action)
        +void UnRegister(onValueChanged)
    }

    class IUnRegister {
        <<interface>>
        +void UnRegister()
    }

    Store_TState_ ..> StoreDispatchContext_TState_ : uses
    Store_TState_ ..> StoreDispatchRecord_TState_ : creates
    Store_TState_ ..> IReducer_TState_TAction_ : uses
    Store_TState_ ..> IStoreMiddleware_TState_ : uses

    Store_TState_ ..> StoreSelection_TState_TSelected_ : creates

    StoreSelection_TState_TSelected_ ..> IReadonlyStore_TState_ : reads

    StoreExtensions ..> StoreSelection_TState_TSelected_ : creates
    StoreSelection_TState_TSelected_ ..|> IReadonlyBindableProperty_TSelected_

    Store_TState_ ..|> IStore_TState_
    Store_TState_ ..|> IReadonlyStore_TState_
    Store_TState_ ..|> IStoreDiagnostics_TState_

    IStore_TState_ ..|> IReadonlyStore_TState_

    StoreBuilder_TState_ ..|> IStoreBuilder_TState_

    StoreDispatchRecord_TState_ <.. IStoreDiagnostics_TState_ : exposes

    NoteForLegend <|-- Store_TState_ : concrete
    NoteForLegend2 <|-- StoreSelection_TState_TSelected_ : concrete
Loading

文件级变更

Change Details Files
添加一个功能完备的集中式状态容器实现,支持 reducers、中间件、诊断信息、缓存选择器以及订阅语义。
  • 实现泛型 Store<TState>,包含派发流水线、reducer 注册表、中间件链以及状态相等比较器支持。
  • 实现健壮的订阅模型(Subscribe / SubscribeWithInitValue),带有待处理状态的处理逻辑,以避免遗漏更新,并防止在同一个 Store 上发生嵌套派发。
  • 添加选择器缓存和类似 Bindable 的投影(GetOrCreateSelection / GetOrCreateBindableProperty),以及内部的 reducer 适配器和监听者订阅类型。
GFramework.Core/StateManagement/Store.cs
引入 Store 的选择/只读绑定层和扩展方法,将 Store 与现有的 BindableProperty 风格 API 进行桥接。
  • 实现 StoreSelection<TState,TSelected>,作为由 IReadonlyStore<TState> 支持的 IReadonlyBindableProperty<TSelected>,包含监听者管理以及与 Store 一致的 SubscribeWithInitValue 语义。
  • 确保 StoreSelection 仅在存在监听者时才附着/分离到底层 Store,并正确批量处理通知以避免死锁和遗漏更新。
  • 提供 StoreExtensions,用于选择切片、使用自定义比较器或 IStateSelector 实现,并暴露 ToBindableProperty 以实现 UI 兼容。
GFramework.Core/StateManagement/StoreSelection.cs
GFramework.Core/Extensions/StoreExtensions.cs
在抽象层中添加构建器、中间件、选择器、诊断和 Store 抽象,以及派发上下文/记录类型。
  • 定义 IReadonlyStore<TState>IStore<TState>IReducer<TState,TAction>IStoreMiddleware<TState>IStateSelector<TState,TSelected>IStoreBuilder<TState>IStoreDiagnostics<TState> 接口,用于状态管理。
  • 添加 StoreDispatchContext<TState>,用于在中间件流水线中传递 action、状态和时间信息,并添加 StoreDispatchRecord<TState> 用于暴露最后一次派发的诊断数据。
  • 更新 Core README 和 Abstractions README,使其提及新的状态管理接口和能力。
GFramework.Core.Abstractions/StateManagement/IReadonlyStore.cs
GFramework.Core.Abstractions/StateManagement/IStore.cs
GFramework.Core.Abstractions/StateManagement/IReducer.cs
GFramework.Core.Abstractions/StateManagement/IStoreMiddleware.cs
GFramework.Core.Abstractions/StateManagement/IStateSelector.cs
GFramework.Core.Abstractions/StateManagement/IStoreBuilder.cs
GFramework.Core.Abstractions/StateManagement/IStoreDiagnostics.cs
GFramework.Core.Abstractions/StateManagement/StoreDispatchContext.cs
GFramework.Core.Abstractions/StateManagement/StoreDispatchRecord.cs
GFramework.Core.Abstractions/README.md
GFramework.Core/StateManagement/StoreBuilder.cs
GFramework.Core/README.md
使用全面的单元测试覆盖 Store 行为,包括 reducers、订阅、选择、中间件、诊断、构建器以及与 BindableProperty 的互操作。
  • 添加 StoreTests,验证初始状态暴露、派发更新/通知、在状态相等时抑制通知、多 reducer 顺序以及取消注册行为。
  • 测试 SubscribeWithInitValueStoreSelection.RegisterWithInitValue,确保在初始化回调期间不会遗漏更新,并正确处理自定义比较器。
  • 测试中间件顺序、嵌套派发保护、诊断字段、StoreBuilder 配置、选择缓存,以及通过 ToBindableProperty 与 BindableProperty 的桥接。
GFramework.Core.Tests/StateManagement/StoreTests.cs
扩展和调整文档,将 Store 与 BindableProperty 并列定位,描述使用模式,并将状态管理集成到核心文档和命令生命周期中。
  • 更新属性文档,澄清在何种情况下继续使用 BindableProperty、何时使用 Store/StateMachine,展示一个同时使用 Store 与 BindableProperty 的混合 Model 示例,增加一个推荐将 Store 用于复杂聚合状态的最佳实践,并链接到状态管理文档。
  • 新增 zh-CNcore/state-management.md,描述概念(Store、reducers、selectors、StoreBuilder)、一个在 Model/Command/Controller 之间贯穿的完整 PlayerPanel 示例,以及何时使用/不使用 Store。
  • 更新核心索引包表格,增加 state-management 包条目和链接;更新 command 文档,展示将 Command 作为写入 Store 的入口,并引用状态管理教程。
  • 在必要的地方对文档进行轻微的空白符/格式调整,不改变语义。
docs/zh-CN/core/property.md
docs/zh-CN/core/state-management.md
docs/zh-CN/core/index.md
docs/zh-CN/core/command.md

技巧和命令

与 Sourcery 交互

  • 触发新审查: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在审查评论下回复,请求 Sourcery 从该评论创建 issue。你也可以直接回复审查评论 @sourcery-ai issue 来从评论创建 issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题中任意位置写上 @sourcery-ai,即可随时生成标题。也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 摘要: 在 Pull Request 正文中任意位置写上 @sourcery-ai summary,即可在该位置生成 PR 摘要。也可以在 Pull Request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在 Pull Request 中评论 @sourcery-ai guide,可随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve 以解决所有 Sourcery 评论。若你已经处理了所有评论且不希望再看到它们,这会非常有用。
  • 忽略所有 Sourcery 审查: 在 Pull Request 中评论 @sourcery-ai dismiss 以忽略所有现有的 Sourcery 审查。特别适用于你想从一个全新的审查开始——别忘了随后评论 @sourcery-ai review 触发新的审查!

自定义你的体验

访问你的控制面板 来:

  • 启用或禁用审查特性,例如 Sourcery 自动生成的 Pull Request 摘要、审阅者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

Introduce a new centralized state management core (Store) with abstractions, implementation, extensions, tests, and documentation, and integrate it with existing Property, Command, and documentation structure without breaking BindableProperty usage.

Sequence diagram for Store dispatch pipeline with middleware, reducers, and subscribers

sequenceDiagram
    participant Caller
    participant Store as Store_TState_
    participant Middleware1 as Middleware1
    participant Middleware2 as Middleware2
    participant Reducer1 as Reducer1
    participant Reducer2 as Reducer2
    participant Subscriber as Subscriber

    Caller->>Store: Dispatch_TAction_(action)
    activate Store
    Store->>Store: EnsureNotDispatching()
    Store->>Store: Create StoreDispatchContext_TState_

    Store->>Middleware1: Invoke(context, next)
    activate Middleware1
    Middleware1->>Middleware2: Invoke(context, next)
    activate Middleware2

    Middleware2->>Reducer1: Reduce(currentState, action)
    activate Reducer1
    Reducer1-->>Middleware2: nextState1
    deactivate Reducer1

    Middleware2->>Reducer2: Reduce(nextState1, action)
    activate Reducer2
    Reducer2-->>Middleware2: nextState2
    deactivate Reducer2

    Middleware2->>Store: update context.NextState, HasStateChanged
    deactivate Middleware2

    Middleware1-->>Store: after next()
    deactivate Middleware1

    Store->>Store: compare PreviousState vs NextState
    alt state changed
        Store->>Store: update _state and diagnostics
        Store->>Store: SnapshotListenersForNotification()
        Store-->>Subscriber: onValueChanged(newState)
    else no change
        Store-->>Caller: return
    end

    deactivate Store
Loading

Sequence diagram for Controller–Command–Model–Store UI update flow

sequenceDiagram
    actor User
    participant Controller as PlayerPanelController
    participant Command as DamagePlayerCommand
    participant Model as PlayerPanelModel
    participant Store as Store_PlayerPanelState_
    participant Selection as StoreSelection_PlayerPanelState_int_
    participant View as UI

    User->>Controller: OnDamageButtonClicked()
    Controller->>Command: new DamagePlayerCommand(amount)
    Controller->>Command: Execute()
    activate Command
    Command->>Model: GetModel_PlayerPanelModel_()
    Command->>Store: Dispatch_DamagePlayerAction_(action)
    deactivate Command

    Store-->>Store: apply reducers
    Store-->>Store: update PlayerPanelState

    Store-->>Selection: OnStoreChanged(state)
    Selection-->>View: onValueChanged(health)

    Controller->>Model: subscribe to Name, Health, HealthPercent
    Model->>Selection: GetOrCreateBindableProperty for each key
    Selection-->>Controller: RegisterWithInitValue callbacks
    Controller-->>View: initial render with current values
Loading

Class diagram for new state management core (Store and related abstractions)

classDiagram
    direction LR

    class IReadonlyStore_TState_ {
        <<interface>>
        +TState State
        +IUnRegister Subscribe(listener)
        +IUnRegister SubscribeWithInitValue(listener)
        +void UnSubscribe(listener)
    }

    class IStore_TState_ {
        <<interface>>
        +void Dispatch(action)
    }

    class IStoreDiagnostics_TState_ {
        <<interface>>
        +int SubscriberCount
        +Type LastActionType
        +DateTimeOffset LastStateChangedAt
        +StoreDispatchRecord_TState_ LastDispatchRecord
    }

    class IReducer_TState_TAction_ {
        <<interface>>
        +TState Reduce(currentState, action)
    }

    class IStoreMiddleware_TState_ {
        <<interface>>
        +void Invoke(context, next)
    }

    class IStateSelector_TState_TSelected_ {
        <<interface>>
        +TSelected Select(state)
    }

    class IStoreBuilder_TState_ {
        <<interface>>
        +IStoreBuilder_TState_ WithComparer(comparer)
        +IStoreBuilder_TState_ AddReducer_TAction_(reducer)
        +IStoreBuilder_TState_ AddReducerFunc_TAction_(reducer)
        +IStoreBuilder_TState_ UseMiddleware(middleware)
        +IStore_TState_ Build(initialState)
    }

    class Store_TState_ {
        +TState State
        +int SubscriberCount
        +Type LastActionType
        +DateTimeOffset LastStateChangedAt
        +StoreDispatchRecord_TState_ LastDispatchRecord
        +Store_TState_(initialState, comparer)
        +IUnRegister Subscribe(listener)
        +IUnRegister SubscribeWithInitValue(listener)
        +void UnSubscribe(listener)
        +void Dispatch_TAction_(action)
        +Store_TState_ RegisterReducer_TAction_(reducer)
        +Store_TState_ RegisterReducerFunc_TAction_(reducer)
        +Store_TState_ UseMiddleware(middleware)
        +StoreSelection_TState_TSelected_ GetOrCreateSelection_TSelected_(key, selector, comparer)
        +StoreSelection_TState_TSelected_ GetOrCreateBindableProperty_TSelected_(key, selector, comparer)
        +static StoreBuilder_TState_ CreateBuilder()
    }

    class StoreSelection_TState_TSelected_ {
        +TSelected Value
        +StoreSelection_TState_TSelected_(store, selector, comparer)
        +IUnRegister Register(onValueChanged)
        +IUnRegister RegisterWithInitValue(action)
        +void UnRegister(onValueChanged)
    }

    class StoreBuilder_TState_ {
        +StoreBuilder_TState_ WithComparer(comparer)
        +StoreBuilder_TState_ AddReducer_TAction_(reducer)
        +StoreBuilder_TState_ AddReducerFunc_TAction_(reducer)
        +StoreBuilder_TState_ UseMiddleware(middleware)
        +IStore_TState_ Build(initialState)
    }

    class StoreDispatchContext_TState_ {
        +object Action
        +Type ActionType
        +TState PreviousState
        +TState NextState
        +bool HasStateChanged
        +DateTimeOffset DispatchedAt
        +StoreDispatchContext_TState_(action, previousState)
    }

    class StoreDispatchRecord_TState_ {
        +object Action
        +Type ActionType
        +TState PreviousState
        +TState NextState
        +bool HasStateChanged
        +DateTimeOffset DispatchedAt
        +StoreDispatchRecord_TState_(action, previousState, nextState, hasStateChanged, dispatchedAt)
    }

    class StoreExtensions {
        <<static>>
        +StoreSelection_TState_TSelected_ Select_TState_TSelected_(store, selector)
        +StoreSelection_TState_TSelected_ SelectWithComparer_TState_TSelected_(store, selector, comparer)
        +StoreSelection_TState_TSelected_ SelectWithStateSelector_TState_TSelected_(store, selector, comparer)
        +IReadonlyBindableProperty_TSelected_ ToBindableProperty_TState_TSelected_(store, selector, comparer)
    }

    class IReadonlyBindableProperty_TSelected_ {
        <<interface>>
        +TSelected Value
        +IUnRegister Register(onValueChanged)
        +IUnRegister RegisterWithInitValue(action)
        +void UnRegister(onValueChanged)
    }

    class IUnRegister {
        <<interface>>
        +void UnRegister()
    }

    Store_TState_ ..> StoreDispatchContext_TState_ : uses
    Store_TState_ ..> StoreDispatchRecord_TState_ : creates
    Store_TState_ ..> IReducer_TState_TAction_ : uses
    Store_TState_ ..> IStoreMiddleware_TState_ : uses

    Store_TState_ ..> StoreSelection_TState_TSelected_ : creates

    StoreSelection_TState_TSelected_ ..> IReadonlyStore_TState_ : reads

    StoreExtensions ..> StoreSelection_TState_TSelected_ : creates
    StoreSelection_TState_TSelected_ ..|> IReadonlyBindableProperty_TSelected_

    Store_TState_ ..|> IStore_TState_
    Store_TState_ ..|> IReadonlyStore_TState_
    Store_TState_ ..|> IStoreDiagnostics_TState_

    IStore_TState_ ..|> IReadonlyStore_TState_

    StoreBuilder_TState_ ..|> IStoreBuilder_TState_

    StoreDispatchRecord_TState_ <.. IStoreDiagnostics_TState_ : exposes

    NoteForLegend <|-- Store_TState_ : concrete
    NoteForLegend2 <|-- StoreSelection_TState_TSelected_ : concrete
Loading

File-Level Changes

Change Details Files
Add a fully featured centralized state container implementation with reducers, middleware, diagnostics, cached selectors, and subscription semantics.
  • Implement generic Store with dispatch pipeline, reducer registry, middleware chain, and state equality comparer support.
  • Implement robust subscription model (Subscribe / SubscribeWithInitValue) with pending-state handling to avoid missed updates and prevent nested dispatch on same store.
  • Add selector caching and bindable-style projections (GetOrCreateSelection / GetOrCreateBindableProperty) plus internal reducer adapters and listener subscription types.
GFramework.Core/StateManagement/Store.cs
Introduce Store selection/read-only binding layer and extension methods to bridge Store into existing BindableProperty-style APIs.
  • Implement StoreSelection<TState,TSelected> as IReadonlyBindableProperty backed by an IReadonlyStore, with listener management and SubscribeWithInitValue semantics mirroring Store.
  • Ensure StoreSelection attaches/detaches to underlying Store only when listeners are present and correctly batches notifications to avoid deadlocks and missed updates.
  • Provide StoreExtensions for selecting slices, using custom comparers or IStateSelector implementations, and exposing ToBindableProperty for UI compatibility.
GFramework.Core/StateManagement/StoreSelection.cs
GFramework.Core/Extensions/StoreExtensions.cs
Add builder, middleware, selector, diagnostics, and store abstractions plus dispatch context/record types in the abstractions layer.
  • Define IReadonlyStore, IStore, IReducer<TState,TAction>, IStoreMiddleware, IStateSelector<TState,TSelected>, IStoreBuilder, and IStoreDiagnostics interfaces for state management.
  • Add StoreDispatchContext to carry action, state, and timing through the middleware pipeline, and StoreDispatchRecord to expose last dispatch diagnostics.
  • Wire Core README and Abstractions README to mention new state management interfaces and capabilities.
GFramework.Core.Abstractions/StateManagement/IReadonlyStore.cs
GFramework.Core.Abstractions/StateManagement/IStore.cs
GFramework.Core.Abstractions/StateManagement/IReducer.cs
GFramework.Core.Abstractions/StateManagement/IStoreMiddleware.cs
GFramework.Core.Abstractions/StateManagement/IStateSelector.cs
GFramework.Core.Abstractions/StateManagement/IStoreBuilder.cs
GFramework.Core.Abstractions/StateManagement/IStoreDiagnostics.cs
GFramework.Core.Abstractions/StateManagement/StoreDispatchContext.cs
GFramework.Core.Abstractions/StateManagement/StoreDispatchRecord.cs
GFramework.Core.Abstractions/README.md
GFramework.Core/StateManagement/StoreBuilder.cs
GFramework.Core/README.md
Cover Store behavior with comprehensive unit tests including reducers, subscriptions, selections, middleware, diagnostics, builder, and BindableProperty interop.
  • Add StoreTests verifying initial state exposure, dispatch updates/notifications, suppression when state is equal, multiple reducers ordering, and unregistration behavior.
  • Test SubscribeWithInitValue and StoreSelection.RegisterWithInitValue to ensure no missed updates during init callbacks and correct custom comparer handling.
  • Test middleware ordering, nested dispatch protection, diagnostics fields, StoreBuilder configuration, selection caching, and ToBindableProperty bridging with BindableProperty.
GFramework.Core.Tests/StateManagement/StoreTests.cs
Extend and adjust documentation to position Store alongside BindableProperty, describe usage patterns, and integrate state-management into the core docs and command lifecycle.
  • Update property docs to clarify when to keep using BindableProperty vs Store/StateMachine, show a mixed Model example using Store plus BindableProperty, add a best practice recommending Store for complex aggregate state, and link to state-management docs.
  • Add a new zh-CN core/state-management.md describing concepts (Store, reducers, selectors, StoreBuilder), a full PlayerPanel example across Model/Command/Controller, and when to use or not use Store.
  • Update core index package table to add state-management package entry and link; update command docs to show using Command as write-entry into Store and reference state-management tutorial.
  • Minor whitespace/formatting tweaks where necessary in docs without changing semantics.
docs/zh-CN/core/property.md
docs/zh-CN/core/state-management.md
docs/zh-CN/core/index.md
docs/zh-CN/core/command.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepsource-io

deepsource-io Bot commented Mar 23, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in c70728b...3d21271 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
C# Mar 23, 2026 12:14p.m. Review ↗
Secrets Mar 23, 2026 12:14p.m. Review ↗

Comment thread GFramework.Core/StateManagement/Store.cs

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我在这里给出了一些高层次的反馈:

  • Store<TState>.Dispatch 中,目前的锁覆盖了整个 middleware + reducer 管线的执行;如果中间件或 reducer 执行较耗时的操作,这会使所有 dispatch 串行化并增加竞争。建议将临界区限制在对状态 / reducer 表的修改上,并在离开锁之前捕获运行管线所需的上下文,然后在锁之外执行管线(或者在文档中明确说明中间件必须保持非常轻量)。
  • 新增的类型 Store<TState>StoreSelection<TState, TSelected>StoreBuilder<TState> 暴露了较大的继承面,但看起来并不是为了扩展而设计的;建议将它们标记为 sealed(并将某些实现细节层面的接口 / 类标记为 internal),以缩小公共 API 的范围,并使未来的重构更安全。
面向 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- In `Store<TState>.Dispatch` the lock currently spans the entire middleware + reducer pipeline execution; if middlewares or reducers perform longer-running work this will serialize all dispatches and can increase contention, so consider limiting the critical section to state/reducer table mutations and capturing the needed context before running the pipeline outside the lock (or explicitly documenting that middlewares must remain very lightweight).
- The new types `Store<TState>`, `StoreSelection<TState, TSelected>` and `StoreBuilder<TState>` expose quite a large surface for inheritance but don’t appear to be designed for extension; consider marking them `sealed` (and potentially some implementation-detail interfaces/classes as `internal`) to keep the public API smaller and make future refactors safer.

Sourcery 对开源项目是免费的——如果你觉得我们的代码评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've left some high level feedback:

  • In Store<TState>.Dispatch the lock currently spans the entire middleware + reducer pipeline execution; if middlewares or reducers perform longer-running work this will serialize all dispatches and can increase contention, so consider limiting the critical section to state/reducer table mutations and capturing the needed context before running the pipeline outside the lock (or explicitly documenting that middlewares must remain very lightweight).
  • The new types Store<TState>, StoreSelection<TState, TSelected> and StoreBuilder<TState> expose quite a large surface for inheritance but don’t appear to be designed for extension; consider marking them sealed (and potentially some implementation-detail interfaces/classes as internal) to keep the public API smaller and make future refactors safer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `Store<TState>.Dispatch` the lock currently spans the entire middleware + reducer pipeline execution; if middlewares or reducers perform longer-running work this will serialize all dispatches and can increase contention, so consider limiting the critical section to state/reducer table mutations and capturing the needed context before running the pipeline outside the lock (or explicitly documenting that middlewares must remain very lightweight).
- The new types `Store<TState>`, `StoreSelection<TState, TSelected>` and `StoreBuilder<TState>` expose quite a large surface for inheritance but don’t appear to be designed for extension; consider marking them `sealed` (and potentially some implementation-detail interfaces/classes as `internal`) to keep the public API smaller and make future refactors safer.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

GeWuYou added 2 commits March 23, 2026 20:11
- 将 Store 类标记为 sealed 以防止继承
- 引入独立的 dispatch 门闩锁,将状态锁的保护范围缩小为仅保护临界区访问
- 实现 dispatch 过程中的快照机制,确保中间件和 reducer 在锁外执行稳定的不可变序列
- 重构 ExecuteDispatchPipeline 方法,接受快照参数并改为静态方法
- 添加 CreateReducerSnapshot 方法为每次分发创建 reducer 快照
- 更新 StoreBuilder 和 StoreSelection 类为 sealed
- 新增测试用例验证长时间运行的 middleware 不会阻塞状态读取和订阅操作
- 修复 dispatch 过程中状态锁占用时间过长的问题,提升并发性能
- 添加了属性抽象层的命名空间引用
- 添加了状态管理抽象层的命名空间引用
- 保持了原有的核心扩展、属性和状态管理命名空间引用
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant