feat(coroutine): 扩展协程功能并重构指令结构

- 将协程等待指令移动到instructions命名空间下
- 添加WaitForProgress指令支持带进度回调的时间等待
- 添加WaitForAllCoroutines指令用于等待多个协程完成
- 添加AsyncOperation类用于桥接协程与async/await模型
- 添加协程扩展方法包括RepeatEvery、ExecuteAfter、Sequence等功能
- 添加Task与协程的转换扩展方法AsCoroutineInstruction
- 添加协程调度器的ParallelCoroutines扩展方法
- 添加IsCoroutineAlive方法检查协程状态
- 更新相关测试文件以匹配新的命名空间结构
This commit is contained in:
GeWuYou 2026-01-26 09:44:07 +08:00
parent 27bc481577
commit 01d9fefa1d
23 changed files with 1816 additions and 10 deletions

View File

@ -0,0 +1,342 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;
/// <summary>
/// AsyncOperation的单元测试类
/// 测试内容包括:
/// - 初始化状态
/// - 完成和状态检查
/// - 异常处理
/// - 延续操作
/// - GetAwaiter
/// - IsCompleted属性
/// </summary>
[TestFixture]
public class AsyncOperationTests
{
/// <summary>
/// 验证AsyncOperation初始状态为未完成
/// </summary>
[Test]
public void AsyncOperation_Should_Not_Be_Done_Initially()
{
var op = new AsyncOperation();
Assert.That(op.IsDone, Is.False);
}
/// <summary>
/// 验证AsyncOperation初始状态IsCompleted为false
/// </summary>
[Test]
public void AsyncOperation_Should_Not_Be_Completed_Initially()
{
var op = new AsyncOperation();
Assert.That(op.IsCompleted, Is.False);
}
/// <summary>
/// 验证SetCompleted后IsDone应该为true
/// </summary>
[Test]
public void SetCompleted_Should_Set_IsDone_To_True()
{
var op = new AsyncOperation();
op.SetCompleted();
Assert.That(op.IsDone, Is.True);
}
/// <summary>
/// 验证SetCompleted后IsCompleted应该为true
/// </summary>
[Test]
public void SetCompleted_Should_Set_IsCompleted_To_True()
{
var op = new AsyncOperation();
op.SetCompleted();
Assert.That(op.IsCompleted, Is.True);
}
/// <summary>
/// 验证SetCompleted只能被调用一次
/// </summary>
[Test]
public void SetCompleted_Should_Be_Idempotent()
{
var op = new AsyncOperation();
op.SetCompleted();
op.SetCompleted();
op.SetCompleted();
Assert.That(op.IsDone, Is.True);
}
/// <summary>
/// 验证SetException后IsDone应该为true
/// </summary>
[Test]
public void SetException_Should_Set_IsDone_To_True()
{
var op = new AsyncOperation();
op.SetException(new InvalidOperationException("Test exception"));
Assert.That(op.IsDone, Is.True);
}
/// <summary>
/// 验证SetException后Task应该包含异常
/// </summary>
[Test]
public void SetException_Should_Set_Exception_On_Task()
{
var op = new AsyncOperation();
var expectedException = new InvalidOperationException("Test exception");
op.SetException(expectedException);
Assert.That(async () => await op.Task, Throws.InstanceOf<InvalidOperationException>());
}
/// <summary>
/// 验证OnCompleted应该在已完成时立即执行延续
/// </summary>
[Test]
public void OnCompleted_Should_Execute_Immediately_When_Already_Completed()
{
var op = new AsyncOperation();
var continuationCalled = false;
op.SetCompleted();
op.OnCompleted(() => continuationCalled = true);
Assert.That(continuationCalled, Is.True);
}
/// <summary>
/// 验证OnCompleted应该在未完成时不立即执行延续
/// </summary>
[Test]
public void OnCompleted_Should_Not_Execute_Immediately_When_Not_Completed()
{
var op = new AsyncOperation();
var continuationCalled = false;
op.OnCompleted(() => continuationCalled = true);
Assert.That(continuationCalled, Is.False);
}
/// <summary>
/// 验证延续应该在SetCompleted后被调用
/// </summary>
[Test]
public void Continuation_Should_Be_Called_After_SetCompleted()
{
var op = new AsyncOperation();
var continuationCalled = false;
op.OnCompleted(() => continuationCalled = true);
op.SetCompleted();
Assert.That(continuationCalled, Is.True);
}
/// <summary>
/// 验证多个延续应该都能被调用
/// </summary>
[Test]
public void Multiple_Continuations_Should_All_Be_Called()
{
var op = new AsyncOperation();
var callCount = 0;
op.OnCompleted(() => callCount++);
op.OnCompleted(() => callCount++);
op.OnCompleted(() => callCount++);
op.SetCompleted();
Assert.That(callCount, Is.EqualTo(3));
}
/// <summary>
/// 验证延续应该在SetException后被调用
/// </summary>
[Test]
public void Continuation_Should_Be_Called_After_SetException()
{
var op = new AsyncOperation();
var continuationCalled = false;
op.OnCompleted(() => continuationCalled = true);
op.SetException(new InvalidOperationException("Test"));
Assert.That(continuationCalled, Is.True);
}
/// <summary>
/// 验证SetCompleted后设置的延续也应该被调用
/// </summary>
[Test]
public void Continuation_Registered_After_Completed_Should_Be_Called()
{
var op = new AsyncOperation();
var firstCalled = false;
var secondCalled = false;
op.OnCompleted(() => firstCalled = true);
op.SetCompleted();
op.OnCompleted(() => secondCalled = true);
Assert.That(firstCalled, Is.True);
Assert.That(secondCalled, Is.True);
}
/// <summary>
/// 验证GetAwaiter应该返回自身
/// </summary>
[Test]
public void GetAwaiter_Should_Return_Self()
{
var op = new AsyncOperation();
var awaiter = op.GetAwaiter();
Assert.That(awaiter, Is.SameAs(op));
}
/// <summary>
/// 验证Update方法不应该改变状态
/// </summary>
[Test]
public void Update_Should_Not_Change_State()
{
var op = new AsyncOperation();
op.Update(0.1);
Assert.That(op.IsDone, Is.False);
}
/// <summary>
/// 验证AsyncOperation实现IYieldInstruction接口
/// </summary>
[Test]
public void AsyncOperation_Should_Implement_IYieldInstruction()
{
var op = new AsyncOperation();
Assert.That(op, Is.InstanceOf<IYieldInstruction>());
}
/// <summary>
/// 验证Task属性应该返回有效的Task
/// </summary>
[Test]
public void Task_Property_Should_Return_Valid_Task()
{
var op = new AsyncOperation();
Assert.That(op.Task, Is.Not.Null);
}
/// <summary>
/// 验证SetCompleted后Task应该完成
/// </summary>
[Test]
public async Task Task_Should_Complete_After_SetCompleted()
{
var op = new AsyncOperation();
op.SetCompleted();
await op.Task;
Assert.That(op.Task.IsCompleted, Is.True);
}
/// <summary>
/// 验证SetException后Task应该失败
/// </summary>
[Test]
public void Task_Should_Fault_After_SetException()
{
var op = new AsyncOperation();
op.SetException(new InvalidOperationException("Test"));
Assert.That(op.Task.IsFaulted, Is.True);
}
/// <summary>
/// 验证SetCompleted只能设置一次
/// </summary>
[Test]
public void SetCompleted_Should_Only_Set_Once()
{
var op = new AsyncOperation();
var firstCallCompleted = false;
var secondCallCompleted = false;
op.OnCompleted(() => firstCallCompleted = true);
op.SetCompleted();
op.OnCompleted(() => secondCallCompleted = true);
op.SetCompleted();
Assert.That(firstCallCompleted, Is.True);
Assert.That(secondCallCompleted, Is.True);
}
/// <summary>
/// 验证SetException只能在未完成时设置
/// </summary>
[Test]
public void SetException_Should_Not_Work_After_SetCompleted()
{
var op = new AsyncOperation();
op.SetCompleted();
op.SetException(new InvalidOperationException("Test"));
Assert.That(op.Task.IsCompletedSuccessfully, Is.True);
Assert.That(op.Task.IsFaulted, Is.False);
}
/// <summary>
/// 验证SetCompleted不能在SetException后设置
/// </summary>
[Test]
public void SetCompleted_Should_Not_Work_After_SetException()
{
var op = new AsyncOperation();
op.SetException(new InvalidOperationException("Test"));
op.SetCompleted();
Assert.That(op.Task.IsFaulted, Is.True);
Assert.That(op.Task.IsCompletedSuccessfully, Is.False);
}
/// <summary>
/// 验证延续抛出的异常应该被捕获
/// </summary>
[Test]
public void Continuation_Exception_Should_Be_Caught()
{
var op = new AsyncOperation();
op.OnCompleted(() => throw new InvalidOperationException("Test exception"));
Assert.DoesNotThrow(() => op.SetCompleted());
}
}

View File

@ -1,5 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;

View File

@ -1,5 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;

View File

@ -0,0 +1,295 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine;
using GFramework.Core.coroutine.extensions;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;
/// <summary>
/// TaskCoroutineExtensions的单元测试类
/// 测试内容包括:
/// - AsCoroutineInstruction方法
/// - StartTaskAsCoroutine方法
/// </summary>
[TestFixture]
public class TaskCoroutineExtensionsTests
{
/// <summary>
/// 验证AsCoroutineInstruction应该返回WaitForTask
/// </summary>
[Test]
public void AsCoroutineInstruction_Should_Return_WaitForTask()
{
var task = Task.CompletedTask;
var instruction = task.AsCoroutineInstruction();
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
}
/// <summary>
/// 验证AsCoroutineInstruction<T>应该返回WaitForTask<T>
/// </summary>
[Test]
public void AsCoroutineInstructionOfT_Should_Return_WaitForTaskOfT()
{
var task = Task.FromResult(42);
var instruction = task.AsCoroutineInstruction<int>();
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
}
/// <summary>
/// 验证AsCoroutineInstruction可以处理已完成的Task
/// </summary>
[Test]
public void AsCoroutineInstruction_Should_Handle_Completed_Task()
{
var task = Task.CompletedTask;
var instruction = task.AsCoroutineInstruction();
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
}
/// <summary>
/// 验证AsCoroutineInstruction<T>可以处理已完成的Task
/// </summary>
[Test]
public void AsCoroutineInstructionOfT_Should_Handle_Completed_Task()
{
var task = Task.FromResult(42);
var instruction = task.AsCoroutineInstruction<int>();
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
}
/// <summary>
/// 验证AsCoroutineInstruction<T>应该能够访问Task结果
/// </summary>
[Test]
public void AsCoroutineInstructionOfT_Should_Access_Task_Result()
{
var task = Task.FromResult(42);
var instruction = task.AsCoroutineInstruction<int>();
task.Wait();
Assert.That(instruction.Result, Is.EqualTo(42));
}
/// <summary>
/// 验证AsCoroutineInstruction应该处理null Task抛出异常
/// </summary>
[Test]
public void AsCoroutineInstruction_Should_Handle_Null_Task()
{
Task task = null!;
Assert.Throws<ArgumentNullException>(() => task.AsCoroutineInstruction());
}
/// <summary>
/// 验证AsCoroutineInstruction<T>应该处理null Task抛出异常
/// </summary>
[Test]
public void AsCoroutineInstructionOfT_Should_Handle_Null_Task()
{
Task<int> task = null!;
Assert.Throws<ArgumentNullException>(() => task.AsCoroutineInstruction<int>());
}
/// <summary>
/// 验证AsCoroutineInstruction应该处理失败的Task
/// </summary>
[Test]
public void AsCoroutineInstruction_Should_Handle_Faulted_Task()
{
var task = Task.FromException(new InvalidOperationException("Test exception"));
var instruction = task.AsCoroutineInstruction();
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
}
/// <summary>
/// 验证AsCoroutineInstruction<T>应该处理失败的Task
/// </summary>
[Test]
public void AsCoroutineInstructionOfT_Should_Handle_Faulted_Task()
{
var task = Task.FromException<int>(new InvalidOperationException("Test exception"));
var instruction = task.AsCoroutineInstruction<int>();
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
}
/// <summary>
/// 验证StartTaskAsCoroutine应该返回有效的协程句柄
/// </summary>
[Test]
public void StartTaskAsCoroutine_Should_Return_Valid_Handle()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var task = Task.CompletedTask;
var handle = scheduler.StartTaskAsCoroutine(task);
Assert.That(handle.IsValid, Is.True);
}
/// <summary>
/// 验证StartTaskAsCoroutine<T>应该返回有效的协程句柄
/// </summary>
[Test]
public void StartTaskAsCoroutineOfT_Should_Return_Valid_Handle()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var task = Task.FromResult(42);
var handle = scheduler.StartTaskAsCoroutine(task);
Assert.That(handle.IsValid, Is.True);
}
/// <summary>
/// 验证StartTaskAsCoroutine应该等待Task完成
/// </summary>
[Test]
public void StartTaskAsCoroutine_Should_Wait_For_Task_Completion()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var completed = false;
var tcs = new TaskCompletionSource<object?>();
var handle = scheduler.StartTaskAsCoroutine(tcs.Task);
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
Assert.That(completed, Is.False);
tcs.SetResult(null);
Task.Delay(50).Wait();
scheduler.Update();
scheduler.Update();
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
}
/// <summary>
/// 验证StartTaskAsCoroutine<T>应该等待Task完成
/// </summary>
[Test]
public void StartTaskAsCoroutineOfT_Should_Wait_For_Task_Completion()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var tcs = new TaskCompletionSource<int>();
var handle = scheduler.StartTaskAsCoroutine(tcs.Task);
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
tcs.SetResult(42);
Task.Delay(50).Wait();
scheduler.Update();
scheduler.Update();
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
}
/// <summary>
/// 验证StartTaskAsCoroutine应该处理已完成的Task
/// </summary>
[Test]
public void StartTaskAsCoroutine_Should_Handle_Completed_Task()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var task = Task.CompletedTask;
var handle = scheduler.StartTaskAsCoroutine(task);
scheduler.Update();
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
}
/// <summary>
/// 验证StartTaskAsCoroutine应该处理失败的Task
/// </summary>
[Test]
public void StartTaskAsCoroutine_Should_Handle_Faulted_Task()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var task = Task.FromException(new InvalidOperationException("Test"));
var handle = scheduler.StartTaskAsCoroutine(task);
Assert.DoesNotThrow(() => scheduler.Update());
}
/// <summary>
/// 验证StartTaskAsCoroutine<T>应该处理失败的Task
/// </summary>
[Test]
public void StartTaskAsCoroutineOfT_Should_Handle_Faulted_Task()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var task = Task.FromException<int>(new InvalidOperationException("Test"));
scheduler.StartTaskAsCoroutine(task);
Assert.DoesNotThrow(() => scheduler.Update());
}
/// <summary>
/// 验证StartTaskAsCoroutine应该与调度器正常协作
/// </summary>
[Test]
public void StartTaskAsCoroutine_Should_Work_With_Scheduler()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var tcs = new TaskCompletionSource<object?>();
scheduler.StartTaskAsCoroutine(tcs.Task);
var tcs2 = new TaskCompletionSource<int>();
scheduler.StartTaskAsCoroutine(tcs2.Task);
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(2));
tcs.SetResult(null);
tcs2.SetResult(42);
Task.Delay(50).Wait();
scheduler.Update();
scheduler.Update();
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
}
/// <summary>
/// 测试用时间源类
/// </summary>
private class TestTimeSource : ITimeSource
{
public double CurrentTime { get; private set; }
public double DeltaTime { get; private set; }
public void Update()
{
DeltaTime = 0.1;
CurrentTime += DeltaTime;
}
}
}

View File

@ -0,0 +1,294 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;
/// <summary>
/// WaitForProgress的单元测试类
/// 测试内容包括:
/// - 初始化和基本功能
/// - 进度回调
/// - 边界条件
/// - 异常处理
/// </summary>
[TestFixture]
public class WaitForProgressTests
{
/// <summary>
/// 验证WaitForProgress初始状态为未完成
/// </summary>
[Test]
public void WaitForProgress_Should_Not_Be_Done_Initially()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
Assert.That(wait.IsDone, Is.False);
}
/// <summary>
/// 验证WaitForProgress应该在指定时间后完成
/// </summary>
[Test]
public void WaitForProgress_Should_Be_Done_After_Duration()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.5);
Assert.That(wait.IsDone, Is.False);
wait.Update(0.5);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForProgress应该在Update时调用进度回调
/// </summary>
[Test]
public void WaitForProgress_Should_Call_Progress_Callback_On_Update()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.25);
Assert.That(progressValues.Count, Is.EqualTo(1));
}
/// <summary>
/// 验证进度值应该在0到1之间
/// </summary>
[Test]
public void WaitForProgress_Should_Have_Progress_Between_0_And_1()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
while (!wait.IsDone)
{
wait.Update(0.1);
}
foreach (var progress in progressValues)
{
Assert.That(progress, Is.GreaterThanOrEqualTo(0.0f));
Assert.That(progress, Is.LessThanOrEqualTo(1.0f));
}
}
/// <summary>
/// 验证进度值应该随着时间增加
/// </summary>
[Test]
public void WaitForProgress_Should_Increase_Progress_Over_Time()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.25);
Assert.That(progressValues[0], Is.EqualTo(0.25f).Within(0.01f));
wait.Update(0.25);
Assert.That(progressValues[1], Is.EqualTo(0.5f).Within(0.01f));
wait.Update(0.25);
Assert.That(progressValues[2], Is.EqualTo(0.75f).Within(0.01f));
wait.Update(0.25);
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress应该抛出ArgumentNullException当回调为null
/// </summary>
[Test]
public void WaitForProgress_Should_Throw_ArgumentNullException_When_Callback_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new WaitForProgress(1.0, null!));
}
/// <summary>
/// 验证WaitForProgress应该抛出ArgumentException当duration为0
/// </summary>
[Test]
public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Zero()
{
Assert.Throws<ArgumentException>(() => new WaitForProgress(0, _ => { }));
}
/// <summary>
/// 验证WaitForProgress应该抛出ArgumentException当duration为负数
/// </summary>
[Test]
public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Negative()
{
Assert.Throws<ArgumentException>(() => new WaitForProgress(-1.0, _ => { }));
}
/// <summary>
/// 验证WaitForProgress应该处理超过duration的更新
/// </summary>
[Test]
public void WaitForProgress_Should_Clamp_Progress_To_1_When_Exceeding_Duration()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.5);
wait.Update(0.5);
wait.Update(0.5);
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress可以处理精确的duration
/// </summary>
[Test]
public void WaitForProgress_Should_Handle_Exact_Duration()
{
var progressValues = new List<float>();
var duration = 1.0;
var wait = new WaitForProgress(duration, progressValues.Add);
wait.Update(duration);
Assert.That(wait.IsDone, Is.True);
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress可以处理不同的delta time
/// </summary>
[Test]
public void WaitForProgress_Should_Handle_Variable_Delta_Time()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.1);
wait.Update(0.05);
wait.Update(0.15);
wait.Update(0.2);
wait.Update(0.5);
Assert.That(wait.IsDone, Is.True);
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress可以处理多次Update
/// </summary>
[Test]
public void WaitForProgress_Should_Handle_Multiple_Updates()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
var updateCount = 0;
while (!wait.IsDone && updateCount < 100)
{
wait.Update(0.01);
updateCount++;
}
Assert.That(wait.IsDone, Is.True);
Assert.That(progressValues.Count, Is.GreaterThan(0));
}
/// <summary>
/// 验证WaitForProgress应该确保最后一个进度为1.0
/// </summary>
[Test]
public void WaitForProgress_Should_Ensure_Final_Progress_Is_1()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
while (!wait.IsDone)
{
wait.Update(0.1);
}
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress实现IYieldInstruction接口
/// </summary>
[Test]
public void WaitForProgress_Should_Implement_IYieldInstruction()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
}
/// <summary>
/// 验证WaitForProgress可以处理很短的duration
/// </summary>
[Test]
public void WaitForProgress_Should_Handle_Short_Duration()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(0.001, progressValues.Add);
wait.Update(0.001);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForProgress可以处理很长的duration
/// </summary>
[Test]
public void WaitForProgress_Should_Handle_Long_Duration()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(100.0, progressValues.Add);
wait.Update(50.0);
Assert.That(wait.IsDone, Is.False);
wait.Update(50.0);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForProgress在完成前不会超过1.0
/// </summary>
[Test]
public void WaitForProgress_Should_Not_Exceed_1_Before_Completion()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.9);
Assert.That(progressValues[^1], Is.LessThan(1.0f));
wait.Update(0.05);
Assert.That(progressValues[^1], Is.LessThanOrEqualTo(1.0f));
wait.Update(0.05);
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress的Update方法不影响未完成状态
/// </summary>
[Test]
public void WaitForProgress_Update_Should_Not_Affect_Before_Completion()
{
var progressValues = new List<float>();
var wait = new WaitForProgress(1.0, progressValues.Add);
wait.Update(0.1);
Assert.That(wait.IsDone, Is.False);
wait.Update(0.1);
Assert.That(wait.IsDone, Is.False);
}
}

View File

@ -0,0 +1,311 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;
/// <summary>
/// WaitForTask的单元测试类
/// 测试内容包括:
/// - WaitForTask初始化和等待
/// - WaitForTask<T>初始化、等待和结果获取
/// - 异常处理
/// - 边界条件
/// </summary>
[TestFixture]
public class WaitForTaskTests
{
/// <summary>
/// 验证WaitForTask初始状态为未完成
/// </summary>
[Test]
public void WaitForTask_Should_Not_Be_Done_Initially()
{
var task = Task.Run(() => { });
var wait = new WaitForTask(task);
Assert.That(wait.IsDone, Is.False);
}
/// <summary>
/// 验证WaitForTask应该在Task完成后完成
/// </summary>
[Test]
public void WaitForTask_Should_Be_Done_After_Task_Completes()
{
var tcs = new TaskCompletionSource<object?>();
var wait = new WaitForTask(tcs.Task);
Assert.That(wait.IsDone, Is.False);
tcs.SetResult(null);
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForTask应该处理已完成的Task
/// </summary>
[Test]
public void WaitForTask_Should_Handle_Already_Completed_Task()
{
var task = Task.CompletedTask;
Task.Delay(100).Wait();
var wait = new WaitForTask(task);
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForTask应该处理失败的Task
/// </summary>
[Test]
public void WaitForTask_Should_Handle_Faulted_Task()
{
var tcs = new TaskCompletionSource<object?>();
var wait = new WaitForTask(tcs.Task);
tcs.SetException(new InvalidOperationException("Test exception"));
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForTask应该处理取消的Task
/// </summary>
[Test]
public void WaitForTask_Should_Handle_Cancelled_Task()
{
var cts = new CancellationTokenSource();
cts.Cancel();
var task = Task.FromCanceled(cts.Token);
Task.Delay(100).Wait();
var wait = new WaitForTask(task);
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForTask应该抛出ArgumentNullException当task为null
/// </summary>
[Test]
public void WaitForTask_Should_Throw_ArgumentNullException_When_Task_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new WaitForTask(null!));
}
/// <summary>
/// 验证WaitForTask<T>初始状态为未完成
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Not_Be_Done_Initially()
{
var tcs = new TaskCompletionSource<int>();
var wait = new WaitForTask<int>(tcs.Task);
Assert.That(wait.IsDone, Is.False);
}
/// <summary>
/// 验证WaitForTask<T>应该在Task完成后完成
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Be_Done_After_Task_Completes()
{
var tcs = new TaskCompletionSource<int>();
var wait = new WaitForTask<int>(tcs.Task);
Assert.That(wait.IsDone, Is.False);
tcs.SetResult(42);
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForTask<T>应该返回Task的结果
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Return_Task_Result()
{
var tcs = new TaskCompletionSource<int>();
var wait = new WaitForTask<int>(tcs.Task);
tcs.SetResult(42);
Task.Delay(100).Wait();
Assert.That(wait.Result, Is.EqualTo(42));
}
/// <summary>
/// 验证WaitForTask<T>应该处理已完成的Task
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Handle_Already_Completed_Task()
{
var task = Task.FromResult(42);
Task.Delay(100).Wait();
var wait = new WaitForTask<int>(task);
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
Assert.That(wait.Result, Is.EqualTo(42));
}
/// <summary>
/// 验证WaitForTask<T>应该处理失败的Task
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Handle_Faulted_Task()
{
var tcs = new TaskCompletionSource<int>();
var wait = new WaitForTask<int>(tcs.Task);
tcs.SetException(new InvalidOperationException("Test exception"));
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
Assert.Throws<InvalidOperationException>(() => { var _ = wait.Result; });
}
/// <summary>
/// 验证WaitForTask<T>应该抛出ArgumentNullException当task为null
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Throw_ArgumentNullException_When_Task_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new WaitForTask<int>(null!));
}
/// <summary>
/// 验证WaitForTask实现IYieldInstruction接口
/// </summary>
[Test]
public void WaitForTask_Should_Implement_IYieldInstruction()
{
var task = Task.CompletedTask;
var wait = new WaitForTask(task);
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
}
/// <summary>
/// 验证WaitForTask<T>实现IYieldInstruction接口
/// </summary>
[Test]
public void WaitForTaskOfT_Should_Implement_IYieldInstruction()
{
var task = Task.FromResult(42);
var wait = new WaitForTask<int>(task);
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
}
/// <summary>
/// 验证WaitForTask的Update方法不影响状态
/// </summary>
[Test]
public void WaitForTask_Update_Should_Not_Affect_State()
{
var tcs = new TaskCompletionSource<object?>();
var wait = new WaitForTask(tcs.Task);
wait.Update(0.1);
Assert.That(wait.IsDone, Is.False);
wait.Update(1.0);
Assert.That(wait.IsDone, Is.False);
}
/// <summary>
/// 验证WaitForTask<T>的Update方法不影响状态
/// </summary>
[Test]
public void WaitForTaskOfT_Update_Should_Not_Affect_State()
{
var tcs = new TaskCompletionSource<int>();
var wait = new WaitForTask<int>(tcs.Task);
wait.Update(0.1);
Assert.That(wait.IsDone, Is.False);
wait.Update(1.0);
Assert.That(wait.IsDone, Is.False);
}
/// <summary>
/// 验证WaitForTask在延迟完成后能够正确等待
/// </summary>
[Test]
public void WaitForTask_Should_Wait_For_Delayed_Task()
{
var delayMs = 100;
var task = Task.Delay(delayMs);
var wait = new WaitForTask(task);
Assert.That(wait.IsDone, Is.False);
Task.Delay(delayMs + 50).Wait();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForTask<T>在异步操作完成后能够正确获取结果
/// </summary>
[Test]
public async Task WaitForTaskOfT_Should_Get_Result_From_Async_Operation()
{
var expectedValue = 123;
var task = Task.Run(async () =>
{
await Task.Delay(50);
return expectedValue;
});
var wait = new WaitForTask<int>(task);
await task;
Task.Delay(100).Wait();
Assert.That(wait.IsDone, Is.True);
Assert.That(wait.Result, Is.EqualTo(expectedValue));
}
/// <summary>
/// 验证WaitForTask应该处理长时间运行的Task
/// </summary>
[Test]
public void WaitForTask_Should_Handle_Long_Running_Task()
{
var task = Task.Delay(200);
var wait = new WaitForTask(task);
Assert.That(wait.IsDone, Is.False);
Task.Delay(250).Wait();
Assert.That(wait.IsDone, Is.True);
}
}

View File

@ -1,5 +1,5 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine;
using GFramework.Core.coroutine.instructions;
using NUnit.Framework;
namespace GFramework.Core.Tests.coroutine;

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading;
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine.instructions;
namespace GFramework.Core.coroutine;
@ -59,6 +57,17 @@ public static class CoroutineHelper
return new WaitWhile(predicate);
}
/// <summary>
/// 等待指定时间并提供进度回调
/// </summary>
/// <param name="duration">等待的持续时间(秒)</param>
/// <param name="onProgress">进度回调函数接收0-1之间的进度值</param>
/// <returns>等待进度指令</returns>
public static WaitForProgress WaitForProgress(double duration, Action<float> onProgress)
{
return new WaitForProgress(duration, onProgress);
}
/// <summary>
/// 延迟调用指定的委托
/// </summary>

View File

@ -368,4 +368,8 @@ public sealed class CoroutineScheduler(
}
#endregion
public bool IsCoroutineAlive(CoroutineHandle handle)
{
return _metadata.ContainsKey(handle);
}
}

View File

@ -0,0 +1,131 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine.instructions;
namespace GFramework.Core.coroutine.extensions;
/// <summary>
/// 协程相关的扩展方法
/// </summary>
public static class CoroutineExtensions
{
/// <summary>
/// 在指定时间间隔内重复执行动作的协程
/// </summary>
/// <param name="interval">执行间隔时间(秒)</param>
/// <param name="action">要重复执行的动作</param>
/// <param name="count">重复次数如果为null则无限重复</param>
/// <returns>协程枚举器</returns>
public static IEnumerator<IYieldInstruction> RepeatEvery(
double interval,
Action action,
int? count = null)
{
if (count is < 0)
{
yield break;
}
var executedCount = 0;
while (count == null || executedCount < count)
{
action?.Invoke();
yield return new Delay(interval);
executedCount++;
}
}
/// <summary>
/// 在指定延迟后执行动作的协程
/// </summary>
/// <param name="delay">延迟时间(秒)</param>
/// <param name="action">要执行的动作</param>
/// <returns>协程枚举器</returns>
public static IEnumerator<IYieldInstruction> ExecuteAfter(
double delay,
Action? action)
{
if (delay < 0)
{
yield break;
}
yield return new Delay(delay);
action?.Invoke();
}
/// <summary>
/// 顺序执行多个协程
/// </summary>
/// <param name="coroutines">要顺序执行的协程集合</param>
/// <returns>协程枚举器</returns>
public static IEnumerator<IYieldInstruction> Sequence(
params IEnumerator<IYieldInstruction>[] coroutines)
{
foreach (var coroutine in coroutines)
{
while (coroutine.MoveNext())
{
yield return coroutine.Current;
}
// 清理协程
coroutine.Dispose();
}
}
/// <summary>
/// 并行执行多个协程(等待所有协程完成)
/// 注意:这需要协程调度器的支持,这里提供一个包装器返回多个句柄
/// </summary>
/// <param name="scheduler">协程调度器</param>
/// <param name="coroutines">要并行执行的协程集合</param>
/// <returns>等待所有协程完成的协程</returns>
public static IEnumerator<IYieldInstruction> ParallelCoroutines(
this CoroutineScheduler scheduler,
params IEnumerator<IYieldInstruction>[]? coroutines)
{
if (coroutines == null || coroutines.Length == 0)
{
yield break;
}
// 启动所有协程并收集句柄
var handles = new List<CoroutineHandle>();
foreach (var coroutine in coroutines)
{
var handle = scheduler.Run(coroutine);
handles.Add(handle);
}
// 等待所有协程完成
yield return new WaitForAllCoroutines(scheduler,handles);
}
/// <summary>
/// 带进度回调的等待协程
/// </summary>
/// <param name="totalTime">总等待时间(秒)</param>
/// <param name="onProgress">进度回调参数为0-1之间的进度值</param>
/// <returns>协程枚举器</returns>
public static IEnumerator<IYieldInstruction> WaitForSecondsWithProgress(
double totalTime,
Action<float>? onProgress)
{
if (totalTime <= 0)
{
onProgress?.Invoke(1.0f);
yield break;
}
onProgress?.Invoke(0.0f);
if (onProgress != null)
{
yield return new WaitForProgress(totalTime, onProgress);
}
else
{
yield return new Delay(totalTime);
}
}
}

View File

@ -0,0 +1,70 @@
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine.instructions;
namespace GFramework.Core.coroutine.extensions;
/// <summary>
/// Task与协程之间的扩展方法
/// </summary>
public static class TaskCoroutineExtensions
{
/// <summary>
/// 将Task转换为协程等待指令
/// </summary>
/// <param name="task">要等待的Task</param>
/// <returns>等待Task的协程指令</returns>
public static WaitForTask AsCoroutineInstruction(this Task task)
{
return new WaitForTask(task);
}
/// <summary>
/// 将泛型Task转换为协程等待指令
/// </summary>
/// <typeparam name="T">Task返回值的类型</typeparam>
/// <param name="task">要等待的Task</param>
/// <returns>等待Task的协程指令</returns>
public static WaitForTask<T> AsCoroutineInstruction<T>(this Task<T> task)
{
return new WaitForTask<T>(task);
}
/// <summary>
/// 在调度器中启动一个Task并等待其完成
/// </summary>
/// <param name="scheduler">协程调度器</param>
/// <param name="task">要等待的Task</param>
/// <returns>协程句柄</returns>
public static CoroutineHandle StartTaskAsCoroutine(this CoroutineScheduler scheduler, Task task)
{
return scheduler.Run(CreateTaskCoroutine(task));
}
/// <summary>
/// 在调度器中启动一个泛型Task并等待其完成
/// </summary>
/// <typeparam name="T">Task返回值的类型</typeparam>
/// <param name="scheduler">协程调度器</param>
/// <param name="task">要等待的Task</param>
/// <returns>协程句柄</returns>
public static CoroutineHandle StartTaskAsCoroutine<T>(this CoroutineScheduler scheduler, Task<T> task)
{
return scheduler.Run(CreateTaskCoroutine(task));
}
/// <summary>
/// 创建等待Task的协程
/// </summary>
private static IEnumerator<IYieldInstruction> CreateTaskCoroutine(Task task)
{
yield return task.AsCoroutineInstruction();
}
/// <summary>
/// 创建等待泛型Task的协程
/// </summary>
private static IEnumerator<IYieldInstruction> CreateTaskCoroutine<T>(Task<T> task)
{
yield return task.AsCoroutineInstruction();
}
}

View File

@ -0,0 +1,141 @@
using System.Runtime.CompilerServices;
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 异步操作包装器用于桥接协程系统和async/await异步编程模型
/// </summary>
public class AsyncOperation : IYieldInstruction, INotifyCompletion
{
private readonly TaskCompletionSource<object?> _tcs = new();
private volatile bool _completed;
private volatile Action? _continuation;
/// <summary>
/// 获取异步操作是否已完成
/// </summary>
public bool IsDone => _completed;
/// <summary>
/// 获取异步操作的任务
/// </summary>
public Task Task => _tcs.Task;
/// <summary>
/// 更新方法,用于处理时间更新逻辑
/// </summary>
/// <param name="deltaTime">时间增量</param>
public void Update(double deltaTime)
{
// 由外部调用SetCompleted来更新状态
}
/// <summary>
/// 标记异步操作已完成
/// </summary>
public void SetCompleted()
{
if (_completed) return;
_completed = true;
_tcs.SetResult(null);
var continuation = Interlocked.Exchange(ref _continuation, null);
if (continuation != null)
{
try
{
continuation.Invoke();
}
catch
{
// 忽略延续中的异常
}
}
}
/// <summary>
/// 标记异步操作因异常而失败
/// </summary>
/// <param name="exception">导致失败的异常</param>
public void SetException(Exception exception)
{
if (_completed) return;
_completed = true;
_tcs.SetException(exception);
var continuation = Interlocked.Exchange(ref _continuation, null);
if (continuation != null)
{
try
{
continuation.Invoke();
}
catch
{
// 忽略延续中的异常
}
}
}
/// <summary>
/// 设置延续操作
/// </summary>
/// <param name="continuation">要执行的延续操作</param>
public void OnCompleted(Action continuation)
{
// 尝试添加延续
var current = _continuation;
var newContinuation = current == null ? continuation : current + continuation;
if (Interlocked.CompareExchange(ref _continuation, newContinuation, current) != current)
{
// 如果CAS失败说明可能已经完成直接执行
if (_completed)
{
continuation();
}
else
{
// 重试
OnCompleted(continuation);
}
return;
}
// 双重检查:如果在设置延续后发现已完成,需要执行延续
if (_completed)
{
var cont = Interlocked.Exchange(ref _continuation, null);
if (cont != null)
{
cont();
}
}
}
/// <summary>
/// 获取异步操作结果
/// </summary>
/// <returns>操作结果</returns>
public object? GetResult()
{
return _tcs.Task.GetAwaiter().GetResult();
}
/// <summary>
/// 获取awaiter对象
/// </summary>
public AsyncOperation GetAwaiter()
{
return this;
}
/// <summary>
/// 检查是否已完成
/// </summary>
public bool IsCompleted => _completed;
}

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 延迟等待指令实现IYieldInstruction接口用于协程中的时间延迟

View File

@ -0,0 +1,29 @@

using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 等待所有协程完成的等待指令
/// </summary>
public sealed class WaitForAllCoroutines(
CoroutineScheduler scheduler,
IReadOnlyList<CoroutineHandle> handles)
: IYieldInstruction
{
private readonly CoroutineScheduler _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
private readonly IReadOnlyList<CoroutineHandle> _handles = handles ?? throw new ArgumentNullException(nameof(handles));
public void Update(double deltaTime)
{
// 不需要做任何事
}
public bool IsDone
{
get
{
return _handles.All(handle => !_scheduler.IsCoroutineAlive(handle));
}
}
}

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 等待协程完成的指令类实现IYieldInstruction接口

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 等待指定帧数的等待指令类

View File

@ -0,0 +1,60 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 带进度回调的等待指令
/// </summary>
public class WaitForProgress : IYieldInstruction
{
private readonly double _duration;
private readonly Action<float> _onProgress;
private double _elapsed;
private bool _progressCompleted;
/// <summary>
/// 初始化等待进度指令
/// </summary>
/// <param name="duration">总持续时间(秒)</param>
/// <param name="onProgress">进度回调参数为0-1之间的进度值</param>
public WaitForProgress(double duration, Action<float> onProgress)
{
if (duration <= 0)
throw new ArgumentException("Duration must be positive", nameof(duration));
_duration = duration;
_onProgress = onProgress ?? throw new ArgumentNullException(nameof(onProgress));
_elapsed = 0;
_progressCompleted = false;
}
/// <summary>
/// 更新方法
/// </summary>
/// <param name="deltaTime">时间增量</param>
public void Update(double deltaTime)
{
if (_progressCompleted)
return;
_elapsed += deltaTime;
// 计算进度并回调
if (_elapsed >= _duration)
{
_elapsed = _duration;
_progressCompleted = true;
_onProgress(1.0f);
}
else
{
var progress = (float)(_elapsed / _duration);
_onProgress(progress);
}
}
/// <summary>
/// 获取等待是否已完成
/// </summary>
public bool IsDone => _progressCompleted;
}

View File

@ -0,0 +1,60 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 等待泛型Task完成的等待指令
/// </summary>
/// <typeparam name="T">Task返回值的类型</typeparam>
public sealed class WaitForTask<T> : IYieldInstruction
{
private readonly Task<T> _task;
private volatile bool _done;
/// <summary>
/// 初始化等待泛型Task的指令
/// </summary>
/// <param name="task">要等待完成的Task</param>
public WaitForTask(Task<T> task)
{
_task = task ?? throw new ArgumentNullException(nameof(task));
// 检查Task是否已经完成
if (_task.IsCompleted)
{
_done = true;
}
else
{
// 注册完成回调
_task.ContinueWith(_ =>
{
_done = true;
}, TaskContinuationOptions.ExecuteSynchronously);
}
}
/// <summary>
/// 更新方法,用于处理时间更新逻辑
/// </summary>
/// <param name="deltaTime">时间增量</param>
public void Update(double deltaTime)
{
// Task的完成由ContinueWith回调设置
}
/// <summary>
/// 获取等待是否已完成
/// </summary>
public bool IsDone => _done;
/// <summary>
/// 获取Task的结果值
/// </summary>
public T Result => _task.GetAwaiter().GetResult();
/// <summary>
/// 获取Task的异常如果有
/// </summary>
public Exception? Exception => _task.Exception;
}

View File

@ -0,0 +1,57 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 等待Task完成的等待指令
/// </summary>
/// <summary>
/// 等待Task完成的等待指令
/// </summary>
public sealed class WaitForTask : IYieldInstruction
{
private readonly Task _task;
private volatile bool _done;
/// <summary>
/// 初始化等待Task的指令
/// </summary>
/// <param name="task">要等待完成的Task</param>
public WaitForTask(Task task)
{
_task = task ?? throw new ArgumentNullException(nameof(task));
// 检查Task是否已经完成
if (_task.IsCompleted)
{
_done = true;
}
else
{
// 注册完成回调
_task.ContinueWith(_ =>
{
_done = true;
}, TaskContinuationOptions.ExecuteSynchronously);
}
}
/// <summary>
/// 更新方法,用于处理时间更新逻辑
/// </summary>
/// <param name="deltaTime">时间增量</param>
public void Update(double deltaTime)
{
// Task的完成由ContinueWith回调设置
}
/// <summary>
/// 获取等待是否已完成
/// </summary>
public bool IsDone => _done;
/// <summary>
/// 获取Task的异常如果有
/// </summary>
public Exception? Exception => _task.Exception;
}

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 表示等待一帧的等待指令实现

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 表示一个等待直到指定条件满足的协程指令

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.coroutine;
namespace GFramework.Core.coroutine;
namespace GFramework.Core.coroutine.instructions;
/// <summary>
/// 表示一个等待条件为假时才完成的协程指令

View File

@ -1,6 +1,7 @@
using System.Reflection;
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine;
using GFramework.Core.coroutine.instructions;
using Godot;
namespace GFramework.Godot.coroutine;