mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
fix(pr-review): 收敛PR建议并修复构建验证
- 修复 PR #288 中经本地复核后仍成立的 Core、Game 与测试建议 - 更新 WSL 标准 dotnet build 验证路径并确认 Release 构建可通过 - 补充 analyzer-warning-reduction 跟踪文档记录本轮结论与恢复点
This commit is contained in:
parent
70c42b579f
commit
a7a3eca40d
@ -366,7 +366,7 @@ public class TestArchitectureContextV3 : IArchitectureContext
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令响应任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendCommandAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
@ -380,7 +380,7 @@ public class TestArchitectureContextV3 : IArchitectureContext
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <returns>命令响应。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
@ -393,7 +393,7 @@ public class TestArchitectureContextV3 : IArchitectureContext
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>查询结果任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendQueryAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
|
||||
CancellationToken cancellationToken = default)
|
||||
@ -407,7 +407,7 @@ public class TestArchitectureContextV3 : IArchitectureContext
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <returns>查询结果。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
||||
@ -401,7 +401,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="request">要发送的请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>请求响应任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@ -414,7 +414,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="request">要发送的请求。</param>
|
||||
/// <returns>请求响应。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
@ -427,7 +427,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令响应任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendCommandAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
@ -441,7 +441,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <returns>命令响应。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
@ -454,7 +454,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>查询结果任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendQueryAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
|
||||
CancellationToken cancellationToken = default)
|
||||
@ -468,7 +468,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <returns>查询结果。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
@ -481,7 +481,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="notification">要发布的通知。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>通知发布任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||
CancellationToken cancellationToken = default) where TNotification : INotification
|
||||
{
|
||||
@ -495,7 +495,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="request">流式请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>异步响应流。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
|
||||
IStreamRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@ -510,7 +510,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令发送任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
|
||||
where TCommand : IRequest<Unit>
|
||||
{
|
||||
@ -524,7 +524,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// <param name="command">要发送的请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>请求响应任务。</returns>
|
||||
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
@ -287,7 +287,7 @@ public class TestArchitectureContextWithRegistry : TestArchitectureContext
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
public override TUtility GetUtility<TUtility>()
|
||||
public override TUtility? GetUtility<TUtility>() where TUtility : class
|
||||
{
|
||||
if (typeof(TUtility) == typeof(TestRegistry))
|
||||
{
|
||||
|
||||
@ -66,8 +66,6 @@ public class TaskCoroutineExtensionsTests
|
||||
var task = Task.FromResult(42);
|
||||
var instruction = task.AsCoroutineInstruction();
|
||||
|
||||
task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
Assert.That(instruction.Result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
|
||||
@ -291,8 +291,7 @@ public class WaitForTaskTests
|
||||
var wait = new WaitForTask<int>(task);
|
||||
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.Result, Is.EqualTo(expectedValue));
|
||||
|
||||
@ -68,7 +68,7 @@ public class RollingFileAppenderTests
|
||||
}
|
||||
|
||||
// 检查是否生成了多个文件
|
||||
var files = Directory.GetFiles(_testDir, "*.log").OrderBy(f => f, System.StringComparer.Ordinal).ToArray();
|
||||
var files = Directory.GetFiles(_testDir, "*.log");
|
||||
Assert.That(files.Length, Is.GreaterThan(1));
|
||||
}
|
||||
|
||||
|
||||
@ -28,10 +28,7 @@ public static class AsyncExtensions
|
||||
TimeSpan timeout,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (taskFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(taskFactory));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(taskFactory);
|
||||
|
||||
// linkedCts 同时响应:超时 + 外部取消
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
@ -74,10 +71,7 @@ public static class AsyncExtensions
|
||||
TimeSpan timeout,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (taskFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(taskFactory));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(taskFactory);
|
||||
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
linkedCts.CancelAfter(timeout);
|
||||
@ -119,15 +113,8 @@ public static class AsyncExtensions
|
||||
/// </example>
|
||||
public static async Task<T> WithFallbackAsync<T>(this Task<T> task, Func<Exception, T> fallback)
|
||||
{
|
||||
if (task is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(task));
|
||||
}
|
||||
|
||||
if (fallback is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fallback));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(task);
|
||||
ArgumentNullException.ThrowIfNull(fallback);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@ -19,15 +19,8 @@ public static class ContextAwareCommandExtensions
|
||||
public static TResult SendCommand<TResult>(this IContextAware contextAware,
|
||||
ICommand<TResult> command)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (command is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(command));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
return context.SendCommand(command);
|
||||
@ -41,15 +34,8 @@ public static class ContextAwareCommandExtensions
|
||||
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
|
||||
public static void SendCommand(this IContextAware contextAware, ICommand command)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (command is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(command));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
context.SendCommand(command);
|
||||
@ -64,15 +50,8 @@ public static class ContextAwareCommandExtensions
|
||||
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
|
||||
public static async Task SendCommandAsync(this IContextAware contextAware, IAsyncCommand command)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (command is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(command));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
await context.SendCommandAsync(command).ConfigureAwait(false);
|
||||
@ -89,15 +68,8 @@ public static class ContextAwareCommandExtensions
|
||||
public static async Task<TResult> SendCommandAsync<TResult>(this IContextAware contextAware,
|
||||
IAsyncCommand<TResult> command)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (command is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(command));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
return await context.SendCommandAsync(command).ConfigureAwait(false);
|
||||
|
||||
@ -16,10 +16,7 @@ public static class ContextAwareEventExtensions
|
||||
/// <exception cref="ArgumentNullException">当 contextAware 为 null 时抛出</exception>
|
||||
public static void SendEvent<TEvent>(this IContextAware contextAware) where TEvent : new()
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
context.SendEvent<TEvent>();
|
||||
@ -34,15 +31,8 @@ public static class ContextAwareEventExtensions
|
||||
/// <exception cref="ArgumentNullException">当 contextAware 或 e 为 null 时抛出</exception>
|
||||
public static void SendEvent<TEvent>(this IContextAware contextAware, TEvent e) where TEvent : class
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (e is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(e));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(e);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
context.SendEvent(e);
|
||||
@ -57,15 +47,8 @@ public static class ContextAwareEventExtensions
|
||||
/// <returns>事件注销接口</returns>
|
||||
public static IUnRegister RegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> handler)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (handler is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
return context.RegisterEvent(handler);
|
||||
@ -79,15 +62,8 @@ public static class ContextAwareEventExtensions
|
||||
/// <param name="onEvent">之前绑定的事件处理器</param>
|
||||
public static void UnRegisterEvent<TEvent>(this IContextAware contextAware, Action<TEvent> onEvent)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (onEvent is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(onEvent));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(onEvent);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
context.UnRegisterEvent(onEvent);
|
||||
|
||||
@ -18,15 +18,8 @@ public static class ContextAwareQueryExtensions
|
||||
/// <exception cref="ArgumentNullException">当 contextAware 或 query 为 null 时抛出</exception>
|
||||
public static TResult SendQuery<TResult>(this IContextAware contextAware, IQuery<TResult> query)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (query is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
return context.SendQuery(query);
|
||||
@ -44,15 +37,8 @@ public static class ContextAwareQueryExtensions
|
||||
public static async Task<TResult> SendQueryAsync<TResult>(this IContextAware contextAware,
|
||||
IAsyncQuery<TResult> query)
|
||||
{
|
||||
if (contextAware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(contextAware));
|
||||
}
|
||||
|
||||
if (query is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(contextAware);
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
|
||||
var context = contextAware.GetContext();
|
||||
return await context.SendQueryAsync(query).ConfigureAwait(false);
|
||||
|
||||
@ -25,20 +25,9 @@ public static class NumericExtensions
|
||||
/// </example>
|
||||
public static bool Between<T>(this T value, T min, T max, bool inclusive = true) where T : IComparable<T>
|
||||
{
|
||||
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));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
ArgumentNullException.ThrowIfNull(min);
|
||||
ArgumentNullException.ThrowIfNull(max);
|
||||
|
||||
if (min.CompareTo(max) > 0)
|
||||
{
|
||||
|
||||
@ -38,15 +38,8 @@ public static class StringExtensions
|
||||
/// </example>
|
||||
public static string Truncate(this string str, int maxLength, string suffix = "...")
|
||||
{
|
||||
if (str is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(str));
|
||||
}
|
||||
|
||||
if (suffix is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(suffix));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
ArgumentNullException.ThrowIfNull(suffix);
|
||||
|
||||
if (maxLength < suffix.Length)
|
||||
{
|
||||
@ -77,15 +70,8 @@ public static class StringExtensions
|
||||
/// </example>
|
||||
public static string Join(this IEnumerable<string> values, string separator)
|
||||
{
|
||||
if (values is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
if (separator is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(separator));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
ArgumentNullException.ThrowIfNull(separator);
|
||||
|
||||
return string.Join(separator, values);
|
||||
}
|
||||
|
||||
@ -39,10 +39,7 @@ public sealed class StoreBuilder<TState> : IStoreBuilder<TState>
|
||||
/// <returns>当前构建器实例。</returns>
|
||||
public IStoreBuilder<TState> UseMiddleware(IStoreMiddleware<TState> middleware)
|
||||
{
|
||||
if (middleware is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(middleware));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(middleware);
|
||||
|
||||
_configurators.Add(store => store.UseMiddleware(middleware));
|
||||
return this;
|
||||
@ -112,10 +109,7 @@ public sealed class StoreBuilder<TState> : IStoreBuilder<TState>
|
||||
/// <returns>当前构建器实例。</returns>
|
||||
public IStoreBuilder<TState> AddReducer<TAction>(Func<TState, TAction, TState> reducer)
|
||||
{
|
||||
if (reducer is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(reducer));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(reducer);
|
||||
|
||||
_configurators.Add(store => store.RegisterReducer(reducer));
|
||||
return this;
|
||||
@ -129,10 +123,7 @@ public sealed class StoreBuilder<TState> : IStoreBuilder<TState>
|
||||
/// <returns>当前构建器实例。</returns>
|
||||
public IStoreBuilder<TState> AddReducer<TAction>(IReducer<TState, TAction> reducer)
|
||||
{
|
||||
if (reducer is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(reducer));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(reducer);
|
||||
|
||||
_configurators.Add(store => store.RegisterReducer(reducer));
|
||||
return this;
|
||||
|
||||
@ -24,18 +24,11 @@ public sealed class StoreSelection<TState, TSelected> : IReadonlyBindablePropert
|
||||
/// </summary>
|
||||
private readonly List<SelectionListenerSubscription> _listeners = [];
|
||||
|
||||
/// <summary>
|
||||
/// 保护监听器集合和底层 Store 订阅句柄的同步锁。
|
||||
/// </summary>
|
||||
#if NET9_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// net9.0 及以上目标使用专用 Lock,以满足分析器对专用同步原语的建议。
|
||||
/// </summary>
|
||||
// net9.0 及以上目标使用专用 Lock,以满足分析器对专用同步原语的建议。
|
||||
private readonly System.Threading.Lock _lock = new();
|
||||
#else
|
||||
/// <summary>
|
||||
/// net8.0 目标仍回退到 object 锁,以保持多目标编译兼容性。
|
||||
/// </summary>
|
||||
// net8.0 目标仍回退到 object 锁,以保持多目标编译兼容性。
|
||||
private readonly object _lock = new();
|
||||
#endif
|
||||
|
||||
|
||||
@ -193,7 +193,7 @@ public abstract class SceneRouterBase
|
||||
|
||||
await _pipeline.ExecuteAroundAsync(
|
||||
@event,
|
||||
() => ExecutePushCoreAsync(@event, sceneKey, param)).ConfigureAwait(false);
|
||||
() => ExecutePushCoreAsync(@event, sceneKey, param)).ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -271,7 +271,7 @@ public abstract class SceneRouterBase
|
||||
|
||||
await _pipeline.ExecuteAroundAsync(
|
||||
@event,
|
||||
() => ExecutePopCoreAsync(@event)).ConfigureAwait(false);
|
||||
() => ExecutePopCoreAsync(@event)).ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -339,7 +339,7 @@ public abstract class SceneRouterBase
|
||||
|
||||
await _pipeline.ExecuteAroundAsync(
|
||||
@event,
|
||||
() => ExecuteClearCoreAsync(@event)).ConfigureAwait(false);
|
||||
() => ExecuteClearCoreAsync(@event)).ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -357,7 +357,7 @@ public abstract class SceneRouterBase
|
||||
{
|
||||
while (Stack.Count > 0)
|
||||
{
|
||||
await PopInternalAsync();
|
||||
await PopInternalAsync().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,6 +365,8 @@ public abstract class SceneRouterBase
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
// Scene 生命周期回调和 pipeline handlers 可能依赖引擎线程,因此这些核心切换顺序统一显式保留上下文。
|
||||
|
||||
/// <summary>
|
||||
/// 执行 Replace 的核心切换顺序。
|
||||
/// </summary>
|
||||
@ -377,11 +379,10 @@ public abstract class SceneRouterBase
|
||||
string sceneKey,
|
||||
ISceneEnterParam? param)
|
||||
{
|
||||
// 场景生命周期回调可能依赖引擎线程,因此这里保留默认 await 行为。
|
||||
await BeforeChangeAsync(@event);
|
||||
await ClearInternalAsync();
|
||||
await PushInternalAsync(sceneKey, param);
|
||||
await AfterChangeAsync(@event);
|
||||
await BeforeChangeAsync(@event).ConfigureAwait(true);
|
||||
await ClearInternalAsync().ConfigureAwait(true);
|
||||
await PushInternalAsync(sceneKey, param).ConfigureAwait(true);
|
||||
await AfterChangeAsync(@event).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -396,9 +397,9 @@ public abstract class SceneRouterBase
|
||||
string sceneKey,
|
||||
ISceneEnterParam? param)
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await PushInternalAsync(sceneKey, param);
|
||||
await AfterChangeAsync(@event);
|
||||
await BeforeChangeAsync(@event).ConfigureAwait(true);
|
||||
await PushInternalAsync(sceneKey, param).ConfigureAwait(true);
|
||||
await AfterChangeAsync(@event).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -408,9 +409,9 @@ public abstract class SceneRouterBase
|
||||
/// <returns>异步任务。</returns>
|
||||
private async Task ExecutePopCoreAsync(SceneTransitionEvent @event)
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await PopInternalAsync();
|
||||
await AfterChangeAsync(@event);
|
||||
await BeforeChangeAsync(@event).ConfigureAwait(true);
|
||||
await PopInternalAsync().ConfigureAwait(true);
|
||||
await AfterChangeAsync(@event).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -420,9 +421,9 @@ public abstract class SceneRouterBase
|
||||
/// <returns>异步任务。</returns>
|
||||
private async Task ExecuteClearCoreAsync(SceneTransitionEvent @event)
|
||||
{
|
||||
await BeforeChangeAsync(@event);
|
||||
await ClearInternalAsync();
|
||||
await AfterChangeAsync(@event);
|
||||
await BeforeChangeAsync(@event).ConfigureAwait(true);
|
||||
await ClearInternalAsync().ConfigureAwait(true);
|
||||
await AfterChangeAsync(@event).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -454,7 +455,7 @@ public abstract class SceneRouterBase
|
||||
private async Task BeforeChangeAsync(SceneTransitionEvent @event)
|
||||
{
|
||||
Log.Debug("BeforeChange phases started: {0}", @event.TransitionType);
|
||||
await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.BeforeChange).ConfigureAwait(false);
|
||||
await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.BeforeChange).ConfigureAwait(true);
|
||||
Log.Debug("BeforeChange phases completed: {0}", @event.TransitionType);
|
||||
}
|
||||
|
||||
@ -465,7 +466,7 @@ public abstract class SceneRouterBase
|
||||
private async Task AfterChangeAsync(SceneTransitionEvent @event)
|
||||
{
|
||||
Log.Debug("AfterChange phases started: {0}", @event.TransitionType);
|
||||
await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.AfterChange).ConfigureAwait(false);
|
||||
await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.AfterChange).ConfigureAwait(true);
|
||||
Log.Debug("AfterChange phases completed: {0}", @event.TransitionType);
|
||||
}
|
||||
|
||||
|
||||
@ -251,7 +251,7 @@ public sealed class FileStorage : IFileStorage, IDisposable
|
||||
useAsync: true);
|
||||
await using var configuredFileStream = fs.ConfigureAwait(false);
|
||||
|
||||
using var sr = new StreamReader(fs, Encoding.UTF8);
|
||||
using var sr = new StreamReader(fs, Encoding.UTF8, true, -1, leaveOpen: true);
|
||||
var content = await sr.ReadToEndAsync().ConfigureAwait(false);
|
||||
return _serializer.Deserialize<T>(content);
|
||||
}
|
||||
@ -373,7 +373,7 @@ public sealed class FileStorage : IFileStorage, IDisposable
|
||||
useAsync: true);
|
||||
await using var configuredFileStream = fs.ConfigureAwait(false);
|
||||
|
||||
var sw = new StreamWriter(fs, Encoding.UTF8);
|
||||
var sw = new StreamWriter(fs, Encoding.UTF8, leaveOpen: true);
|
||||
await using var configuredStreamWriter = sw.ConfigureAwait(false);
|
||||
|
||||
await sw.WriteAsync(content).ConfigureAwait(false);
|
||||
|
||||
@ -100,9 +100,9 @@ public sealed class ScopedStorage(IStorage inner, string prefix) : IScopedStorag
|
||||
/// </summary>
|
||||
/// <param name="key">要删除的键</param>
|
||||
/// <returns>异步操作任务</returns>
|
||||
public async Task DeleteAsync(string key)
|
||||
public Task DeleteAsync(string key)
|
||||
{
|
||||
await inner.DeleteAsync(Key(key)).ConfigureAwait(false);
|
||||
return inner.DeleteAsync(Key(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -6,15 +6,16 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-063`
|
||||
- 当前阶段:`Phase 63`
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-064`
|
||||
- 当前阶段:`Phase 64`
|
||||
- 当前焦点:
|
||||
- `2026-04-25` 当前 turn 先执行 `$gframework-pr-review`,核对 PR #288 的 latest-head AI review 与本地真实状态
|
||||
- 已修复 `GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs` 的 `ConfiguredTaskAwaitable -> Task` 编译错误,并顺手收敛一批同类测试/样式残留
|
||||
- `2026-04-25` 当前 turn 先执行 `$gframework-pr-review`,复核 PR #288 的 latest-head unresolved 线程与折叠评论
|
||||
- 已收敛一批经本地复核后仍成立的 review 建议,包括 `ThrowIfNull` 回退、测试桩 XML 注释修正、`FileStorage` 资源所有权、`SceneRouterBase` 线程亲和语义与若干测试噪音
|
||||
- 已确认用户在 WSL 下直接执行的标准 `dotnet build -c Release` 路径可用;前一轮失败主要来自主线程附加的 workaround 参数而非仓库本身不可构建
|
||||
- 基线 `origin/main` 仍为 `9964962`(`2026-04-24T23:05:53+08:00`)
|
||||
- 当前累计 branch diff 相对 `origin/main` 为 `75` 个文件、`2098` 行,已触达本轮 `75 files` 阈值
|
||||
- `RP-061` 之后已接受 2 个批次提交:`03c73a8`、`9ce1fa6`
|
||||
- 当前默认恢复入口不再继续扩写集;若要继续 analyzer reduction,优先先处理 WSL 下 NuGet fallback package folder 指向失效 Windows 路径的构建环境阻塞
|
||||
- 当前默认恢复入口不再继续扩写集;若要继续 analyzer reduction,优先重新抓取 PR #288 的 unresolved 线程并按最新 head 再做一轮收口
|
||||
|
||||
## 当前活跃事实
|
||||
|
||||
@ -30,6 +31,10 @@
|
||||
- `PauseStackManagerTests.cs`
|
||||
- 本 turn 结合 PR #288 latest-head review 额外收敛了以下仍然成立的问题:
|
||||
- `AsyncExtensionsTests.cs`:修复 `WithTimeoutAsync` 无返回值测试中错误返回 `ConfiguredTaskAwaitable` 导致的 `CS0029` / `CS1662`
|
||||
- `ContextAwareCommandExtensions.cs`
|
||||
- `ContextAwareQueryExtensions.cs`
|
||||
- `ContextAwareEventExtensions.cs`
|
||||
- `AsyncExtensions.cs`
|
||||
- `AsyncKeyLockManagerTests.cs`:去掉两处不会产生额外价值的 `Assert.DoesNotThrowAsync(() => Task.WhenAll(...))` 包装,并把取消断言改为直接消费 `ValueTask.AsTask()`
|
||||
- `AsyncArchitectureTests.cs`
|
||||
- `ArchitectureLifecycleBehaviorTests.cs`
|
||||
@ -39,11 +44,19 @@
|
||||
- `StringExtensions.cs`
|
||||
- `StoreBuilder.cs`
|
||||
- `StoreSelection.cs`
|
||||
- `ArchitectureServicesTests.cs`
|
||||
- `GameContextTests.cs`
|
||||
- `RollingFileAppenderTests.cs`
|
||||
- `TaskCoroutineExtensionsTests.cs`
|
||||
- `WaitForTaskTests.cs`
|
||||
- `ScopedStorage.cs`
|
||||
- `FileStorage.cs`
|
||||
- `SceneRouterBase.cs`
|
||||
- 当前 PR review 观察:
|
||||
- PR:`#288`
|
||||
- latest reviewed commit:`be336b2088b7c283a140add76d5cff30618ad16d`
|
||||
- `coderabbitai[bot]` 仍有 `7` 个 open threads,`greptile-apps[bot]` 仍有 `2` 个 open threads
|
||||
- 本 turn 已优先修复 latest-head 中明确指向 `AsyncExtensionsTests.cs:126` 的 critical 编译错误
|
||||
- latest reviewed commit:`70c42b579f70c90ab5461a02e611c0fbd8d8a6f2`
|
||||
- 抓取时 `coderabbitai[bot]` 有 `6` 个 open threads,`greptile-apps[bot]` 有 `2` 个 open threads
|
||||
- `Actionable comments posted: 7` 与 `outside diff + nitpick = 19` 并不等于必须全收;本 turn 仅接受经本地复核后仍成立且不与仓库约束冲突的建议
|
||||
- 本轮 `Core` runtime 低风险机械型清理已落地到:
|
||||
- `AsyncExtensions.cs`
|
||||
- `CollectionExtensions.cs`
|
||||
@ -64,15 +77,14 @@
|
||||
|
||||
## 当前风险
|
||||
|
||||
- 当前环境下 `GFramework.Core` / `GFramework.Core.Tests` 的 Release build 会命中 `MSB4018`。
|
||||
- 直接原因:`ResolvePackageAssets` 仍从历史 restore 生成的 `obj/*.csproj.nuget.g.props` 读取失效的 Windows fallback package folder `D:\Tool\Development Tools\Microsoft Visual Studio\Shared\NuGetPackages`。
|
||||
- 缓解措施:下次恢复时先重建 WSL 原生 restore 元数据,或显式清理并重做 `obj` 下的 NuGet restore 工件,再重新建立可信 build 基线。
|
||||
- `dotnet clean GFramework.sln -c Release` 与 `dotnet clean GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release` 仍无法稳定提供新的 clean 基线。
|
||||
- 缓解措施:后续若继续整仓 warning reduction,需要单独定位 clean 失败原因,或明确继续沿用 direct build 观测值作为临时真值。
|
||||
- 当前 worktree 仍存在未跟踪的 `.codex` 目录。
|
||||
- 缓解措施:提交当前批次时只暂存 analyzer-warning-reduction 相关源码与 `ai-plan` 文件,避免把工作目录辅助文件混入提交。
|
||||
- 将分支继续推过 `75 files` 会明显降低本轮 reviewability。
|
||||
- 缓解措施:当前恢复点默认停止;如需继续,建议在新 turn 明确新的文件阈值或先 rebase / refresh baseline。
|
||||
- `GFramework.Core`、`GFramework.Game`、`GFramework.Core.Tests` 当前都仍存在模块级历史 warning 基线。
|
||||
- 缓解措施:本 turn 已确保本次 touched files 不再引入新的编译错误,并消化了当前 PR review 中仍成立的高信号问题;若要继续 warning reduction,应开新批次按模块系统化收敛。
|
||||
|
||||
## 活跃文档
|
||||
|
||||
@ -100,6 +112,14 @@
|
||||
- 结果:失败;`MSB4018`,原因同上
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- 结果:成功;定位到 PR `#288`,提取 latest-head unresolved AI review threads、MegaLinter 与 Docstring Coverage 信号
|
||||
- `dotnet restore GFramework.sln -p:RestoreFallbackFolders="" -v minimal`
|
||||
- 结果:成功;已刷新 WSL 原生 restore 元数据,清除先前的 stale fallback package folder 阻塞
|
||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||
- 结果:成功;`28 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 结果:成功;`329 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 结果:成功;`137 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet restore GFramework.Core.Tests/GFramework.Core.Tests.csproj -p:TestTargetFrameworks=net8.0 -p:RestoreFallbackFolders="" -v minimal`
|
||||
- 结果:失败;`NU1201`,`GFramework.Tests.Common` 仅支持 `net10.0`,因此不能用 `net8.0` 旁路验证 `Core.Tests`
|
||||
- `git diff --name-only origin/main...HEAD | wc -l`
|
||||
@ -109,6 +129,6 @@
|
||||
|
||||
## 下一步建议
|
||||
|
||||
1. 当前 turn 已先修复 latest-head PR review 中最紧急的编译错误;后续若继续 PR #288 收尾,优先重新抓取 unresolved threads,确认剩余 8 个 open threads 哪些仍成立。
|
||||
2. 若后续要继续 `Core` / `Core.Tests` warning reduction,先修复 WSL 下 stale NuGet restore metadata 导致的 `MSB4018`,再重新建立可信 build 基线。
|
||||
1. 当前 turn 已按标准 WSL `dotnet build` 路径完成 `Core` / `Game` / `Core.Tests` Release build 验证;后续若继续 PR #288 收尾,优先重新抓取 unresolved threads,确认哪些线程已可直接 resolve。
|
||||
2. 若后续要继续 `Core` / `Core.Tests` / `Game` warning reduction,应以当前标准 build 输出为新真值,而不是继续沿用上一轮带 workaround 参数的失败命令。
|
||||
3. 若要开启下一轮批处理,优先选择新的 stop-condition(例如新的 file 阈值、warning 目标或限定到单模块)后再继续。
|
||||
|
||||
@ -1,5 +1,38 @@
|
||||
# Analyzer Warning Reduction 追踪
|
||||
|
||||
## 2026-04-25 — RP-064
|
||||
|
||||
### 阶段:按标准 WSL build 路径复核 PR #288 建议并完成本轮收口
|
||||
|
||||
- 触发背景:
|
||||
- 用户指出“在 WSL 里直接执行 `dotnet build` 可以成功”,要求主线程按普通路径重新验证,而不是继续使用带 `MSBuildEnableWorkloadResolver=false`、`--no-restore`、手工 `TargetFramework` 的 workaround 命令
|
||||
- 当前任务仍属于 PR #288 review follow-up,因此本轮重点改为“区分哪些 AI 建议值得采纳”以及“用真实 WSL build 结果验证”
|
||||
- 主线程实施:
|
||||
- 重新抓取 PR #288 review,确认 latest-head open threads 为 `CodeRabbit 6 + Greptile 2`
|
||||
- 复核 `outside diff + nitpick` 的 19 条建议,只采纳本地仍成立的建议;拒绝把“评论总数”机械等同于“必须全改”
|
||||
- 完成以下高信号修复:
|
||||
- `ContextAware*` / `AsyncExtensions` / `NumericExtensions` / `StringExtensions` / `StoreBuilder`:回退为 `ArgumentNullException.ThrowIfNull(...)`
|
||||
- `ArchitectureServicesTests` / `GameContextTests`:同步 XML `<exception>` 到 `NotSupportedException`
|
||||
- `RegistryInitializationHookBaseTests`:修复 override 可空签名实现,避免再次引入编译错误
|
||||
- `RollingFileAppenderTests` / `TaskCoroutineExtensionsTests` / `WaitForTaskTests` / `ScopedStorage`:移除无收益噪音代码
|
||||
- `FileStorage`:通过 `leaveOpen: true` 修正 `FileStream` 的双重释放语义
|
||||
- `SceneRouterBase`:统一显式 `ConfigureAwait(true)` 并补齐引擎线程亲和说明
|
||||
- `StoreSelection`:保留 `net9.0+` 的 `System.Threading.Lock`,同时修正条件编译旁的注释写法,避免 `CS1587`
|
||||
- 验证里程碑:
|
||||
- `dotnet restore GFramework.sln -p:RestoreFallbackFolders="" -v minimal`
|
||||
- 结果:成功;证明先前 `MSB4018` 来自 stale restore 元数据,而不是当前 WSL 默认 build 路径本身不可用
|
||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||
- 结果:成功;`28 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 结果:成功;`329 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 结果:成功;`137 Warning(s)`、`0 Error(s)`
|
||||
- 当前结论:
|
||||
- 用户关于“WSL 里直接 `dotnet build` 可行”的判断正确
|
||||
- 前一轮失败的核心原因不是仓库不可构建,而是主线程附加的 workaround 参数改变了 MSBuild 行为
|
||||
- 本轮已完成 PR #288 中一组仍成立的建议修复,并重新拿到标准 WSL 路径下的 Release build 验证
|
||||
- 剩余 review 线程需要在新 head 上重新抓取后再决定是否逐条 resolve
|
||||
|
||||
## 2026-04-25 — RP-063
|
||||
|
||||
### 阶段:先收口 PR #288 latest-head 编译错误,再暂停在环境阻塞点并准备提交
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user