diff --git a/GFramework.Core.Tests/Logging/ConfigurableLoggerFactoryTests.cs b/GFramework.Core.Tests/Logging/ConfigurableLoggerFactoryTests.cs index 074c9a02..b20dd184 100644 --- a/GFramework.Core.Tests/Logging/ConfigurableLoggerFactoryTests.cs +++ b/GFramework.Core.Tests/Logging/ConfigurableLoggerFactoryTests.cs @@ -36,6 +36,24 @@ public sealed class ConfigurableLoggerFactoryTests }); } + /// + /// 验证当配置输入把 appenders 集合中的某个元素反序列化为 时,工厂会抛出可诊断异常。 + /// + [Test] + public void CreateFactory_ShouldThrowInvalidOperationException_WhenAppenderEntryIsNull() + { + var config = LoggingConfigurationLoader.LoadFromJsonString( + """ + { + "appenders": [ null ] + } + """); + + var exception = Assert.Throws(() => LoggingConfigurationLoader.CreateFactory(config)); + + Assert.That(exception!.Message, Is.EqualTo("Appender configuration cannot be null.")); + } + /// /// 验证在未命中命名空间覆盖时,调用方传入的默认最小级别会作为最终 logger 级别的下限参与计算。 /// diff --git a/GFramework.Core/Logging/ConfigurableLoggerFactory.cs b/GFramework.Core/Logging/ConfigurableLoggerFactory.cs index 5e4b497e..4d1fd064 100644 --- a/GFramework.Core/Logging/ConfigurableLoggerFactory.cs +++ b/GFramework.Core/Logging/ConfigurableLoggerFactory.cs @@ -16,6 +16,8 @@ internal sealed class ConfigurableLoggerFactory : ILoggerFactory, IDisposable /// 初始化一个基于日志配置创建输出管线的工厂实例。 /// /// 日志配置。 + /// + /// 配置中的某个 Appender 项为 public ConfigurableLoggerFactory(LoggingConfiguration config) { _config = config ?? throw new ArgumentNullException(nameof(config)); @@ -23,7 +25,13 @@ internal sealed class ConfigurableLoggerFactory : ILoggerFactory, IDisposable // 反序列化输入可能显式把集合写成 null,这里统一归一化为可安全枚举的空集合。 _config.Appenders ??= []; _config.LoggerLevels ??= new Dictionary(StringComparer.Ordinal); - _appenders = _config.Appenders.Select(LoggingConfigurationLoader.CreateAppender).ToArray(); + + // 外部配置可能把集合项反序列化为 null,这里先给出可诊断异常,避免后续工厂链路出现不清晰的空引用失败。 + _appenders = _config.Appenders + .Select(static appenderConfig => appenderConfig ?? + throw new InvalidOperationException("Appender configuration cannot be null.")) + .Select(LoggingConfigurationLoader.CreateAppender) + .ToArray(); } /// diff --git a/GFramework.Godot/Architectures/AbstractArchitecture.cs b/GFramework.Godot/Architectures/AbstractArchitecture.cs index 668c84ce..1beb8609 100644 --- a/GFramework.Godot/Architectures/AbstractArchitecture.cs +++ b/GFramework.Godot/Architectures/AbstractArchitecture.cs @@ -2,8 +2,6 @@ using GFramework.Core.Abstractions.Environment; using GFramework.Core.Architectures; using GFramework.Core.Constants; -using GFramework.Godot.Extensions; -using Godot; namespace GFramework.Godot.Architectures; @@ -165,7 +163,7 @@ public abstract class AbstractArchitecture( { try { - await DestroyAsync(); + await DestroyAsync().ConfigureAwait(false); } catch (Exception ex) { diff --git a/docs/zh-CN/godot/setting.md b/docs/zh-CN/godot/setting.md index f1c75ae5..8187f303 100644 --- a/docs/zh-CN/godot/setting.md +++ b/docs/zh-CN/godot/setting.md @@ -378,7 +378,7 @@ public class AudioManager : Node ); } - private void SetMasterVolume(float linearVolume) + private async void SetMasterVolume(float linearVolume) { var settings = new AudioSettings { MasterVolume = linearVolume }; var audioSettings = new GodotAudioSettings(settings, new AudioBusMap()); @@ -419,7 +419,7 @@ public class CustomAudioManager : Node ); } - private void SetMasterVolume(float linearVolume) + private async void SetMasterVolume(float linearVolume) { var audioSettingsData = new AudioSettings { MasterVolume = linearVolume }; var audioSettings = new GodotAudioSettings(audioSettingsData, _customBusMap);