diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/AttributeGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/AttributeGenerator.cs index 48c1846c..da43e6d7 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/AttributeGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/AttributeGenerator.cs @@ -140,6 +140,8 @@ internal sealed class AttributeGenerator : IIncrementalGenerator public InjectionAttribute(InjectAs injectAs, Type interfaceType) { } + + public object Key { get; set; } } """; context.AddSource("Snap.Hutao.Core.DependencyInjection.Annotation.Attributes.g.cs", coreDependencyInjectionAnnotations); diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/DependencyPropertyGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/DependencyPropertyGenerator.cs index 66744fc5..8e1d0447 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/DependencyPropertyGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Automation/DependencyPropertyGenerator.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Snap.Hutao.SourceGeneration.Primitive; using System.Collections.Generic; @@ -68,7 +69,7 @@ internal sealed class DependencyPropertyGenerator : IIncrementalGenerator string propertyName = (string)arguments[0].Value!; string propertyType = arguments[1].Value!.ToString(); - string defaultValue = GetLiteralString(arguments.ElementAtOrDefault(2)) ?? "default"; + string defaultValue = arguments.ElementAtOrDefault(2).ToCSharpString() ?? "default"; string propertyChangedCallback = arguments.ElementAtOrDefault(3) is { IsNull: false } arg3 ? $", {arg3.Value}" : string.Empty; string code; @@ -125,25 +126,4 @@ internal sealed class DependencyPropertyGenerator : IIncrementalGenerator production.AddSource($"{normalizedClassName}.{propertyName}.g.cs", code); } } - - private static string? GetLiteralString(TypedConstant typedConstant) - { - if (typedConstant.IsNull) - { - return default; - } - - if (typedConstant.Value is bool boolValue) - { - return boolValue ? "true" : "false"; - } - - string result = typedConstant.Value!.ToString(); - if (string.IsNullOrEmpty(result)) - { - return default; - } - - return result; - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs index b264850b..7aa07a88 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs @@ -92,17 +92,29 @@ internal sealed class InjectionGenerator : IIncrementalGenerator ImmutableArray arguments = injectionInfo.ConstructorArguments; string injectAsName = arguments[0].ToCSharpString(); - switch (injectAsName) + + bool hasKey = injectionInfo.TryGetNamedArgumentValue("Key", out TypedConstant key); + + switch (injectAsName, hasKey) { - case InjectAsSingletonName: + case (InjectAsSingletonName, false): lineBuilder.Append(" services.AddSingleton<"); break; - case InjectAsTransientName: + case (InjectAsSingletonName, true): + lineBuilder.Append(" services.AddKeyedSingleton<"); + break; + case (InjectAsTransientName, false): lineBuilder.Append(" services.AddTransient<"); break; - case InjectAsScopedName: + case (InjectAsTransientName, true): + lineBuilder.Append(" services.AddKeyedTransient<"); + break; + case (InjectAsScopedName, false): lineBuilder.Append(" services.AddScoped<"); break; + case (InjectAsScopedName, true): + lineBuilder.Append(" services.AddKeyedScoped<"); + break; default: production.ReportDiagnostic(Diagnostic.Create(invalidInjectionDescriptor, context.Context.Node.GetLocation(), injectAsName)); break; @@ -113,7 +125,14 @@ internal sealed class InjectionGenerator : IIncrementalGenerator lineBuilder.Append($"{arguments[1].Value}, "); } - lineBuilder.Append($"{context.Symbol.ToDisplayString()}>();"); + if (hasKey) + { + lineBuilder.Append($"{context.Symbol.ToDisplayString()}>({key.ToCSharpString()});"); + } + else + { + lineBuilder.Append($"{context.Symbol.ToDisplayString()}>();"); + } lines.Add(lineBuilder.ToString()); } diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/AttributeDataExtension.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/AttributeDataExtension.cs index 91e8b326..98496f2f 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/AttributeDataExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/AttributeDataExtension.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis; using System; +using System.Collections.Generic; using System.Linq; namespace Snap.Hutao.SourceGeneration.Primitive; @@ -13,4 +14,19 @@ internal static class AttributeDataExtension { return data.NamedArguments.Any(a => a.Key == key && predicate((TValue)a.Value.Value!)); } + + public static bool TryGetNamedArgumentValue(this AttributeData data, string key, out TypedConstant value) + { + foreach (KeyValuePair pair in data.NamedArguments) + { + if (pair.Key == key) + { + value = pair.Value; + return true; + } + } + + value = default; + return false; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj index 62f03b54..5b81c9af 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj @@ -8,6 +8,7 @@ x64 true Debug;Release + true diff --git a/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs index d9c8e0fc..2ec63fa2 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs @@ -7,6 +7,10 @@ namespace Snap.Hutao.Test; [TestClass] public class JsonSerializeTest { + private TestContext? testContext; + + public TestContext? TestContext { get => testContext; set => testContext = value; } + private readonly JsonSerializerOptions AlowStringNumberOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString, @@ -53,6 +57,27 @@ public class JsonSerializeTest Assert.AreEqual(sample[111], "12"); } + [TestMethod] + public void ByteArraySerializeAsBase64() + { + byte[] array = +#if NET8_0_OR_GREATER + [1, 2, 3, 4, 5]; +#else + { 1, 2, 3, 4, 5 }; +#endif + ByteArraySample sample = new() + { + Array = array, + }; + + string result = JsonSerializer.Serialize(sample); + TestContext!.WriteLine($"ByteArray Serialize Result: {result}"); + Assert.AreEqual(result, """ + {"Array":"AQIDBAU="} + """); + } + private sealed class Sample { public int A { get => B; set => B = value; } @@ -64,4 +89,9 @@ public class JsonSerializeTest [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] public int A { get; set; } } + + private sealed class ByteArraySample + { + public byte[]? Array { get; set; } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs index 5caeb2e6..dc2bc7ba 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs @@ -9,8 +9,12 @@ public sealed class ForEachRuntimeBehaviorTest [TestMethod] public void ListOfStringCanEnumerateAsReadOnlySpanOfChar() { - List strings = ["a", "b", "c"]; - + List strings = +#if NET8_0_OR_GREATER + ["a", "b", "c"]; +#else + new() { "a", "b", "c" }; +#endif int count = 0; foreach (ReadOnlySpan chars in strings) { diff --git a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/RangeRuntimeBehaviorTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/RangeRuntimeBehaviorTest.cs index bb43cef2..abb82eb1 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/RangeRuntimeBehaviorTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/RangeRuntimeBehaviorTest.cs @@ -8,8 +8,13 @@ public sealed class RangeRuntimeBehaviorTest [TestMethod] public void RangeTrimLastOne() { +#if NET8_0_OR_GREATER int[] array = [1, 2, 3, 4]; int[] test = [1, 2, 3]; +#else + int[] array = { 1, 2, 3, 4 }; + int[] test = { 1, 2, 3 }; +#endif int[] result = array[..^1]; Assert.AreEqual(3, result.Length); Assert.IsTrue(MemoryExtensions.SequenceEqual(test, result)); diff --git a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/UnsafeRuntimeBehaviorTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/UnsafeRuntimeBehaviorTest.cs index e69e6f04..0d516772 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/UnsafeRuntimeBehaviorTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/UnsafeRuntimeBehaviorTest.cs @@ -6,7 +6,12 @@ public sealed class UnsafeRuntimeBehaviorTest [TestMethod] public unsafe void UInt32AllSetIs() { - byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF,]; + byte[] bytes = +#if NET8_0_OR_GREATER + [0xFF, 0xFF, 0xFF, 0xFF]; +#else + { 0xFF, 0xFF, 0xFF, 0xFF, }; +#endif fixed (byte* pBytes = bytes) { diff --git a/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj b/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj index 8a171c43..e440cdd6 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj +++ b/src/Snap.Hutao/Snap.Hutao.Test/Snap.Hutao.Test.csproj @@ -1,7 +1,7 @@ - + - net8.0 + net7.0;net8.0 disable enable false diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs index 5a709cb0..e9484f79 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs @@ -35,7 +35,6 @@ internal static class DependencyInjection // Discrete services .AddSingleton() - .BuildServiceProvider(true); Ioc.Default.ConfigureServices(serviceProvider); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Configuration/GameChannelOptionsService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Configuration/GameChannelOptionsService.cs index 20db8658..10d37817 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Configuration/GameChannelOptionsService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Configuration/GameChannelOptionsService.cs @@ -48,7 +48,7 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer { using (FileStream readStream = File.OpenRead(configPath)) { - elements = IniSerializer.Deserialize(readStream).ToList(); + elements = [.. IniSerializer.Deserialize(readStream)]; } } catch (FileNotFoundException ex) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs index 9734b2d6..aa265ef3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs @@ -165,11 +165,11 @@ internal sealed class LaunchOptions : DbStoreOptions set => SetOption(ref isMonitorEnabled, SettingEntry.LaunchIsMonitorEnabled, value); } - public List AspectRatios { get; } = new() - { + public List AspectRatios { get; } = + [ new(2560, 1440), new(1920, 1080), - }; + ]; public AspectRatio? SelectedAspectRatio { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs index 3e39bfd9..3377193f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs @@ -178,11 +178,11 @@ internal sealed partial class PackageConverter private async ValueTask> GetVersionItemsAsync(Stream stream) { - Dictionary results = new(); + Dictionary results = []; using (StreamReader reader = new(stream)) { Regex dataFolderRegex = DataFolderRegex(); - while (await reader.ReadLineAsync().ConfigureAwait(false) is { } row && !string.IsNullOrEmpty(row)) + while (await reader.ReadLineAsync().ConfigureAwait(false) is { Length: > 0 } row) { VersionItem? item = JsonSerializer.Deserialize(row, options); ArgumentNullException.ThrowIfNull(item); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs index 13780b2d..c1dbf7d4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Process/GameProcessService.cs @@ -30,8 +30,8 @@ internal sealed partial class GameProcessService : IGameProcessService return false; } - return System.Diagnostics.Process.GetProcessesByName(YuanShenProcessName).Any() - || System.Diagnostics.Process.GetProcessesByName(GenshinImpactProcessName).Any(); + return System.Diagnostics.Process.GetProcessesByName(YuanShenProcessName).Length > 0 + || System.Diagnostics.Process.GetProcessesByName(GenshinImpactProcessName).Length > 0; } public async ValueTask LaunchAsync(IProgress progress) @@ -126,7 +126,9 @@ internal sealed partial class GameProcessService : IGameProcessService private ValueTask UnlockFpsAsync(System.Diagnostics.Process game, IProgress progress, CancellationToken token = default) { +#pragma warning disable CA1859 IGameFpsUnlocker unlocker = serviceProvider.CreateInstance(game); +#pragma warning restore CA1859 UnlockTimingOptions options = new(100, 20000, 3000); Progress lockerProgress = new(unlockStatus => progress.Report(LaunchStatus.FromUnlockStatus(unlockStatus))); return unlocker.UnlockAsync(options, lockerProgress, token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Scheme/KnownLaunchSchemes.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Scheme/KnownLaunchSchemes.cs index c1373917..1240c893 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Scheme/KnownLaunchSchemes.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Scheme/KnownLaunchSchemes.cs @@ -28,8 +28,9 @@ internal static class KnownLaunchSchemes /// 已知的启动方案 public static List Get() { - return new List() - { + return + [ + // 官服 ServerChineseChannelDefaultSubChannelDefaultCompat, ServerChineseChannelOfficialSubChannelDefault, @@ -47,6 +48,6 @@ internal static class KnownLaunchSchemes ServerGlobalChannelOfficialSubChannelOfficial, ServerGlobalChannelOfficialSubChannelEpic, ServerGlobalChannelOfficialSubChannelGoogle, - }; + ]; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs index 05132861..978ebf55 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs @@ -135,8 +135,8 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker { // E8 ?? ?? ?? ?? 85 C0 7E 07 E8 ?? ?? ?? ?? EB 05 int second = 0; - ReadOnlySpan secondPart = stackalloc byte[] { 0x85, 0xC0, 0x7E, 0x07, 0xE8, }; - ReadOnlySpan thirdPart = stackalloc byte[] { 0xEB, 0x05, }; + ReadOnlySpan secondPart = [0x85, 0xC0, 0x7E, 0x07, 0xE8,]; + ReadOnlySpan thirdPart = [0xEB, 0x05,]; while (second >= 0 && second < memory.Length) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs index dddc3381..28a52a4e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoAsAService.cs @@ -39,7 +39,7 @@ internal sealed partial class HutaoAsAService : IHutaoAsAService } else { - return new(); + return []; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs index 0d8f10dc..8729e996 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs @@ -168,6 +168,23 @@ internal sealed class NavigationService : INavigationService, INavigationInitial }); } + private static IEnumerable EnumerateMenuItems(IList items) + { + foreach (NavigationViewItem item in items.OfType()) + { + yield return item; + + // Suppress recursion method call if possible + if (item.MenuItems.Count > 0) + { + foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems)) + { + yield return subItem; + } + } + } + } + private bool SyncSelectedNavigationViewItemWith(Type? pageType) { if (NavigationView is null || pageType is null) @@ -191,23 +208,6 @@ internal sealed class NavigationService : INavigationService, INavigationInitial return true; } - private IEnumerable EnumerateMenuItems(IList items) - { - foreach (NavigationViewItem item in items.OfType()) - { - yield return item; - - // Suppress recursion method call if possible - if (item.MenuItems.Count > 0) - { - foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems)) - { - yield return subItem; - } - } - } - } - private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) { selected = NavigationView?.SelectedItem as NavigationViewItem; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs index 1f69dae3..f5ebe8f9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs @@ -31,7 +31,7 @@ internal sealed class InfoBarService : IInfoBarService /// public ObservableCollection Collection { - get => collection ??= new(); + get => collection ??= []; } /// @@ -121,7 +121,7 @@ internal sealed class InfoBarService : IInfoBarService Title = title, Message = message, IsOpen = true, - Transitions = new() { new AddDeleteThemeTransition() }, + Transitions = [new AddDeleteThemeTransition()], }; infoBar.Closed += infobarClosedEventHandler; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index 2d2dfb7c..83816b05 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -24,7 +24,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer if (!await InitializeUserAsync(user, token).ConfigureAwait(false)) { user.UserInfo = new() { Nickname = SH.ModelBindingUserInitializationFailed }; - user.UserGameRoles = new(); + user.UserGameRoles = []; } return user; @@ -176,7 +176,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer if (userGameRolesResponse.IsOk()) { user.UserGameRoles = userGameRolesResponse.Data.List; - return user.UserGameRoles.Any(); + return user.UserGameRoles.Count > 0; } else { diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 1139cf54..7a5f42e4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -271,22 +271,22 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs index 8d9ba7db..aa69f253 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs @@ -252,7 +252,6 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav } } - [SuppressMessage("", "SA1010")] [Command("ExportAsUIAFToFileCommand")] private async Task ExportAsUIAFToFileAsync() { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs index 4340042c..59c8e45a 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs @@ -23,8 +23,7 @@ internal sealed class Team : List public Team(ItemRate team, Dictionary idAvatarMap) : base(4) { - // TODO use Collection Literials - foreach (StringSegment item in new StringTokenizer(team.Item, new char[] { ',' })) + foreach (StringSegment item in new StringTokenizer(team.Item, [','])) { uint id = uint.Parse(item.AsSpan(), CultureInfo.InvariantCulture); Add(new(idAvatarMap[id])); diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModelSlim.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModelSlim.cs index 07fc74b3..a3e785e9 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModelSlim.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModelSlim.cs @@ -38,7 +38,7 @@ internal sealed partial class DailyNoteViewModelSlim : Abstraction.ViewModelSlim // 此处使用浅拷贝的列表以避免当导航到实时便笺页面后 // 由于主页尚未卸载,添加或删除便笺可能会崩溃的问题 - List entryList = entries.ToList(); + List entryList = [.. entries]; await taskContext.SwitchToMainThreadAsync(); DailyNoteEntries = entryList; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs index 983bed24..52cefc4f 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs @@ -231,7 +231,6 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel } } - [SuppressMessage("", "SA1010")] [Command("ExportToUIGFJsonCommand")] private async Task ExportToUIGFJsonAsync() { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs index 911a14ef..3d88e5d2 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/StaticResource.cs @@ -109,7 +109,7 @@ internal static class StaticResource public static HashSet GetUnfulfilledCategorySet() { - HashSet result = new(); + HashSet result = []; ApplicationDataCompositeValue map = LocalSetting.Get(ContractMap, DefaultResourceVersionMap); foreach ((string key, object value) in LatestResourceVersionMap) { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs index b57ee8d9..e1431268 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs @@ -230,15 +230,15 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel } [Command("OpenCacheFolderCommand")] - private Task OpenCacheFolderAsync() + private async Task OpenCacheFolderAsync() { - return Launcher.LaunchFolderPathAsync(runtimeOptions.LocalCache).AsTask(); + await Launcher.LaunchFolderPathAsync(runtimeOptions.LocalCache); } [Command("OpenDataFolderCommand")] - private Task OpenDataFolderAsync() + private async Task OpenDataFolderAsync() { - return Launcher.LaunchFolderPathAsync(runtimeOptions.DataFolder).AsTask(); + await Launcher.LaunchFolderPathAsync(runtimeOptions.DataFolder); } [Command("DeleteUsersCommand")] diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs index 5924b5a4..c5326834 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs @@ -16,11 +16,7 @@ internal sealed class LevelView : IMappingFrom diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs index 33e67f79..abbe8ef6 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs @@ -24,10 +24,8 @@ internal static class AvatarFilter private static bool DoFilter(string input, Avatar avatar) { - List matches = new(); - - // TODO: use Collection Literals - foreach (StringSegment segment in new StringTokenizer(input, new char[] { ' ' })) + List matches = []; + foreach (StringSegment segment in new StringTokenizer(input, [' '])) { string value = segment.ToString(); diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs index ce20ac4e..b07ffc46 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs @@ -22,7 +22,6 @@ internal static class WeaponFilter return (object o) => o is Weapon weapon && DoFilter(input, weapon); } - [SuppressMessage("", "SA1010")] private static bool DoFilter(string input, Weapon weapon) { List matches = []; diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs index d38ca327..9804b221 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs @@ -92,15 +92,15 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel Dictionary idMaterialMap = await metadataService.GetIdToMaterialMapAsync().ConfigureAwait(false); List avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false); - List sorted = avatars + IOrderedEnumerable sorted = avatars .OrderByDescending(avatar => avatar.BeginTime) - .ThenByDescending(avatar => avatar.Sort) - .ToList(); + .ThenByDescending(avatar => avatar.Sort); + List list = [.. sorted]; - await CombineComplexDataAsync(sorted, idMaterialMap).ConfigureAwait(false); + await CombineComplexDataAsync(list, idMaterialMap).ConfigureAwait(false); await taskContext.SwitchToMainThreadAsync(); - Avatars = new AdvancedCollectionView(sorted, true); + Avatars = new AdvancedCollectionView(list, true); Selected = Avatars.Cast().FirstOrDefault(); return true; } @@ -192,13 +192,13 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel Dictionary avatarGrowCurve = avatar.GrowCurves.ToDictionary(g => g.Type, g => g.Value); FightProperty promoteProperty = avatarPromoteMap[0].AddProperties.Last().Type; - List propertyCurveValues = new() - { + List propertyCurveValues = + [ new(FightProperty.FIGHT_PROP_BASE_HP, avatarGrowCurve, avatar.BaseValue), new(FightProperty.FIGHT_PROP_BASE_ATTACK, avatarGrowCurve, avatar.BaseValue), new(FightProperty.FIGHT_PROP_BASE_DEFENSE, avatarGrowCurve, avatar.BaseValue), new(promoteProperty, avatarGrowCurve, avatar.BaseValue), - }; + ]; ArgumentNullException.ThrowIfNull(levelAvatarCurveMap); BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs index 2630976f..332001f9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSBridge.cs @@ -461,7 +461,7 @@ internal class MiHoYoJSBridge } [SuppressMessage("", "CA2254")] - private IJsResult? LogUnhandledMessage([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string message, params object?[] param) + private IJsResult? LogUnhandledMessage(string message, params object?[] param) { logger.LogWarning(message, param); return default; @@ -490,7 +490,7 @@ internal class MiHoYoJSBridge "pushPage" => await PushPageAsync(param).ConfigureAwait(false), "share" => Share(param), "showLoading" => null, - _ => LogUnhandledMessage("Unhandled Message Type: {method}", param.Method), + _ => LogUnhandledMessage("Unhandled Message Type: {Method}", param.Method), }; } catch (ObjectDisposedException) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs index bcf01984..64309812 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs @@ -18,7 +18,7 @@ internal sealed partial class Cookie /// 构造一个空白的Cookie /// public Cookie() - : this(new()) + : this([]) { } @@ -44,7 +44,7 @@ internal sealed partial class Cookie /// 新的Cookie对象 public static Cookie Parse(string cookieString) { - SortedDictionary cookieMap = new(); + SortedDictionary cookieMap = []; cookieString = cookieString.Replace(" ", string.Empty, StringComparison.Ordinal); string[] values = cookieString.Split(';', StringSplitOptions.RemoveEmptyEntries); foreach (string[] parts in values.Select(c => c.Split('=', 2))) @@ -59,7 +59,7 @@ internal sealed partial class Cookie public static Cookie FromCoreWebView2Cookies(IReadOnlyList webView2Cookies) { - SortedDictionary cookieMap = new(); + SortedDictionary cookieMap = []; foreach (CoreWebView2Cookie cookie in webView2Cookies) { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs index 1610cd37..ffabae36 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs @@ -8,7 +8,6 @@ namespace Snap.Hutao.Web.Hoyolab; internal static class PlayerUidExtension { - [SuppressMessage("", "SA1010")] public static string ToQueryString(this in PlayerUid playerUid) { NameValueCollection collection = []; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/AvatarPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/AvatarPromotionDelta.cs index c59e88dc..39578208 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/AvatarPromotionDelta.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/AvatarPromotionDelta.cs @@ -53,12 +53,12 @@ internal sealed class AvatarPromotionDelta return new() { AvatarLevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarLevelTarget, 90U), - SkillList = new() - { + SkillList = + [ new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillATarget, 10U), }, new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillETarget, 10U), }, new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillQTarget, 10U), }, - }, + ], Weapon = new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationWeapon90LevelTarget, 90U), }, }; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs index 79c6c126..da9af651 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs @@ -57,7 +57,7 @@ internal sealed partial class CalculateClient int currentPage = 1; SyncAvatarFilter filter = new() { Uid = userAndUid.Uid.Value, Region = userAndUid.Uid.Region }; - List avatars = new(); + List avatars = []; Response>? resp; do @@ -166,10 +166,10 @@ internal sealed partial class CalculateClient private class SyncAvatarFilter { [JsonPropertyName("element_attr_ids")] - public List? ElementAttrIds { get; set; } = new(); + public List? ElementAttrIds { get; set; } = []; [JsonPropertyName("weapon_cat_ids")] - public List? WeaponCatIds { get; set; } = new(); + public List? WeaponCatIds { get; set; } = []; [JsonPropertyName("page")] public int Page { get; set; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs index beb9c7bf..565e9b0f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using System.Collections.Frozen; using System.Collections.Immutable; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; @@ -17,10 +18,7 @@ internal sealed class Expedition private const string Keqing = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Keqing.png"; private const string Sara = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Sara.png"; - private static readonly ImmutableList ShortExpeditionTimeAvatars = new List() - { - Bennett, Chongyun, Fischl, Keqing, Sara, - }.ToImmutableList(); + private static readonly FrozenSet ShortExpeditionTimeAvatars = FrozenSet.ToFrozenSet([Bennett, Chongyun, Fischl, Keqing, Sara]); /// /// 图标 diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index a12c3088..a196fdac 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -53,7 +53,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient { // Replace message resp.Message = SH.WebDailyNoteVerificationFailed; - IGeetestCardVerifier verifier = serviceProvider.GetRequiredService(); + IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService(GeetestCardVerifierType.Custom); if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) { @@ -99,7 +99,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient { // Replace message resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; - IGeetestCardVerifier verifier = serviceProvider.GetRequiredService(); + IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService(GeetestCardVerifierType.Custom); if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) { @@ -146,7 +146,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient { // Replace message resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; - IGeetestCardVerifier verifier = serviceProvider.GetRequiredService(); + IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService(GeetestCardVerifierType.Custom); if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) { @@ -156,9 +156,9 @@ internal sealed partial class GameRecordClient : IGameRecordClient .SetXrpcChallenge(challenge) .Get(); - await builder.SetDynamicSecretAsync(DynamicSecretVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); + await verifiedbuilder.SetDynamicSecretAsync(DynamicSecretVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); - resp = await builder + resp = await verifiedbuilder .TryCatchSendAsync>(httpClient, logger, token) .ConfigureAwait(false); } @@ -216,7 +216,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient { // Replace message resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; - IGeetestCardVerifier verifier = serviceProvider.GetRequiredService(); + IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService(GeetestCardVerifierType.Custom); if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/GeetestCardVerifierType.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/GeetestCardVerifierType.cs new file mode 100644 index 00000000..f8dedec3 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/GeetestCardVerifierType.cs @@ -0,0 +1,9 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Verification; + +internal enum GeetestCardVerifierType +{ + Custom, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/HomaGeetestCardVerifier.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/HomaGeetestCardVerifier.cs index 634c73ec..789d9f7c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/HomaGeetestCardVerifier.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Verification/HomaGeetestCardVerifier.cs @@ -7,7 +7,7 @@ using Snap.Hutao.Web.Hutao.Geetest; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Verification; [ConstructorGenerated] -[Injection(InjectAs.Transient)] +[Injection(InjectAs.Transient, Key = GeetestCardVerifierType.Custom)] internal sealed partial class HomaGeetestCardVerifier : IGeetestCardVerifier { private readonly CardClient cardClient; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/Converter/ReliquarySetsConverter.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/Converter/ReliquarySetsConverter.cs index 0cae6d25..89b60540 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/Converter/ReliquarySetsConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/Converter/ReliquarySetsConverter.cs @@ -14,7 +14,6 @@ internal sealed class ReliquarySetsConverter : JsonConverter private const char Separator = ','; /// - [SuppressMessage("", "SA1010")] public override ReliquarySets? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.GetString() is { } source) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/JsonHttpContentSerializer.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/JsonHttpContentSerializer.cs index 20d9caf4..9dbfd363 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/JsonHttpContentSerializer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Request/Builder/JsonHttpContentSerializer.cs @@ -62,7 +62,7 @@ internal class JsonHttpContentSerializer : HttpContentSerializer return JsonSerializer.Deserialize(json, contentType, JsonSerializerOptions); } - private HttpContent? SerializeUtf8(object? content, Type contentType) + private ByteArrayContent? SerializeUtf8(object? content, Type contentType) { byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(content, contentType, JsonSerializerOptions); ByteArrayContent httpContent = new(bytes); @@ -75,7 +75,7 @@ internal class JsonHttpContentSerializer : HttpContentSerializer return httpContent; } - private HttpContent? SerializeOtherEncoding(object? content, Type contentType, Encoding encoding) + private StringContent? SerializeOtherEncoding(object? content, Type contentType, Encoding encoding) { string str = JsonSerializer.Serialize(content, contentType, JsonSerializerOptions); return new StringContent(str, encoding, MediaType.ApplicationJson);