refactor(core): 重构协程扩展和函数式编程相关代码

- 优化 CommandCoroutineExtensions 中的代码格式和异常处理逻辑
- 简化 WaitForEvent 和 WaitForEventWithTimeout 中的EventData属性实现
- 调整 EventListenerScope 中的EventData属性访问器
- 重构 ControlExtensions 中 TakeIf 和 TakeUnless 方法的实现
- 优化 FunctionExtensions 中 Repeat 和 Partial 方法的代码结构
- 调整 PipeExtensions 和其他扩展类的文档注释格式
- 修改测试代码中的协程迭代和事件注册相关实现
- 优化 DataRepository 中的异步操作实现方式
= [release ci]
This commit is contained in:
GeWuYou 2026-02-01 14:07:48 +08:00
parent 1d7fe998b9
commit af583c101c
37 changed files with 257 additions and 294 deletions

View File

@ -25,8 +25,16 @@ public class CommandCoroutineExtensionsTests
private class TestCommand : IAsyncCommand private class TestCommand : IAsyncCommand
{ {
private IArchitectureContext? _context; private IArchitectureContext? _context;
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context!; public void SetContext(IArchitectureContext context)
{
_context = context;
}
public IArchitectureContext GetContext()
{
return _context!;
}
public Task ExecuteAsync() public Task ExecuteAsync()
{ {
@ -47,7 +55,7 @@ public class CommandCoroutineExtensionsTests
/// </summary> /// </summary>
private class TestContextAware : IContextAware private class TestContextAware : IContextAware
{ {
public Mock<IArchitectureContext> _mockContext = new(); public readonly Mock<IArchitectureContext> _mockContext = new();
public IArchitectureContext GetContext() public IArchitectureContext GetContext()
{ {
@ -78,13 +86,9 @@ public class CommandCoroutineExtensionsTests
// 迭代协程直到完成 // 迭代协程直到完成
while (coroutine.MoveNext()) while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForTask) if (coroutine.Current is WaitForTask)
{
// 等待任务完成 // 等待任务完成
await Task.Delay(10); await Task.Delay(10);
}
}
Assert.That(capturedException, Is.Null); Assert.That(capturedException, Is.Null);
} }
@ -109,13 +113,9 @@ public class CommandCoroutineExtensionsTests
// 迭代协程直到完成 // 迭代协程直到完成
while (coroutine.MoveNext()) while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForTask) if (coroutine.Current is WaitForTask)
{
// 等待任务完成 // 等待任务完成
await Task.Delay(10); await Task.Delay(10);
}
}
Assert.That(capturedException, Is.Not.Null); Assert.That(capturedException, Is.Not.Null);
// 异常被包装为 AggregateException // 异常被包装为 AggregateException
@ -140,17 +140,13 @@ public class CommandCoroutineExtensionsTests
.Returns(Task.FromException(expectedException)); .Returns(Task.FromException(expectedException));
// 使用null作为错误处理程序 // 使用null作为错误处理程序
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, null); var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command);
// 迭代协程直到完成 // 迭代协程直到完成
while (coroutine.MoveNext()) while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForTask) if (coroutine.Current is WaitForTask)
{
// 等待任务完成 // 等待任务完成
await Task.Delay(10); await Task.Delay(10);
}
}
// 应该不会抛出异常 // 应该不会抛出异常
Assert.Pass(); Assert.Pass();
@ -172,7 +168,7 @@ public class CommandCoroutineExtensionsTests
var unRegisterMock = new Mock<IUnRegister>(); var unRegisterMock = new Mock<IUnRegister>();
Action<TestEvent>? eventCallback = null; Action<TestEvent>? eventCallback = null;
eventBusMock.Setup(bus => bus.Register<TestEvent>(It.IsAny<Action<TestEvent>>())) eventBusMock.Setup(bus => bus.Register(It.IsAny<Action<TestEvent>>()))
.Returns(unRegisterMock.Object) .Returns(unRegisterMock.Object)
.Callback<Action<TestEvent>>(callback => eventCallback = callback); .Callback<Action<TestEvent>>(callback => eventCallback = callback);
@ -196,10 +192,7 @@ public class CommandCoroutineExtensionsTests
// 启动协程并等待命令执行完成 // 启动协程并等待命令执行完成
coroutine.MoveNext(); // 进入命令发送阶段 coroutine.MoveNext(); // 进入命令发送阶段
if (coroutine.Current is WaitForTask) if (coroutine.Current is WaitForTask) await Task.Delay(10); // 等待命令任务完成
{
await Task.Delay(10); // 等待命令任务完成
}
// 此时协程应该在等待事件 // 此时协程应该在等待事件
Assert.That(coroutine.MoveNext(), Is.True); // 等待事件阶段 Assert.That(coroutine.MoveNext(), Is.True); // 等待事件阶段
@ -252,13 +245,9 @@ public class CommandCoroutineExtensionsTests
// 运行协程直到完成 // 运行协程直到完成
while (coroutine.MoveNext()) while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForEventWithTimeout<TestEvent> timeoutWait) if (coroutine.Current is WaitForEventWithTimeout<TestEvent> timeoutWait)
{
// 模拟超时 // 模拟超时
timeoutWait.Update(0.2); // 更新时间超过超时限制 timeoutWait.Update(0.2); // 更新时间超过超时限制
}
}
}); });
} }
@ -292,15 +281,11 @@ public class CommandCoroutineExtensionsTests
// 使用null作为事件回调 // 使用null作为事件回调
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>( var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
command, command); // null回调
null); // null回调
// 启动协程 // 启动协程
coroutine.MoveNext(); // 进入命令发送阶段 coroutine.MoveNext(); // 进入命令发送阶段
if (coroutine.Current is WaitForTask) if (coroutine.Current is WaitForTask) await Task.Delay(10); // 等待命令任务完成
{
await Task.Delay(10); // 等待命令任务完成
}
// 触发事件 // 触发事件
var testEvent = new TestEvent { Data = "TestData" }; var testEvent = new TestEvent { Data = "TestData" };
@ -343,10 +328,7 @@ public class CommandCoroutineExtensionsTests
// 启动协程 - 命令失败时协程仍然继续 // 启动协程 - 命令失败时协程仍然继续
coroutine.MoveNext(); // 进入命令发送阶段 coroutine.MoveNext(); // 进入命令发送阶段
if (coroutine.Current is WaitForTask) if (coroutine.Current is WaitForTask) await Task.Delay(10); // 等待命令任务完成
{
await Task.Delay(10); // 等待命令任务完成
}
// 命令执行失败后,协程继续执行 // 命令执行失败后,协程继续执行
Assert.Pass(); Assert.Pass();
@ -384,7 +366,7 @@ public class CommandCoroutineExtensionsTests
var eventBusMock = new Mock<IEventBus>(); var eventBusMock = new Mock<IEventBus>();
var unRegisterMock = new Mock<IUnRegister>(); var unRegisterMock = new Mock<IUnRegister>();
eventBusMock.Setup(bus => bus.Register<TestEvent>(It.IsAny<Action<TestEvent>>())) eventBusMock.Setup(bus => bus.Register(It.IsAny<Action<TestEvent>>()))
.Returns(unRegisterMock.Object); .Returns(unRegisterMock.Object);
// 设置上下文服务以返回事件总线 // 设置上下文服务以返回事件总线

View File

@ -50,10 +50,7 @@ public class CoroutineComposeExtensionsTests
var combined = first.Then(() => executionOrder.Add("action")); var combined = first.Then(() => executionOrder.Add("action"));
// 执行组合协程 // 执行组合协程
while (combined.MoveNext()) while (combined.MoveNext()) combined.Current.Update(0.016);
{
combined.Current.Update(0.016);
}
Assert.That(executionOrder.Count, Is.EqualTo(3)); Assert.That(executionOrder.Count, Is.EqualTo(3));
Assert.That(executionOrder[0], Is.EqualTo("coroutine")); Assert.That(executionOrder[0], Is.EqualTo("coroutine"));
@ -112,10 +109,7 @@ public class CoroutineComposeExtensionsTests
.Then(() => executionOrder.Add(2)) .Then(() => executionOrder.Add(2))
.Then(() => executionOrder.Add(3)); .Then(() => executionOrder.Add(3));
while (combined.MoveNext()) while (combined.MoveNext()) combined.Current.Update(0.016);
{
combined.Current.Update(0.016);
}
Assert.That(executionOrder, Is.EqualTo([1, 2, 3])); Assert.That(executionOrder, Is.EqualTo([1, 2, 3]));
} }
@ -162,10 +156,7 @@ public class CoroutineComposeExtensionsTests
var second = CreateTestCoroutine(2, () => executionOrder.Add("second")); var second = CreateTestCoroutine(2, () => executionOrder.Add("second"));
var combined = first.Then(second); var combined = first.Then(second);
while (combined.MoveNext()) while (combined.MoveNext()) combined.Current.Update(0.016);
{
combined.Current.Update(0.016);
}
Assert.That(executionOrder.Count, Is.EqualTo(4)); Assert.That(executionOrder.Count, Is.EqualTo(4));
Assert.That(executionOrder[0], Is.EqualTo("first")); Assert.That(executionOrder[0], Is.EqualTo("first"));
@ -206,10 +197,7 @@ public class CoroutineComposeExtensionsTests
var second = CreateTestCoroutine(0); var second = CreateTestCoroutine(0);
var combined = first.Then(second); var combined = first.Then(second);
while (combined.MoveNext()) while (combined.MoveNext()) combined.Current.Update(0.016);
{
combined.Current.Update(0.016);
}
Assert.That(firstExecuteCount, Is.EqualTo(2)); Assert.That(firstExecuteCount, Is.EqualTo(2));
} }
@ -262,10 +250,7 @@ public class CoroutineComposeExtensionsTests
var combined = first.Then(second).Then(third); var combined = first.Then(second).Then(third);
while (combined.MoveNext()) while (combined.MoveNext()) combined.Current.Update(0.016);
{
combined.Current.Update(0.016);
}
Assert.That(executionOrder, Is.EqualTo(new[] { 1, 2, 3 })); Assert.That(executionOrder, Is.EqualTo(new[] { 1, 2, 3 }));
} }
@ -286,10 +271,7 @@ public class CoroutineComposeExtensionsTests
.Then(second) .Then(second)
.Then(() => executionOrder.Add("action2")); .Then(() => executionOrder.Add("action2"));
while (combined.MoveNext()) while (combined.MoveNext()) combined.Current.Update(0.016);
{
combined.Current.Update(0.016);
}
Assert.That(executionOrder, Is.EqualTo(new[] { "coroutine1", "action1", "coroutine2", "action2" })); Assert.That(executionOrder, Is.EqualTo(new[] { "coroutine1", "action1", "coroutine2", "action2" }));
} }
@ -339,10 +321,7 @@ public class CoroutineComposeExtensionsTests
coroutine = coroutine.Then(nextCoroutine); coroutine = coroutine.Then(nextCoroutine);
} }
while (coroutine.MoveNext()) while (coroutine.MoveNext()) coroutine.Current.Update(0.016);
{
coroutine.Current.Update(0.016);
}
Assert.That(count, Is.EqualTo(11)); Assert.That(count, Is.EqualTo(11));
} }

View File

@ -443,7 +443,7 @@ public class CoroutineHelperTests
public void RepeatCallForever_Should_Execute_Forever_When_ShouldContinue_Is_Null() public void RepeatCallForever_Should_Execute_Forever_When_ShouldContinue_Is_Null()
{ {
var callCount = 0; var callCount = 0;
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++, (Func<bool>?)null); var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++);
for (var i = 0; i < 5; i++) for (var i = 0; i < 5; i++)
{ {

View File

@ -23,9 +23,21 @@ public class QueryCoroutineExtensionsTests
{ {
private IArchitectureContext? _context; private IArchitectureContext? _context;
public string QueryData { get; set; } = string.Empty; public string QueryData { get; set; } = string.Empty;
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context!; public void SetContext(IArchitectureContext context)
public string Do() => QueryData; {
_context = context;
}
public IArchitectureContext GetContext()
{
return _context!;
}
public string Do()
{
return QueryData;
}
} }
/// <summary> /// <summary>
@ -33,7 +45,7 @@ public class QueryCoroutineExtensionsTests
/// </summary> /// </summary>
private class TestContextAware : IContextAware private class TestContextAware : IContextAware
{ {
public Mock<IArchitectureContext> _mockContext = new Mock<IArchitectureContext>(); public readonly Mock<IArchitectureContext> _mockContext = new();
public IArchitectureContext GetContext() public IArchitectureContext GetContext()
{ {
@ -58,7 +70,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为 // 设置上下文发送查询的模拟行为
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns("TestResult"); .Returns("TestResult");
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result => var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result =>
@ -90,7 +102,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为 // 设置上下文发送查询的模拟行为
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<bool>(It.IsAny<IntQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<IntQuery>()))
.Returns(true); .Returns(true);
var coroutine = contextAware.SendQueryCoroutine<IntQuery, bool>(query, result => var coroutine = contextAware.SendQueryCoroutine<IntQuery, bool>(query, result =>
@ -118,7 +130,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为 // 设置上下文发送查询的模拟行为
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns("TestResult"); .Returns("TestResult");
// 使用null作为结果回调应该抛出NullReferenceException // 使用null作为结果回调应该抛出NullReferenceException
@ -141,7 +153,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为 // 设置上下文发送查询的模拟行为
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns("ProcessedResult"); .Returns("ProcessedResult");
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result => var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result =>
@ -168,7 +180,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为 // 设置上下文发送查询的模拟行为
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns("TestResult"); .Returns("TestResult");
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { }); var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { });
@ -189,7 +201,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为,让它抛出异常 // 设置上下文发送查询的模拟行为,让它抛出异常
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Throws(expectedException); .Throws(expectedException);
// 由于SendQueryCoroutine会直接执行查询这可能导致异常 // 由于SendQueryCoroutine会直接执行查询这可能导致异常
@ -216,7 +228,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为,并捕获传入的查询参数 // 设置上下文发送查询的模拟行为,并捕获传入的查询参数
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns((IQuery<string> q) => .Returns((IQuery<string> q) =>
{ {
capturedQuery = (TestQuery)q; capturedQuery = (TestQuery)q;
@ -258,7 +270,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为 // 设置上下文发送查询的模拟行为
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<ComplexResult>(It.IsAny<ComplexQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<ComplexQuery>()))
.Returns(expectedResult); .Returns(expectedResult);
var coroutine = var coroutine =
@ -284,7 +296,7 @@ public class QueryCoroutineExtensionsTests
// 设置上下文发送查询的模拟行为,返回空字符串 // 设置上下文发送查询的模拟行为,返回空字符串
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns(string.Empty); .Returns(string.Empty);
var coroutine = var coroutine =
@ -302,12 +314,12 @@ public class QueryCoroutineExtensionsTests
public void SendQueryCoroutine_Should_Handle_Null_Result() public void SendQueryCoroutine_Should_Handle_Null_Result()
{ {
var query = new TestQuery { QueryData = "TestQueryData" }; var query = new TestQuery { QueryData = "TestQueryData" };
string? receivedResult = "initial"; var receivedResult = "initial";
var contextAware = new TestContextAware(); var contextAware = new TestContextAware();
// 设置上下文发送查询的模拟行为返回null // 设置上下文发送查询的模拟行为返回null
contextAware._mockContext contextAware._mockContext
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>())) .Setup(ctx => ctx.SendQuery(It.IsAny<TestQuery>()))
.Returns((string)null!); .Returns((string)null!);
var coroutine = var coroutine =
@ -326,9 +338,21 @@ internal class IntQuery : IQuery<bool>
{ {
private IArchitectureContext? _context; private IArchitectureContext? _context;
public int Value { get; set; } public int Value { get; set; }
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context!; public void SetContext(IArchitectureContext context)
public bool Do() => Value > 0; {
_context = context;
}
public IArchitectureContext GetContext()
{
return _context!;
}
public bool Do()
{
return Value > 0;
}
} }
/// <summary> /// <summary>
@ -338,11 +362,23 @@ internal class ComplexQuery : IQuery<ComplexResult>
{ {
private IArchitectureContext? _context; private IArchitectureContext? _context;
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public List<int> Values { get; set; } = new List<int>(); public List<int> Values { get; set; } = new();
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>(); public Dictionary<string, object> Metadata { get; set; } = new();
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context!; public void SetContext(IArchitectureContext context)
public ComplexResult Do() => new ComplexResult { ProcessedName = Name, Sum = Values.Sum(), Count = Values.Count }; {
_context = context;
}
public IArchitectureContext GetContext()
{
return _context!;
}
public ComplexResult Do()
{
return new ComplexResult { ProcessedName = Name, Sum = Values.Sum(), Count = Values.Count };
}
} }
/// <summary> /// <summary>

View File

@ -217,7 +217,7 @@ public class EventListenerScopeTests
var eventBusMock = new Mock<IEventBus>(); var eventBusMock = new Mock<IEventBus>();
var unRegisterMock = new Mock<IUnRegister>(); var unRegisterMock = new Mock<IUnRegister>();
eventBusMock.Setup(x => x.Register<int>(It.IsAny<Action<int>>())) eventBusMock.Setup(x => x.Register(It.IsAny<Action<int>>()))
.Returns(unRegisterMock.Object) .Returns(unRegisterMock.Object)
.Callback<Action<int>>(action => registeredAction = action); .Callback<Action<int>>(action => registeredAction = action);
@ -242,7 +242,7 @@ public class EventListenerScopeTests
var eventBusMock = new Mock<IEventBus>(); var eventBusMock = new Mock<IEventBus>();
var unRegisterMock = new Mock<IUnRegister>(); var unRegisterMock = new Mock<IUnRegister>();
eventBusMock.Setup(x => x.Register<StructEvent>(It.IsAny<Action<StructEvent>>())) eventBusMock.Setup(x => x.Register(It.IsAny<Action<StructEvent>>()))
.Returns(unRegisterMock.Object) .Returns(unRegisterMock.Object)
.Callback<Action<StructEvent>>(action => registeredAction = action); .Callback<Action<StructEvent>>(action => registeredAction = action);
@ -268,7 +268,7 @@ public class EventListenerScopeTests
var eventBusMock = new Mock<IEventBus>(); var eventBusMock = new Mock<IEventBus>();
var unRegisterMock = new Mock<IUnRegister>(); var unRegisterMock = new Mock<IUnRegister>();
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>())) eventBusMock.Setup(x => x.Register(It.IsAny<Action<TestEvent>>()))
.Returns(unRegisterMock.Object) .Returns(unRegisterMock.Object)
.Callback<Action<TestEvent>>(action => registeredAction = action); .Callback<Action<TestEvent>>(action => registeredAction = action);
@ -290,7 +290,7 @@ public class EventListenerScopeTests
{ {
var eventBusMock = new Mock<IEventBus>(); var eventBusMock = new Mock<IEventBus>();
var unRegisterMock = new Mock<IUnRegister>(); var unRegisterMock = new Mock<IUnRegister>();
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>())) eventBusMock.Setup(x => x.Register(It.IsAny<Action<TestEvent>>()))
.Returns(unRegisterMock.Object); .Returns(unRegisterMock.Object);
var scope = new EventListenerScope<TestEvent>(eventBusMock.Object); var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);

View File

@ -16,7 +16,7 @@ public class ControlExtensionsTests
public void TakeIf_Should_Return_Value_When_Condition_Is_True() public void TakeIf_Should_Return_Value_When_Condition_Is_True()
{ {
// Arrange // Arrange
string str = "Hello"; var str = "Hello";
// Act // Act
var result = str.TakeIf(s => s.Length > 3); var result = str.TakeIf(s => s.Length > 3);
@ -32,7 +32,7 @@ public class ControlExtensionsTests
public void TakeIf_Should_Return_Null_When_Condition_Is_False() public void TakeIf_Should_Return_Null_When_Condition_Is_False()
{ {
// Arrange // Arrange
string str = "Hi"; var str = "Hi";
// Act // Act
var result = str.TakeIf(s => s.Length > 3); var result = str.TakeIf(s => s.Length > 3);
@ -48,7 +48,7 @@ public class ControlExtensionsTests
public void TakeUnless_Should_Return_Value_When_Condition_Is_False() public void TakeUnless_Should_Return_Value_When_Condition_Is_False()
{ {
// Arrange // Arrange
string str = "Hi"; var str = "Hi";
// Act // Act
var result = str.TakeUnless(s => s.Length > 3); var result = str.TakeUnless(s => s.Length > 3);
@ -64,7 +64,7 @@ public class ControlExtensionsTests
public void TakeUnless_Should_Return_Null_When_Condition_Is_True() public void TakeUnless_Should_Return_Null_When_Condition_Is_True()
{ {
// Arrange // Arrange
string str = "Hello"; var str = "Hello";
// Act // Act
var result = str.TakeUnless(s => s.Length > 3); var result = str.TakeUnless(s => s.Length > 3);

View File

@ -43,10 +43,7 @@ public static class CommandCoroutineExtensions
yield return task.AsCoroutineInstruction(); yield return task.AsCoroutineInstruction();
if (task.IsFaulted) if (task.IsFaulted) onError?.Invoke(task.Exception!);
{
onError?.Invoke(task.Exception!);
}
} }
/// <summary> /// <summary>
@ -89,11 +86,9 @@ public static class CommandCoroutineExtensions
// 检查是否超时 // 检查是否超时
if (timeoutWait.IsTimeout) if (timeoutWait.IsTimeout)
{
// 超时处理 // 超时处理
throw new TimeoutException($"wait for the event ${typeof(TEvent).Name} timeout."); throw new TimeoutException($"wait for the event ${typeof(TEvent).Name} timeout.");
} }
}
else else
{ {
// 等待事件触发(无超时) // 等待事件触发(无超时)
@ -101,10 +96,7 @@ public static class CommandCoroutineExtensions
} }
// 调用事件回调 // 调用事件回调
if (onEvent != null && wait.EventData != null) if (onEvent != null && wait.EventData != null) onEvent.Invoke(wait.EventData);
{
onEvent.Invoke(wait.EventData);
}
} }
finally finally
{ {

View File

@ -20,7 +20,6 @@ public sealed class WaitForEvent<TEvent> : IYieldInstruction, IDisposable
{ {
private bool _disposed; private bool _disposed;
private volatile bool _done; private volatile bool _done;
private TEvent? _eventData;
private IUnRegister? _unRegister; private IUnRegister? _unRegister;
/// <summary> /// <summary>
@ -38,7 +37,7 @@ public sealed class WaitForEvent<TEvent> : IYieldInstruction, IDisposable
/// <summary> /// <summary>
/// 获取接收到的事件数据 /// 获取接收到的事件数据
/// </summary> /// </summary>
public TEvent? EventData => _eventData; public TEvent? EventData { get; private set; }
/// <summary> /// <summary>
/// 释放资源 /// 释放资源
@ -77,7 +76,7 @@ public sealed class WaitForEvent<TEvent> : IYieldInstruction, IDisposable
/// <param name="eventData">事件数据</param> /// <param name="eventData">事件数据</param>
private void OnEventTriggered(TEvent eventData) private void OnEventTriggered(TEvent eventData)
{ {
_eventData = eventData; EventData = eventData;
_done = true; _done = true;
} }
} }

View File

@ -51,10 +51,7 @@ public sealed class WaitForEventWithTimeout<TEvent>(WaitForEvent<TEvent> waitFor
public void Update(double deltaTime) public void Update(double deltaTime)
{ {
// 只有在事件未完成时才累加经过的时间 // 只有在事件未完成时才累加经过的时间
if (!_waitForEvent.IsDone) if (!_waitForEvent.IsDone) _elapsedTime += (float)deltaTime;
{
_elapsedTime += (float)deltaTime;
}
_waitForEvent.Update(deltaTime); _waitForEvent.Update(deltaTime);
} }

View File

@ -22,7 +22,6 @@ namespace GFramework.Core.events;
public sealed class EventListenerScope<TEvent> : IDisposable public sealed class EventListenerScope<TEvent> : IDisposable
{ {
private readonly IUnRegister? _unRegister; private readonly IUnRegister? _unRegister;
private TEvent? _eventData;
private volatile bool _triggered; private volatile bool _triggered;
/// <summary> /// <summary>
@ -37,7 +36,7 @@ public sealed class EventListenerScope<TEvent> : IDisposable
/// <summary> /// <summary>
/// 获取接收到的事件数据 /// 获取接收到的事件数据
/// </summary> /// </summary>
public TEvent? EventData => _eventData; public TEvent? EventData { get; private set; }
/// <summary> /// <summary>
/// 获取事件是否已被触发 /// 获取事件是否已被触发
@ -58,7 +57,7 @@ public sealed class EventListenerScope<TEvent> : IDisposable
/// <param name="eventData">接收到的事件数据</param> /// <param name="eventData">接收到的事件数据</param>
private void OnEventTriggered(TEvent eventData) private void OnEventTriggered(TEvent eventData)
{ {
_eventData = eventData; EventData = eventData;
_triggered = true; _triggered = true;
} }
} }

View File

@ -29,7 +29,9 @@ public static class ControlExtensions
this TSource value, this TSource value,
Func<TSource, bool> predicate) Func<TSource, bool> predicate)
where TSource : class where TSource : class
=> predicate(value) ? value : null; {
return predicate(value) ? value : null;
}
/// <summary> /// <summary>
/// TakeUnless条件相反的TakeIf /// TakeUnless条件相反的TakeIf
@ -42,5 +44,7 @@ public static class ControlExtensions
this TSource value, this TSource value,
Func<TSource, bool> predicate) Func<TSource, bool> predicate)
where TSource : class where TSource : class
=> !predicate(value) ? value : null; {
return !predicate(value) ? value : null;
}
} }

View File

@ -36,10 +36,7 @@ public static class FunctionExtensions
ArgumentOutOfRangeException.ThrowIfNegative(times); ArgumentOutOfRangeException.ThrowIfNegative(times);
var result = value; var result = value;
for (var i = 0; i < times; i++) for (var i = 0; i < times; i++) result = func(result);
{
result = func(result);
}
return result; return result;
} }
@ -72,7 +69,6 @@ public static class FunctionExtensions
/// <summary> /// <summary>
/// MemoizeUnbounded /// MemoizeUnbounded
/// 对函数结果进行无界缓存(线程安全) /// 对函数结果进行无界缓存(线程安全)
///
/// ⚠ 注意: /// ⚠ 注意:
/// - 缓存永不释放 /// - 缓存永不释放
/// - TSource 必须具有稳定的 Equals / GetHashCode /// - TSource 必须具有稳定的 Equals / GetHashCode
@ -92,13 +88,14 @@ public static class FunctionExtensions
/// <summary> /// <summary>
/// Partial部分应用二参数函数固定第一个参数 /// Partial部分应用二参数函数固定第一个参数
///
/// ⚠ 偏函数应用属于高级用法,不建议在业务代码滥用 /// ⚠ 偏函数应用属于高级用法,不建议在业务代码滥用
/// </summary> /// </summary>
public static Func<T2, TResult> Partial<T1, T2, TResult>( public static Func<T2, TResult> Partial<T1, T2, TResult>(
this Func<T1, T2, TResult> func, this Func<T1, T2, TResult> func,
T1 firstArg) T1 firstArg)
=> second => func(firstArg, second); {
return second => func(firstArg, second);
}
#endregion #endregion
} }

View File

@ -21,7 +21,6 @@ public static class PipeExtensions
/// <summary> /// <summary>
/// Also /// Also
/// 对值执行副作用操作并返回原值 /// 对值执行副作用操作并返回原值
///
/// 适用于日志、调试、状态同步等场景 /// 适用于日志、调试、状态同步等场景
/// </summary> /// </summary>
public static T Also<T>( public static T Also<T>(

View File

@ -28,7 +28,7 @@ public interface IDataLocation
/// <summary> /// <summary>
/// 存储类型Local / Remote / Database / Memory /// 存储类型Local / Remote / Database / Memory
/// </summary> /// </summary>
StorageKind Kind { get; } StorageKinds Kinds { get; }
/// <summary> /// <summary>
/// 命名空间/分区 /// 命名空间/分区

View File

@ -10,6 +10,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
namespace GFramework.Game.Abstractions.data; namespace GFramework.Game.Abstractions.data;
/// <summary> /// <summary>

View File

@ -18,4 +18,3 @@ namespace GFramework.Game.Abstractions.data.events;
/// </summary> /// </summary>
/// <param name="Location">数据位置信息,标识被删除数据的位置</param> /// <param name="Location">数据位置信息,标识被删除数据的位置</param>
public sealed record DataDeletedEvent(IDataLocation Location); public sealed record DataDeletedEvent(IDataLocation Location);

View File

@ -18,7 +18,7 @@ namespace GFramework.Game.Abstractions.enums;
/// 此枚举使用 Flags 特性,支持位运算组合多个存储类型 /// 此枚举使用 Flags 特性,支持位运算组合多个存储类型
/// </summary> /// </summary>
[Flags] [Flags]
public enum StorageKind public enum StorageKinds
{ {
/// <summary> /// <summary>
/// 无存储类型 /// 无存储类型
@ -43,5 +43,5 @@ public enum StorageKind
/// <summary> /// <summary>
/// 数据库存储 /// 数据库存储
/// </summary> /// </summary>
Database = 1 << 3, Database = 1 << 3
} }

View File

@ -48,10 +48,7 @@ public class AudioSettings : ISettingsData
public void LoadFrom(ISettingsData source) public void LoadFrom(ISettingsData source)
{ {
// 检查数据源是否为音频设置类型 // 检查数据源是否为音频设置类型
if (source is not AudioSettings audioSettings) if (source is not AudioSettings audioSettings) return;
{
return;
}
// 将源数据中的各个音量设置复制到当前对象 // 将源数据中的各个音量设置复制到当前对象
MasterVolume = audioSettings.MasterVolume; MasterVolume = audioSettings.MasterVolume;

View File

@ -47,10 +47,7 @@ public class GraphicsSettings : ISettingsData
public void LoadFrom(ISettingsData source) public void LoadFrom(ISettingsData source)
{ {
// 检查源数据是否为GraphicsSettings类型如果不是则直接返回 // 检查源数据是否为GraphicsSettings类型如果不是则直接返回
if (source is not GraphicsSettings settings) if (source is not GraphicsSettings settings) return;
{
return;
}
// 将源设置中的属性值复制到当前对象 // 将源设置中的属性值复制到当前对象
Fullscreen = settings.Fullscreen; Fullscreen = settings.Fullscreen;

View File

@ -54,10 +54,7 @@ public class LocalizationSettings : ISettingsData
/// </remarks> /// </remarks>
public void LoadFrom(ISettingsData source) public void LoadFrom(ISettingsData source)
{ {
if (source is not LocalizationSettings settings) if (source is not LocalizationSettings settings) return;
{
return;
}
Language = settings.Language; Language = settings.Language;
Version = settings.Version; Version = settings.Version;

View File

@ -35,11 +35,6 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
throw new InvalidOperationException( throw new InvalidOperationException(
"Failed to initialize storage. No IStorage utility found in context."); "Failed to initialize storage. No IStorage utility found in context.");
protected override void OnInit()
{
_storage ??= this.GetUtility<IStorage>()!;
}
/// <summary> /// <summary>
/// 异步加载指定位置的数据 /// 异步加载指定位置的数据
/// </summary> /// </summary>
@ -96,7 +91,9 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
/// <param name="location">数据位置信息</param> /// <param name="location">数据位置信息</param>
/// <returns>如果数据存在返回true否则返回false</returns> /// <returns>如果数据存在返回true否则返回false</returns>
public Task<bool> ExistsAsync(IDataLocation location) public Task<bool> ExistsAsync(IDataLocation location)
=> Storage.ExistsAsync(location.ToStorageKey()); {
return Storage.ExistsAsync(location.ToStorageKey());
}
/// <summary> /// <summary>
/// 异步删除指定位置的数据 /// 异步删除指定位置的数据
@ -117,12 +114,14 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
public async Task SaveAllAsync(IEnumerable<(IDataLocation location, IData data)> dataList) public async Task SaveAllAsync(IEnumerable<(IDataLocation location, IData data)> dataList)
{ {
var valueTuples = dataList.ToList(); var valueTuples = dataList.ToList();
foreach (var (location, data) in valueTuples) foreach (var (location, data) in valueTuples) await SaveAsync(location, data);
{
await SaveAsync(location, data);
}
if (_options.EnableEvents) if (_options.EnableEvents)
this.SendEvent(new DataBatchSavedEvent(valueTuples)); this.SendEvent(new DataBatchSavedEvent(valueTuples));
} }
protected override void OnInit()
{
_storage ??= this.GetUtility<IStorage>()!;
}
} }

View File

@ -209,13 +209,9 @@ public class UnifiedSettingsDataRepository(
var key = UnifiedKey; var key = UnifiedKey;
if (await Storage.ExistsAsync(key)) if (await Storage.ExistsAsync(key))
{
_file = await Storage.ReadAsync<UnifiedSettingsFile>(key); _file = await Storage.ReadAsync<UnifiedSettingsFile>(key);
}
else else
{
_file = new UnifiedSettingsFile { Version = 1 }; _file = new UnifiedSettingsFile { Version = 1 };
}
_loaded = true; _loaded = true;
} }

View File

@ -114,7 +114,6 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
} }
foreach (var data in _data.Values) foreach (var data in _data.Values)
{
try try
{ {
var type = data.GetType(); var type = data.GetType();
@ -135,7 +134,6 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
{ {
Log.Error($"Failed to initialize settings data: {data.GetType().Name}", ex); Log.Error($"Failed to initialize settings data: {data.GetType().Name}", ex);
} }
}
this.SendEvent(new SettingsInitializedEvent()); this.SendEvent(new SettingsInitializedEvent());
} }
@ -147,7 +145,6 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
public async Task SaveAllAsync() public async Task SaveAllAsync()
{ {
foreach (var data in _data.Values) foreach (var data in _data.Values)
{
try try
{ {
var location = LocationProvider.GetLocation(data.GetType()); var location = LocationProvider.GetLocation(data.GetType());
@ -157,7 +154,6 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
{ {
Log.Error($"Failed to save settings data: {data.GetType().Name}", ex); Log.Error($"Failed to save settings data: {data.GetType().Name}", ex);
} }
}
this.SendEvent(new SettingsSavedAllEvent()); this.SendEvent(new SettingsSavedAllEvent());
} }
@ -168,7 +164,6 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
public async Task ApplyAllAsync() public async Task ApplyAllAsync()
{ {
foreach (var applicator in _applicators) foreach (var applicator in _applicators)
{
try try
{ {
await applicator.Value.Apply(); await applicator.Value.Apply();
@ -177,7 +172,6 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
{ {
Log.Error($"Failed to apply settings: {applicator.GetType().Name}", ex); Log.Error($"Failed to apply settings: {applicator.GetType().Name}", ex);
} }
}
this.SendEvent(new SettingsAppliedAllEvent()); this.SendEvent(new SettingsAppliedAllEvent());
} }

View File

@ -1,5 +1,4 @@
using System.Collections.Concurrent; using System.IO;
using System.IO;
using System.Text; using System.Text;
using GFramework.Core.Abstractions.serializer; using GFramework.Core.Abstractions.serializer;
using GFramework.Game.Abstractions.storage; using GFramework.Game.Abstractions.storage;