目录在 Blocks 中管理状态State In Blocks全局状态Global State会话状态Session State无法深拷贝对象按会话隔离实例浏览器状态Browser State总结上一篇Gradio.Net (https://github.com/feiyun0112/Gradio.Net)是一个开源的 .NET 库它是 Gradio 的 .NET 移植版本允许你为机器学习模型、API 或任何 C# 函数快速构建演示或 Web 应用程序无需任何 JavaScript、CSS 或 Web 开发经验在 Blocks 中管理状态State In Blocks当你使用gr.Blocks()构建应用时通常会遇到“状态”问题有些值需要在所有用户之间共享例如访问计数有些值只需要在单个用户会话内保留例如购物车、聊天历史有些值希望刷新页面后仍然保留例如用户名、偏好设置。在 Gradio / Gradio.Net 中常见有三种状态管理方式全局状态Global State应用运行期间所有用户共享。会话状态Session State仅对当前用户会话有效刷新页面会重置。浏览器状态Browser State保存在浏览器 ‎localStorage关闭/刷新后仍可恢复。全局状态Global State全局状态最简单把变量定义在函数外部即可。下面示例里visitorCount在所有用户之间共享。using Gradio.Net; using Gradio.Net.Components; using Gradio.Net.Events; var visitorCount 0; string IncrementCounter() { visitorCount 1; return visitorCount.ToString(); } using var demo gr.Blocks(); var number gr.Textbox(label: Total Visitors, value: Counting...); _ gr.On( triggers: (ListEventListener?)null, fn: IncrementCounter, outputs: number ); await demo.Launch();代码如果你不希望值在不同用户之间共享请不要放在全局变量里而应使用会话状态。会话状态Session State会话状态会在同一页面会话内持续保留但不会跨用户共享用户刷新页面后会重置。使用步骤创建 ‎gr.State()可传默认值。在事件监听里把该 ‎State作为输入和输出。在函数参数和返回值中处理该状态。下面是购物车示例using Gradio.Net; using Gradio.Net.Components; Liststring AddItems(Liststring? newItems, Liststring? previousCart) { var cart new Liststring(previousCart ?? new()); if (newItems ! null) { cart.AddRange(newItems); } return cart; } double CartSize(Liststring? cart) { return (cart ?? new Liststring()).Count; } using var demo gr.Blocks(); var cart gr.State(value: new Liststring()); var itemsToAdd gr.CheckboxGroup(new[] { Cereal, Milk, Orange Juice, Water }); var addButton gr.Button(Add Items); _ addButton.Click( fn: AddItems, inputs: new object[] { itemsToAdd, cart }, outputs: cart ); var cartSize gr.Number(label: Cart Size); _ cart.Change( fn: CartSize, inputs: cart, outputs: cartSize ); await demo.Launch();你可以把gr.State理解为一个“不可见组件”前端不直接显示但可以参与事件计算。说明gr.State的值需要可深拷贝deepcopy-able。如果状态对象本身无法深拷贝请使用“按会话哈希维护全局字典”的方案。无法深拷贝对象按会话隔离实例当对象例如包含锁无法深拷贝时可以读取request.SessionHash并在服务端用全局字典保存“每个用户会话”的实例。using System.Collections.Concurrent; using Gradio.Net; using Gradio.Net.Components; using Gradio.Net.Core; using Gradio.Net.Events; var instances new ConcurrentDictionarystring, NonDeepCopyable(); string InitializeInstance(Request request) { if (string.IsNullOrWhiteSpace(request.SessionHash)) { return Error: session hash missing; } instances[request.SessionHash] new NonDeepCopyable(); return Session initialized!; } double IncrementCounter(Request request) { if (string.IsNullOrWhiteSpace(request.SessionHash)) { return -1; } if (instances.TryGetValue(request.SessionHash, out var instance)) { return instance.Increment(); } return -1; } using var demo gr.Blocks(); var output gr.Textbox(label: Status); var counter gr.Number(label: Counter Value); var incrementBtn gr.Button(Increment Counter); _ gr.On( triggers: (ListEventListener?)null, fn: InitializeInstance, outputs: output ); _ incrementBtn.Click( fn: IncrementCounter, outputs: counter ); await demo.Launch(); class NonDeepCopyable { private readonly object _lockObj new(); private int _counter; public int Increment() { lock (_lockObj) { _counter 1; return _counter; } } }浏览器状态Browser State浏览器状态会写入本地localStorage适合保存用户偏好、账号输入等信息。using Gradio.Net; using Gradio.Net.Components; using Gradio.Net.Events; var random new Random(); string GenerateToken(int len) { const string chars abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789; return new string(Enumerable.Range(0, len).Select(_ chars[random.Next(chars.Length)]).ToArray()); } object[] GenerateRandomly() { var username GenerateToken(10); var password GenerateToken(10); return new object[] { username, password }; } object[] LoadFromLocalStorage(object? savedValues) { if (savedValues is System.Collections.IList list list.Count 2) { return new object[] { list[0]?.ToString() ?? string.Empty, list[1]?.ToString() ?? string.Empty }; } return new object[] { string.Empty, string.Empty }; } object SaveToLocalStorage(string username, string password) { return new object[] { username, password }; } Markdown ShowSavedMessage() { var timestamp DateTime.Now.ToString(hh:mm:ss tt); return gr.Markdown($✅ Saved to local storage at {timestamp}, visible: true); } using var demo gr.Blocks(); _ gr.Markdown(Your Username and Password will get saved in the browsers local storage. If you refresh the page, the values will be retained.); var username gr.Textbox(label: Username); var password gr.Textbox(label: Password, type: password); var btn gr.Button(Generate Randomly); var localStorage gr.BrowserState(defaultValue: new[] { , }); var savedMessage gr.Markdown(✅ Saved to local storage, visible: false); _ btn.Click( fn: GenerateRandomly, outputs: new object[] { username, password } ); _ gr.On( triggers: (ListEventListener?)null, fn: LoadFromLocalStorage, inputs: localStorage, outputs: new object[] { username, password } ); _ username.Change( fn: SaveToLocalStorage, inputs: new object[] { username, password }, outputs: localStorage ); _ password.Change( fn: SaveToLocalStorage, inputs: new object[] { username, password }, outputs: localStorage ); _ localStorage.Change( fn: ShowSavedMessage, outputs: savedMessage ); await demo.Launch();注意gr.BrowserState的值保存在浏览器中。若 Gradio 应用重启默认随机storage_key/secret会变化历史值可能无法继续读取。要跨重启读取请固定storage_key与secret并确保在可信应用场景中使用。总结这一章你学会了三种状态管理方式全局状态适合跨用户共享的运行期数据。会话状态适合单用户会话内的临时数据。浏览器状态适合需要在刷新后保留的前端数据。引入地址