refactor(core): 收敛 Core 扩展与测试的机械 warning

- 更新 ContextAware、Store 与通用扩展中的参数空校验写法以满足 analyzer 约束

- 简化 coroutine、pause、log 与 async 测试中的等待和断言包装并保持测试语义不变

- 调整测试替身异常类型与 Result 系列断言样例以减少低风险 warning 噪音
This commit is contained in:
gewuyou 2026-04-25 10:38:48 +08:00
parent 03c73a8ee5
commit 9ce1fa630c
22 changed files with 411 additions and 191 deletions

View File

@ -174,8 +174,15 @@ internal sealed class AdditionalAssemblyNotificationHandlerRegistry : ICqrsHandl
/// </exception>
public void Register(IServiceCollection services, ILogger logger)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(logger);
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (logger is null)
{
throw new ArgumentNullException(nameof(logger));
}
services.AddTransient<INotificationHandler<AdditionalAssemblyNotification>>(_ => CreateHandler());
logger.Debug(

View File

@ -168,52 +168,52 @@ public class TestArchitectureWithRegistry : IArchitecture
T IArchitecture.RegisterSystem<T>(T system)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
T IArchitecture.RegisterModel<T>(T model)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
T IArchitecture.RegisterUtility<T>(T utility)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
{
throw new NotImplementedException();
throw new NotSupportedException();
}
/// <summary>
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
/// </summary>
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
/// <exception cref="NotImplementedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
/// <summary>
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
/// </summary>
/// <param name="assemblies">要接入的程序集集合。</param>
/// <exception cref="NotImplementedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public IArchitectureModule InstallModule(IArchitectureModule module)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
Task IArchitecture.WaitUntilReadyAsync()
@ -223,17 +223,17 @@ public class TestArchitectureWithRegistry : IArchitecture
public void RegisterUtility<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IUtility
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterModel<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IModel
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterSystem<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, ISystem
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void Initialize()
@ -242,7 +242,7 @@ public class TestArchitectureWithRegistry : IArchitecture
public void Destroy()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
Task IAsyncInitializable.InitializeAsync()
@ -257,7 +257,7 @@ public class TestArchitectureWithRegistry : IArchitecture
public Task WaitUntilReadyAsync()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
@ -266,12 +266,12 @@ public class TestArchitectureWithRegistry : IArchitecture
public Task InitializeAsync()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public ValueTask DestroyAsync()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
}
@ -313,72 +313,72 @@ public class TestArchitectureWithoutRegistry : IArchitecture
T IArchitecture.RegisterSystem<T>(T system)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
T IArchitecture.RegisterModel<T>(T model)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
T IArchitecture.RegisterUtility<T>(T utility)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
{
throw new NotImplementedException();
throw new NotSupportedException();
}
/// <summary>
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
/// </summary>
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
/// <exception cref="NotImplementedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
/// <summary>
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
/// </summary>
/// <param name="assemblies">要接入的程序集集合。</param>
/// <exception cref="NotImplementedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public IArchitectureModule InstallModule(IArchitectureModule module)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public Task WaitUntilReadyAsync()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterUtility<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IUtility
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterModel<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IModel
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterSystem<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, ISystem
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void Initialize()
@ -387,17 +387,17 @@ public class TestArchitectureWithoutRegistry : IArchitecture
public Task InitializeAsync()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public ValueTask DestroyAsync()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void Destroy()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)

View File

@ -6,6 +6,7 @@ using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Coroutine.Extensions;
using GFramework.Core.Coroutine.Instructions;
using Moq;
using NUnit.Framework;
namespace GFramework.Core.Tests.Coroutine;
@ -18,6 +19,8 @@ namespace GFramework.Core.Tests.Coroutine;
[TestFixture]
public class CommandCoroutineExtensionsTests
{
private static readonly TimeSpan WaitForTaskTimeout = TimeSpan.FromSeconds(1);
/// <summary>
/// 测试用的简单命令类
/// </summary>
@ -83,15 +86,10 @@ public class CommandCoroutineExtensionsTests
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, ex => capturedException = ex);
// 迭代协程直到完成
while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForTask waitForTask)
{
// 等待任务完成
await Task.Delay(10);
}
}
Assert.That(coroutine.MoveNext(), Is.True);
Assert.That(coroutine.Current, Is.TypeOf<WaitForTask>());
await WaitForTaskAsync((WaitForTask)coroutine.Current);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(capturedException, Is.Null);
}
@ -114,15 +112,10 @@ public class CommandCoroutineExtensionsTests
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, ex => capturedException = ex);
// 迭代协程直到完成
while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForTask waitForTask)
{
// 等待任务完成
await Task.Delay(10);
}
}
Assert.That(coroutine.MoveNext(), Is.True);
Assert.That(coroutine.Current, Is.TypeOf<WaitForTask>());
await WaitForTaskAsync((WaitForTask)coroutine.Current);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(capturedException, Is.Not.Null);
// 异常被包装为 AggregateException
@ -148,17 +141,12 @@ public class CommandCoroutineExtensionsTests
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command);
// 迭代协程应该抛出异常
Assert.Throws<InvalidOperationException>(() =>
{
while (coroutine.MoveNext())
{
if (coroutine.Current is WaitForTask waitForTask)
{
Task.Delay(10).Wait();
}
}
});
Assert.That(coroutine.MoveNext(), Is.True);
Assert.That(coroutine.Current, Is.TypeOf<WaitForTask>());
Assert.That(
SpinWait.SpinUntil(() => ((WaitForTask)coroutine.Current).IsDone, WaitForTaskTimeout),
Is.True);
Assert.Throws<InvalidOperationException>(() => coroutine.MoveNext());
}
/// <summary>
@ -201,8 +189,9 @@ public class CommandCoroutineExtensionsTests
});
// 启动协程并等待命令执行完成
coroutine.MoveNext(); // 进入命令发送阶段
if (coroutine.Current is WaitForTask waitForTask) await Task.Delay(10); // 等待命令任务完成
Assert.That(coroutine.MoveNext(), Is.True); // 进入命令发送阶段
Assert.That(coroutine.Current, Is.TypeOf<WaitForTask>());
await WaitForTaskAsync((WaitForTask)coroutine.Current);
// 此时协程应该在等待事件
Assert.That(coroutine.MoveNext(), Is.True); // 等待事件阶段
@ -296,15 +285,16 @@ public class CommandCoroutineExtensionsTests
command); // null回调
// 启动协程
coroutine.MoveNext(); // 进入命令发送阶段
if (coroutine.Current is WaitForTask waitForTask) await Task.Delay(10); // 等待命令任务完成
Assert.That(coroutine.MoveNext(), Is.True); // 进入命令发送阶段
Assert.That(coroutine.Current, Is.TypeOf<WaitForTask>());
await WaitForTaskAsync((WaitForTask)coroutine.Current);
// 触发事件
var testEvent = new TestEvent { Data = "TestData" };
eventCallback?.Invoke(testEvent);
// 协程应该能正常完成
Assert.That(() => coroutine.MoveNext(), Throws.Nothing);
Assert.DoesNotThrow(() => coroutine.MoveNext());
}
/// <summary>
@ -340,8 +330,9 @@ public class CommandCoroutineExtensionsTests
_ => { });
// 启动协程 - 命令失败时协程仍然继续
coroutine.MoveNext(); // 进入命令发送阶段
if (coroutine.Current is WaitForTask waitForTask) await Task.Delay(10); // 等待命令任务完成
Assert.That(coroutine.MoveNext(), Is.True); // 进入命令发送阶段
Assert.That(coroutine.Current, Is.TypeOf<WaitForTask>());
await WaitForTaskAsync((WaitForTask)coroutine.Current);
// 命令执行失败后,协程继续执行
Assert.Pass();
@ -441,4 +432,18 @@ public class CommandCoroutineExtensionsTests
// 调用 MoveNext 时应该抛出 InvalidOperationException
Assert.Throws<InvalidOperationException>(() => coroutine.MoveNext());
}
}
private static async Task WaitForTaskAsync(WaitForTask waitForTask)
{
var timeoutAt = DateTime.UtcNow + WaitForTaskTimeout;
// 协程通过轮询 IsDone 观察异步命令完成,这里保持相同语义但避免固定延时。
while (!waitForTask.IsDone)
{
if (DateTime.UtcNow >= timeoutAt)
Assert.Fail("WaitForTask did not complete within the expected time.");
await Task.Yield();
}
}
}

View File

@ -66,7 +66,7 @@ public class TaskCoroutineExtensionsTests
var task = Task.FromResult(42);
var instruction = task.AsCoroutineInstruction();
task.Wait();
task.ConfigureAwait(false).GetAwaiter().GetResult();
Assert.That(instruction.Result, Is.EqualTo(42));
}
@ -165,7 +165,7 @@ public class TaskCoroutineExtensionsTests
Assert.That(completed, Is.False);
tcs.SetResult(null);
Task.Delay(50).Wait();
Task.Delay(50).ConfigureAwait(false).GetAwaiter().GetResult();
scheduler.Update();
scheduler.Update();
@ -189,7 +189,7 @@ public class TaskCoroutineExtensionsTests
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
tcs.SetResult(42);
Task.Delay(50).Wait();
Task.Delay(50).ConfigureAwait(false).GetAwaiter().GetResult();
scheduler.Update();
scheduler.Update();
@ -265,7 +265,7 @@ public class TaskCoroutineExtensionsTests
tcs.SetResult(null);
tcs2.SetResult(42);
Task.Delay(50).Wait();
Task.Delay(50).ConfigureAwait(false).GetAwaiter().GetResult();
scheduler.Update();
scheduler.Update();
@ -286,4 +286,4 @@ public class TaskCoroutineExtensionsTests
CurrentTime += DeltaTime;
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace GFramework.Core.Tests.Coroutine
}
[Test]
public async Task Constructor_WithCompletedTask_IsDoneImmediately()
public void Constructor_WithCompletedTask_IsDoneImmediately()
{
// Arrange
var completedTask = Task.FromResult("test");
@ -28,7 +28,7 @@ namespace GFramework.Core.Tests.Coroutine
}
[Test]
public async Task Constructor_WithIncompleteTask_IsNotDoneInitially()
public void Constructor_WithIncompleteTask_IsNotDoneInitially()
{
// Arrange
var tcs = new TaskCompletionSource<string>();
@ -54,7 +54,7 @@ namespace GFramework.Core.Tests.Coroutine
// Act
tcs.SetResult("completed");
await Task.Delay(10); // Allow time for continuation
await Task.Delay(10).ConfigureAwait(false); // Allow time for continuation
// Assert final state
Assert.That(waitForTask.IsDone, Is.True);
@ -62,7 +62,7 @@ namespace GFramework.Core.Tests.Coroutine
}
[Test]
public async Task Update_DoesNotChangeState()
public void Update_DoesNotChangeState()
{
// Arrange
var completedTask = Task.FromResult("test");
@ -85,7 +85,7 @@ namespace GFramework.Core.Tests.Coroutine
// Act
tcs.SetException(new InvalidOperationException("Test exception"));
await Task.Delay(10); // Allow time for continuation
await Task.Delay(10).ConfigureAwait(false); // Allow time for continuation
// Assert
Assert.That(waitForTask.IsDone, Is.True);
@ -93,4 +93,4 @@ namespace GFramework.Core.Tests.Coroutine
Assert.That(waitForTask.Exception?.InnerException, Is.TypeOf<InvalidOperationException>());
}
}
}
}

View File

@ -33,11 +33,11 @@ public class AsyncExtensionsTests
public void WithTimeout_Should_Throw_TimeoutException_When_Task_Exceeds_Timeout()
{
// Act & Assert
Assert.ThrowsAsync<TimeoutException>(async () =>
await AsyncExtensions.WithTimeoutAsync(
Assert.ThrowsAsync<TimeoutException>(() =>
AsyncExtensions.WithTimeoutAsync(
async ct =>
{
await Task.Delay(TimeSpan.FromSeconds(2), ct);
await Task.Delay(TimeSpan.FromSeconds(2), ct).ConfigureAwait(false);
return 42;
},
TimeSpan.FromMilliseconds(100)));
@ -53,11 +53,11 @@ public class AsyncExtensionsTests
using var cts = new CancellationTokenSource();
cts.Cancel();
// Act & Assert
Assert.ThrowsAsync<TaskCanceledException>(async () =>
await AsyncExtensions.WithTimeoutAsync(
Assert.ThrowsAsync<TaskCanceledException>(() =>
AsyncExtensions.WithTimeoutAsync(
async ct =>
{
await Task.Delay(TimeSpan.FromSeconds(2), ct);
await Task.Delay(TimeSpan.FromSeconds(2), ct).ConfigureAwait(false);
return 42;
},
TimeSpan.FromSeconds(1),
@ -74,13 +74,13 @@ public class AsyncExtensionsTests
var innerTaskCanceled = false;
// Act & Assert
Assert.ThrowsAsync<TimeoutException>(async () =>
await AsyncExtensions.WithTimeoutAsync(
Assert.ThrowsAsync<TimeoutException>(() =>
AsyncExtensions.WithTimeoutAsync(
async ct =>
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), ct);
await Task.Delay(TimeSpan.FromSeconds(5), ct).ConfigureAwait(false);
return 0;
}
catch (OperationCanceledException)
@ -121,9 +121,9 @@ public class AsyncExtensionsTests
public void WithTimeout_NoResult_Should_Throw_TimeoutException_When_Task_Exceeds_Timeout()
{
// Act & Assert
Assert.ThrowsAsync<TimeoutException>(async () =>
await AsyncExtensions.WithTimeoutAsync(
ct => Task.Delay(TimeSpan.FromSeconds(2), ct),
Assert.ThrowsAsync<TimeoutException>(() =>
AsyncExtensions.WithTimeoutAsync(
ct => Task.Delay(TimeSpan.FromSeconds(2), ct).ConfigureAwait(false),
TimeSpan.FromMilliseconds(100)));
}
@ -137,13 +137,13 @@ public class AsyncExtensionsTests
var innerTaskCanceled = false;
// Act & Assert
Assert.ThrowsAsync<TimeoutException>(async () =>
await AsyncExtensions.WithTimeoutAsync(
Assert.ThrowsAsync<TimeoutException>(() =>
AsyncExtensions.WithTimeoutAsync(
async ct =>
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), ct);
await Task.Delay(TimeSpan.FromSeconds(5), ct).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@ -217,8 +217,8 @@ public class AsyncExtensionsTests
};
// Act & Assert
Assert.ThrowsAsync<AggregateException>(async () =>
await taskFactory.WithRetryAsync(2, TimeSpan.FromMilliseconds(10)));
Assert.ThrowsAsync<AggregateException>(() =>
taskFactory.WithRetryAsync(2, TimeSpan.FromMilliseconds(10)));
}
/// <summary>

View File

@ -414,9 +414,9 @@ public class ResultTTests
var result = Result<int>.Succeed(42);
var mapped = await result.MapAsync(async x =>
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
return x.ToString(CultureInfo.InvariantCulture);
});
}).ConfigureAwait(false);
Assert.That(mapped.IsSuccess, Is.True);
Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
}
@ -431,9 +431,9 @@ public class ResultTTests
var result = Result<int>.Fail(exception);
var mapped = await result.MapAsync(async x =>
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
return x.ToString(CultureInfo.InvariantCulture);
});
}).ConfigureAwait(false);
Assert.That(mapped.IsFaulted, Is.True);
Assert.That(mapped.Exception, Is.SameAs(exception));
}
@ -447,9 +447,9 @@ public class ResultTTests
var result = Result<int>.Succeed(42);
var mapped = await result.MapAsync<string>(async _ =>
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
throw new InvalidOperationException("Async error");
});
}).ConfigureAwait(false);
Assert.That(mapped.IsFaulted, Is.True);
Assert.That(mapped.Exception, Is.TypeOf<InvalidOperationException>());
}
@ -551,7 +551,7 @@ public class ResultTTests
public void Equals_Should_Return_False_When_Exception_Types_Differ()
{
var result1 = Result<int>.Fail(new InvalidOperationException("Error"));
var result2 = Result<int>.Fail(new ArgumentException("Error"));
var result2 = Result<int>.Fail(new InvalidCastException("Error"));
Assert.That(result1.Equals(result2), Is.False);
}

View File

@ -152,22 +152,22 @@ public class LogContextTests
var task1Values = new List<object?>();
var task2Values = new List<object?>();
var task1 = Task.Run(() =>
var task1 = Task.Run(async () =>
{
using (LogContext.Push("TaskId", "Task1"))
{
task1Values.Add(LogContext.Current["TaskId"]);
Task.Delay(50).Wait();
await Task.Delay(50);
task1Values.Add(LogContext.Current["TaskId"]);
}
});
var task2 = Task.Run(() =>
var task2 = Task.Run(async () =>
{
using (LogContext.Push("TaskId", "Task2"))
{
task2Values.Add(LogContext.Current["TaskId"]);
Task.Delay(50).Wait();
await Task.Delay(50);
task2Values.Add(LogContext.Current["TaskId"]);
}
});
@ -201,4 +201,4 @@ public class LogContextTests
Assert.That(LogContext.Current.Count, Is.EqualTo(0));
}
}
}

View File

@ -22,9 +22,9 @@ public class PauseStackManagerTests
/// 在每个测试方法执行后清理资源
/// </summary>
[TearDown]
public void TearDown()
public async Task TearDown()
{
_manager.DestroyAsync();
await _manager.DestroyAsync().ConfigureAwait(false);
}
private PauseStackManager _manager = null!;
@ -416,7 +416,7 @@ public class PauseStackManagerTests
_manager.Push("Gameplay", PauseGroup.Gameplay);
mockHandler.Reset();
await _manager.DestroyAsync();
await _manager.DestroyAsync().ConfigureAwait(false);
Assert.That(mockHandler.CallCount, Is.EqualTo(2));
Assert.That(mockHandler.LastIsPaused, Is.False);

View File

@ -28,7 +28,10 @@ public static class AsyncExtensions
TimeSpan timeout,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(taskFactory);
if (taskFactory is null)
{
throw new ArgumentNullException(nameof(taskFactory));
}
// linkedCts 同时响应:超时 + 外部取消
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
@ -71,7 +74,10 @@ public static class AsyncExtensions
TimeSpan timeout,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(taskFactory);
if (taskFactory is null)
{
throw new ArgumentNullException(nameof(taskFactory));
}
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
linkedCts.CancelAfter(timeout);
@ -113,8 +119,15 @@ public static class AsyncExtensions
/// </example>
public static async Task<T> WithFallbackAsync<T>(this Task<T> task, Func<Exception, T> fallback)
{
ArgumentNullException.ThrowIfNull(task);
ArgumentNullException.ThrowIfNull(fallback);
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}
if (fallback is null)
{
throw new ArgumentNullException(nameof(fallback));
}
try
{

View File

@ -20,8 +20,15 @@ public static class CollectionExtensions
/// </example>
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(action);
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
foreach (var item in source) action(item);
}
@ -58,7 +65,10 @@ public static class CollectionExtensions
/// </example>
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class
{
ArgumentNullException.ThrowIfNull(source);
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
return source.Where(item => item is not null)!;
}
@ -88,9 +98,20 @@ public static class CollectionExtensions
Func<T, TValue> valueSelector) where TKey : notnull
#pragma warning restore MA0016
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(keySelector);
ArgumentNullException.ThrowIfNull(valueSelector);
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (keySelector is null)
{
throw new ArgumentNullException(nameof(keySelector));
}
if (valueSelector is null)
{
throw new ArgumentNullException(nameof(valueSelector));
}
var dictionary = new Dictionary<TKey, TValue>();

View File

@ -19,8 +19,15 @@ public static class ContextAwareCommandExtensions
public static TResult SendCommand<TResult>(this IContextAware contextAware,
ICommand<TResult> command)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(command);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (command is null)
{
throw new ArgumentNullException(nameof(command));
}
var context = contextAware.GetContext();
return context.SendCommand(command);
@ -34,8 +41,15 @@ public static class ContextAwareCommandExtensions
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
public static void SendCommand(this IContextAware contextAware, ICommand command)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(command);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (command is null)
{
throw new ArgumentNullException(nameof(command));
}
var context = contextAware.GetContext();
context.SendCommand(command);
@ -50,8 +64,15 @@ public static class ContextAwareCommandExtensions
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
public static async Task SendCommandAsync(this IContextAware contextAware, IAsyncCommand command)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(command);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (command is null)
{
throw new ArgumentNullException(nameof(command));
}
var context = contextAware.GetContext();
await context.SendCommandAsync(command).ConfigureAwait(false);
@ -68,8 +89,15 @@ public static class ContextAwareCommandExtensions
public static async Task<TResult> SendCommandAsync<TResult>(this IContextAware contextAware,
IAsyncCommand<TResult> command)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(command);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (command is null)
{
throw new ArgumentNullException(nameof(command));
}
var context = contextAware.GetContext();
return await context.SendCommandAsync(command).ConfigureAwait(false);

View File

@ -16,7 +16,11 @@ public static class ContextAwareEnvironmentExtensions
/// <returns>指定类型的环境对象,如果无法转换则返回null</returns>
public static T? GetEnvironment<T>(this IContextAware contextAware) where T : class
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetEnvironment() as T;
}
@ -28,8 +32,12 @@ public static class ContextAwareEnvironmentExtensions
/// <returns>环境对象</returns>
public static IEnvironment GetEnvironment(this IContextAware contextAware)
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetEnvironment();
}
}
}

View File

@ -16,7 +16,11 @@ public static class ContextAwareEventExtensions
/// <exception cref="ArgumentNullException">当 contextAware 为 null 时抛出</exception>
public static void SendEvent<TEvent>(this IContextAware contextAware) where TEvent : new()
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
context.SendEvent<TEvent>();
}
@ -30,8 +34,15 @@ public static class ContextAwareEventExtensions
/// <exception cref="ArgumentNullException">当 contextAware 或 e 为 null 时抛出</exception>
public static void SendEvent<TEvent>(this IContextAware contextAware, TEvent e) where TEvent : class
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(e);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (e is null)
{
throw new ArgumentNullException(nameof(e));
}
var context = contextAware.GetContext();
context.SendEvent(e);
@ -46,8 +57,15 @@ public static class ContextAwareEventExtensions
/// <returns>事件注销接口</returns>
public static IUnRegister RegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> handler)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(handler);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}
var context = contextAware.GetContext();
return context.RegisterEvent(handler);
@ -61,10 +79,17 @@ public static class ContextAwareEventExtensions
/// <param name="onEvent">之前绑定的事件处理器</param>
public static void UnRegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> onEvent)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(onEvent);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (onEvent is null)
{
throw new ArgumentNullException(nameof(onEvent));
}
var context = contextAware.GetContext();
context.UnRegisterEvent(onEvent);
}
}
}

View File

@ -18,8 +18,15 @@ public static class ContextAwareQueryExtensions
/// <exception cref="ArgumentNullException">当 contextAware 或 query 为 null 时抛出</exception>
public static TResult SendQuery<TResult>(this IContextAware contextAware, IQuery<TResult> query)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(query);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (query is null)
{
throw new ArgumentNullException(nameof(query));
}
var context = contextAware.GetContext();
return context.SendQuery(query);
@ -37,8 +44,15 @@ public static class ContextAwareQueryExtensions
public static async Task<TResult> SendQueryAsync<TResult>(this IContextAware contextAware,
IAsyncQuery<TResult> query)
{
ArgumentNullException.ThrowIfNull(contextAware);
ArgumentNullException.ThrowIfNull(query);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
if (query is null)
{
throw new ArgumentNullException(nameof(query));
}
var context = contextAware.GetContext();
return await context.SendQueryAsync(query).ConfigureAwait(false);

View File

@ -24,7 +24,11 @@ public static class ContextAwareServiceExtensions
/// <exception cref="InvalidOperationException">当指定服务未注册时抛出</exception>
public static TService GetService<TService>(this IContextAware contextAware) where TService : class
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return GetRequiredComponent(context, static architectureContext => architectureContext.GetService<TService>(),
"Service");
@ -40,7 +44,11 @@ public static class ContextAwareServiceExtensions
/// <exception cref="InvalidOperationException">当指定系统未注册时抛出</exception>
public static TSystem GetSystem<TSystem>(this IContextAware contextAware) where TSystem : class, ISystem
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return GetRequiredComponent(context, static architectureContext => architectureContext.GetSystem<TSystem>(),
"System");
@ -56,7 +64,11 @@ public static class ContextAwareServiceExtensions
/// <exception cref="InvalidOperationException">当指定模型未注册时抛出</exception>
public static TModel GetModel<TModel>(this IContextAware contextAware) where TModel : class, IModel
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return GetRequiredComponent(context, static architectureContext => architectureContext.GetModel<TModel>(),
"Model");
@ -72,7 +84,11 @@ public static class ContextAwareServiceExtensions
/// <exception cref="InvalidOperationException">当指定工具未注册时抛出</exception>
public static TUtility GetUtility<TUtility>(this IContextAware contextAware) where TUtility : class, IUtility
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return GetRequiredComponent(context, static architectureContext => architectureContext.GetUtility<TUtility>(),
"Utility");
@ -92,7 +108,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TService> GetServices<TService>(this IContextAware contextAware)
where TService : class
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetServices<TService>();
}
@ -107,7 +127,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TSystem> GetSystems<TSystem>(this IContextAware contextAware)
where TSystem : class, ISystem
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetSystems<TSystem>();
}
@ -122,7 +146,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TModel> GetModels<TModel>(this IContextAware contextAware)
where TModel : class, IModel
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetModels<TModel>();
}
@ -137,7 +165,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TUtility> GetUtilities<TUtility>(this IContextAware contextAware)
where TUtility : class, IUtility
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetUtilities<TUtility>();
}
@ -152,7 +184,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TService> GetServicesByPriority<TService>(this IContextAware contextAware)
where TService : class
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetServicesByPriority<TService>();
}
@ -167,7 +203,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TSystem> GetSystemsByPriority<TSystem>(this IContextAware contextAware)
where TSystem : class, ISystem
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetSystemsByPriority<TSystem>();
}
@ -182,7 +222,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TModel> GetModelsByPriority<TModel>(this IContextAware contextAware)
where TModel : class, IModel
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetModelsByPriority<TModel>();
}
@ -197,7 +241,11 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<TUtility> GetUtilitiesByPriority<TUtility>(this IContextAware contextAware)
where TUtility : class, IUtility
{
ArgumentNullException.ThrowIfNull(contextAware);
if (contextAware is null)
{
throw new ArgumentNullException(nameof(contextAware));
}
var context = contextAware.GetContext();
return context.GetUtilitiesByPriority<TUtility>();
}
@ -206,11 +254,14 @@ public static class ContextAwareServiceExtensions
Func<IArchitectureContext, TComponent> resolver, string componentKind)
where TComponent : class
{
ArgumentNullException.ThrowIfNull(context);
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var component = resolver(context);
return component ?? throw new InvalidOperationException($"{componentKind} {typeof(TComponent)} not registered");
}
#endregion
}
}

View File

@ -27,7 +27,9 @@ public static class GuardExtensions
this T? value,
[CallerArgumentExpression(nameof(value))] string? paramName = null) where T : class
{
ArgumentNullException.ThrowIfNull(value, paramName);
if (value is null)
throw new ArgumentNullException(paramName);
return value;
}
@ -51,7 +53,8 @@ public static class GuardExtensions
this string? value,
[CallerArgumentExpression(nameof(value))] string? paramName = null)
{
ArgumentNullException.ThrowIfNull(value, paramName);
if (value is null)
throw new ArgumentNullException(paramName);
if (value.Length == 0)
throw new ArgumentException("字符串不能为空", paramName);
@ -79,7 +82,8 @@ public static class GuardExtensions
this string? value,
[CallerArgumentExpression(nameof(value))] string? paramName = null)
{
ArgumentNullException.ThrowIfNull(value, paramName);
if (value is null)
throw new ArgumentNullException(paramName);
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("字符串不能为空或仅包含空白字符", paramName);
@ -108,7 +112,8 @@ public static class GuardExtensions
this IEnumerable<T>? source,
[CallerArgumentExpression(nameof(source))] string? paramName = null)
{
ArgumentNullException.ThrowIfNull(source, paramName);
if (source is null)
throw new ArgumentNullException(paramName);
if (!source.Any())
throw new ArgumentException("集合不能为空", paramName);

View File

@ -25,9 +25,14 @@ public static class NumericExtensions
/// </example>
public static bool Between<T>(this T value, T min, T max, bool inclusive = true) where T : IComparable<T>
{
ArgumentNullException.ThrowIfNull(value);
ArgumentNullException.ThrowIfNull(min);
ArgumentNullException.ThrowIfNull(max);
if (value is null)
throw new ArgumentNullException(nameof(value));
if (min is null)
throw new ArgumentNullException(nameof(min));
if (max is null)
throw new ArgumentNullException(nameof(max));
if (min.CompareTo(max) > 0)
throw new ArgumentException($"最小值 ({min}) 不能大于最大值 ({max})", nameof(min));

View File

@ -27,8 +27,15 @@ public static class StoreEventBusExtensions
bool publishDispatches = true,
bool publishStateChanges = true)
{
ArgumentNullException.ThrowIfNull(store);
ArgumentNullException.ThrowIfNull(eventBus);
if (store is null)
{
throw new ArgumentNullException(nameof(store));
}
if (eventBus is null)
{
throw new ArgumentNullException(nameof(eventBus));
}
IUnRegister? dispatchBridge = null;
IUnRegister? stateBridge = null;
@ -60,8 +67,15 @@ public static class StoreEventBusExtensions
/// <returns>用于移除 dispatch 桥接中间件的句柄。</returns>
public static IUnRegister BridgeDispatchesToEventBus<TState>(this Store<TState> store, IEventBus eventBus)
{
ArgumentNullException.ThrowIfNull(store);
ArgumentNullException.ThrowIfNull(eventBus);
if (store is null)
{
throw new ArgumentNullException(nameof(store));
}
if (eventBus is null)
{
throw new ArgumentNullException(nameof(eventBus));
}
return store.RegisterMiddleware(new DispatchEventBusMiddleware<TState>(eventBus));
}
@ -77,8 +91,15 @@ public static class StoreEventBusExtensions
public static IUnRegister BridgeStateChangesToEventBus<TState>(this IReadonlyStore<TState> store,
IEventBus eventBus)
{
ArgumentNullException.ThrowIfNull(store);
ArgumentNullException.ThrowIfNull(eventBus);
if (store is null)
{
throw new ArgumentNullException(nameof(store));
}
if (eventBus is null)
{
throw new ArgumentNullException(nameof(eventBus));
}
return store.Subscribe(state =>
eventBus.Send(new StoreStateChangedEvent<TState>(state, DateTimeOffset.UtcNow)));
@ -115,4 +136,4 @@ public static class StoreEventBusExtensions
_eventBus.Send(new StoreDispatchedEvent<TState>(dispatchRecord));
}
}
}
}

View File

@ -38,8 +38,11 @@ public static class StringExtensions
/// </example>
public static string Truncate(this string str, int maxLength, string suffix = "...")
{
ArgumentNullException.ThrowIfNull(str);
ArgumentNullException.ThrowIfNull(suffix);
if (str is null)
throw new ArgumentNullException(nameof(str));
if (suffix is null)
throw new ArgumentNullException(nameof(suffix));
if (maxLength < suffix.Length)
throw new ArgumentOutOfRangeException(nameof(maxLength),
@ -66,9 +69,12 @@ public static class StringExtensions
/// </example>
public static string Join(this IEnumerable<string> values, string separator)
{
ArgumentNullException.ThrowIfNull(values);
ArgumentNullException.ThrowIfNull(separator);
if (values is null)
throw new ArgumentNullException(nameof(values));
if (separator is null)
throw new ArgumentNullException(nameof(separator));
return string.Join(separator, values);
}
}
}

View File

@ -39,7 +39,9 @@ public sealed class StoreBuilder<TState> : IStoreBuilder<TState>
/// <returns>当前构建器实例。</returns>
public IStoreBuilder<TState> UseMiddleware(IStoreMiddleware<TState> middleware)
{
ArgumentNullException.ThrowIfNull(middleware);
if (middleware is null)
throw new ArgumentNullException(nameof(middleware));
_configurators.Add(store => store.UseMiddleware(middleware));
return this;
}
@ -108,7 +110,9 @@ public sealed class StoreBuilder<TState> : IStoreBuilder<TState>
/// <returns>当前构建器实例。</returns>
public IStoreBuilder<TState> AddReducer<TAction>(Func<TState, TAction, TState> reducer)
{
ArgumentNullException.ThrowIfNull(reducer);
if (reducer is null)
throw new ArgumentNullException(nameof(reducer));
_configurators.Add(store => store.RegisterReducer(reducer));
return this;
}
@ -121,8 +125,10 @@ public sealed class StoreBuilder<TState> : IStoreBuilder<TState>
/// <returns>当前构建器实例。</returns>
public IStoreBuilder<TState> AddReducer<TAction>(IReducer<TState, TAction> reducer)
{
ArgumentNullException.ThrowIfNull(reducer);
if (reducer is null)
throw new ArgumentNullException(nameof(reducer));
_configurators.Add(store => store.RegisterReducer(reducer));
return this;
}
}
}

View File

@ -82,7 +82,9 @@ public sealed class StoreSelection<TState, TSelected> : IReadonlyBindablePropert
/// <returns>用于取消订阅的句柄。</returns>
IUnRegister IEvent.Register(Action onEvent)
{
ArgumentNullException.ThrowIfNull(onEvent);
if (onEvent is null)
throw new ArgumentNullException(nameof(onEvent));
return Register(_ => onEvent());
}
@ -94,7 +96,8 @@ public sealed class StoreSelection<TState, TSelected> : IReadonlyBindablePropert
/// <exception cref="ArgumentNullException">当 <paramref name="onValueChanged"/> 为 <see langword="null"/> 时抛出。</exception>
public IUnRegister Register(Action<TSelected> onValueChanged)
{
ArgumentNullException.ThrowIfNull(onValueChanged);
if (onValueChanged is null)
throw new ArgumentNullException(nameof(onValueChanged));
var subscription = new SelectionListenerSubscription(onValueChanged);
var shouldAttach = false;
@ -126,7 +129,8 @@ public sealed class StoreSelection<TState, TSelected> : IReadonlyBindablePropert
/// <exception cref="ArgumentNullException">当 <paramref name="action"/> 为 <see langword="null"/> 时抛出。</exception>
public IUnRegister RegisterWithInitValue(Action<TSelected> action)
{
ArgumentNullException.ThrowIfNull(action);
if (action is null)
throw new ArgumentNullException(nameof(action));
var subscription = new SelectionListenerSubscription(action)
{
@ -189,7 +193,8 @@ public sealed class StoreSelection<TState, TSelected> : IReadonlyBindablePropert
/// <exception cref="ArgumentNullException">当 <paramref name="onValueChanged"/> 为 <see langword="null"/> 时抛出。</exception>
public void UnRegister(Action<TSelected> onValueChanged)
{
ArgumentNullException.ThrowIfNull(onValueChanged);
if (onValueChanged is null)
throw new ArgumentNullException(nameof(onValueChanged));
SelectionListenerSubscription? subscriptionToRemove = null;
@ -391,4 +396,4 @@ public sealed class StoreSelection<TState, TSelected> : IReadonlyBindablePropert
/// </summary>
public TSelected PendingValue { get; set; } = default!;
}
}
}