diff --git a/src/Snap.Hutao/.editorconfig b/src/Snap.Hutao/.editorconfig index fc537b99..057deeb9 100644 --- a/src/Snap.Hutao/.editorconfig +++ b/src/Snap.Hutao/.editorconfig @@ -109,6 +109,7 @@ dotnet_diagnostic.SA1629.severity = none dotnet_diagnostic.SA1642.severity = none dotnet_diagnostic.IDE0060.severity = none +dotnet_diagnostic.IDE0290.severity = none # SA1208: System using directives should be placed before other using directives dotnet_diagnostic.SA1208.severity = none @@ -322,6 +323,8 @@ dotnet_diagnostic.CA2227.severity = suggestion # CA2251: 使用 “string.Equals” dotnet_diagnostic.CA2251.severity = suggestion +csharp_style_prefer_primary_constructors = true:suggestion +dotnet_diagnostic.SA1010.severity = none [*.vb] #### 命名样式 #### diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/HttpClientGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/HttpClientGenerator.cs index 112993fd..b679cf02 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/HttpClientGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/HttpClientGenerator.cs @@ -86,7 +86,7 @@ internal sealed class HttpClientGenerator : IIncrementalGenerator private static void FillUpWithAddHttpClient(StringBuilder sourceBuilder, SourceProductionContext production, ImmutableArray contexts) { - List lines = new(); + List lines = []; StringBuilder lineBuilder = new(); foreach (GeneratorSyntaxContext2 context in contexts.DistinctBy(c => c.Symbol.ToDisplayString())) diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs index 6d51142e..b264850b 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DependencyInjection/InjectionGenerator.cs @@ -81,7 +81,7 @@ internal sealed class InjectionGenerator : IIncrementalGenerator private static void FillUpWithAddServices(StringBuilder sourceBuilder, SourceProductionContext production, ImmutableArray contexts) { - List lines = new(); + List lines = []; StringBuilder lineBuilder = new(); foreach (GeneratorSyntaxContext2 context in contexts.DistinctBy(c => c.Symbol.ToDisplayString())) diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/JsonParser.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/JsonParser.cs index 1aed7884..24d06d4e 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/JsonParser.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/JsonParser.cs @@ -41,10 +41,10 @@ public static class JsonParser public static T? FromJson(this string json) { // Initialize, if needed, the ThreadStatic variables - propertyInfoCache ??= new Dictionary>(); - fieldInfoCache ??= new Dictionary>(); - stringBuilder ??= new StringBuilder(); - splitArrayPool ??= new Stack>(); + propertyInfoCache ??= []; + fieldInfoCache ??= []; + stringBuilder ??= new(); + splitArrayPool ??= []; // Remove all whitespace not within strings to make parsing simpler stringBuilder.Length = 0; @@ -99,7 +99,7 @@ public static class JsonParser // Splits { :, : } and [ , ] into a list of strings private static List Split(string json) { - List splitArray = splitArrayPool!.Count > 0 ? splitArrayPool.Pop() : new List(); + List splitArray = splitArrayPool!.Count > 0 ? splitArrayPool.Pop() : []; splitArray.Clear(); if (json.Length == 2) { diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/EnumerableExtension.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/EnumerableExtension.cs index 05ee1f6a..544504a3 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/EnumerableExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Primitive/EnumerableExtension.cs @@ -20,7 +20,7 @@ internal static class EnumerableExtension if (enumerator.MoveNext()) { - HashSet set = new(); + HashSet set = []; do { diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Resx/ResxGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Resx/ResxGenerator.cs index 17979d76..a9472dbd 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Resx/ResxGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Resx/ResxGenerator.cs @@ -39,10 +39,8 @@ public sealed class ResxGenerator : IIncrementalGenerator private static void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider options, string? assemblyName, bool supportNullableReferenceTypes, ImmutableArray files) { // Group additional file by resource kind ((a.resx, a.en.resx, a.en-us.resx), (b.resx, b.en-us.resx)) - List> resxGroups = files - .GroupBy(file => GetResourceName(file.Path), StringComparer.OrdinalIgnoreCase) - .OrderBy(x => x.Key, StringComparer.Ordinal) - .ToList(); + List> resxGroups = [.. files.GroupBy(file => GetResourceName(file.Path), StringComparer.OrdinalIgnoreCase).OrderBy(x => x.Key, StringComparer.Ordinal)]; + foreach (IGrouping? resxGroug in resxGroups) { @@ -400,7 +398,7 @@ public sealed class ResxGenerator : IIncrementalGenerator private static List? LoadResourceFiles(SourceProductionContext context, IGrouping resxGroug) { - List entries = new(); + List entries = []; foreach (AdditionalText? entry in resxGroug.OrderBy(file => file.Path, StringComparer.Ordinal)) { SourceText? content = entry.GetText(context.CancellationToken); diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs index 2c8d6f77..d09bbc6a 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs @@ -57,23 +57,13 @@ internal sealed class UniversalAnalyzer : DiagnosticAnalyzer private static void CompilationStart(CompilationStartAnalysisContext context) { - SyntaxKind[] types = - { - SyntaxKind.ClassDeclaration, - SyntaxKind.InterfaceDeclaration, - SyntaxKind.StructDeclaration, - SyntaxKind.EnumDeclaration, - }; + SyntaxKind[] types = [SyntaxKind.ClassDeclaration, SyntaxKind.InterfaceDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.EnumDeclaration,]; context.RegisterSyntaxNodeAction(HandleTypeShouldBeInternal, types); context.RegisterSyntaxNodeAction(HandleMethodParameterShouldUseRefLikeKeyword, SyntaxKind.MethodDeclaration); context.RegisterSyntaxNodeAction(HandleMethodReturnTypeShouldUseValueTaskInsteadOfTask, SyntaxKind.MethodDeclaration); context.RegisterSyntaxNodeAction(HandleConstructorParameterShouldUseRefLikeKeyword, SyntaxKind.ConstructorDeclaration); - SyntaxKind[] expressions = - { - SyntaxKind.EqualsExpression, - SyntaxKind.NotEqualsExpression, - }; + SyntaxKind[] expressions = [SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression,]; context.RegisterSyntaxNodeAction(HandleEqualsAndNotEqualsExpressionShouldUsePatternMatching, expressions); context.RegisterSyntaxNodeAction(HandleIsPatternShouldUseRecursivePattern, SyntaxKind.IsPatternExpression); context.RegisterSyntaxNodeAction(HandleArgumentNullExceptionThrowIfNull, SyntaxKind.SuppressNullableWarningExpression); diff --git a/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs index 98b818a1..d9c8e0fc 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/JsonSerializeTest.cs @@ -7,6 +7,11 @@ namespace Snap.Hutao.Test; [TestClass] public class JsonSerializeTest { + private readonly JsonSerializerOptions AlowStringNumberOptions = new() + { + NumberHandling = JsonNumberHandling.AllowReadingFromString, + }; + private const string SmapleObjectJson = """ { "A" :1 @@ -44,12 +49,7 @@ public class JsonSerializeTest [TestMethod] public void NumberStringKeyCanSerializeAsKey() { - JsonSerializerOptions options = new() - { - NumberHandling = JsonNumberHandling.AllowReadingFromString, - }; - - Dictionary sample = JsonSerializer.Deserialize>(SmapleNumberKeyDictionaryJson, options)!; + Dictionary sample = JsonSerializer.Deserialize>(SmapleNumberKeyDictionaryJson, AlowStringNumberOptions)!; Assert.AreEqual(sample[111], "12"); } diff --git a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs index 8011f30c..5caeb2e6 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/ForEachRuntimeBehaviorTest.cs @@ -9,10 +9,7 @@ public sealed class ForEachRuntimeBehaviorTest [TestMethod] public void ListOfStringCanEnumerateAsReadOnlySpanOfChar() { - List strings = new() - { - "a", "b", "c" - }; + List strings = ["a", "b", "c"]; 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 9634543e..bb43cef2 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/RangeRuntimeBehaviorTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/RangeRuntimeBehaviorTest.cs @@ -8,8 +8,8 @@ public sealed class RangeRuntimeBehaviorTest [TestMethod] public void RangeTrimLastOne() { - int[] array = { 1, 2, 3, 4 }; - int[] test = { 1, 2, 3 }; + int[] array = [1, 2, 3, 4]; + int[] test = [1, 2, 3]; 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 46c5db18..e69e6f04 100644 --- a/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/UnsafeRuntimeBehaviorTest.cs +++ b/src/Snap.Hutao/Snap.Hutao.Test/RuntimeBehavior/UnsafeRuntimeBehaviorTest.cs @@ -6,7 +6,7 @@ public sealed class UnsafeRuntimeBehaviorTest [TestMethod] public unsafe void UInt32AllSetIs() { - byte[] bytes = { 0xFF, 0xFF, 0xFF, 0xFF, }; + byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF,]; fixed (byte* pBytes = bytes) { diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs index e10f2a65..1426af0e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/DefaultItemCollectionTransitionProvider.cs @@ -27,9 +27,9 @@ internal sealed class DefaultItemCollectionTransitionProvider : ItemCollectionTr protected override void StartTransitions(IList transitions) { - List addTransitions = new(); - List removeTransitions = new(); - List moveTransitions = new(); + List addTransitions = []; + List removeTransitions = []; + List moveTransitions = []; foreach (ItemCollectionTransition transition in addTransitions) { diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs index ce1b24f9..477fe2a9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayout.cs @@ -118,7 +118,7 @@ internal sealed partial class UniformStaggeredLayout : VirtualizingLayout Span columnHeights = new double[numberOfColumns]; Span itemsPerColumn = new int[numberOfColumns]; - HashSet deadColumns = new(); + HashSet deadColumns = []; for (int i = 0; i < context.ItemCount; i++) { diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs index bb44b651..bd191648 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Layout/UniformStaggeredLayoutState.cs @@ -9,9 +9,9 @@ namespace Snap.Hutao.Control.Layout; internal sealed class UniformStaggeredLayoutState { - private readonly List items = new(); + private readonly List items = []; private readonly VirtualizingLayoutContext context; - private readonly Dictionary columnLayout = new(); + private readonly Dictionary columnLayout = []; private double lastAverageHeight; public UniformStaggeredLayoutState(VirtualizingLayoutContext context) @@ -32,7 +32,7 @@ internal sealed class UniformStaggeredLayoutState { if (!this.columnLayout.TryGetValue(columnIndex, out UniformStaggeredColumnLayout? columnLayout)) { - columnLayout = new(); + columnLayout = []; this.columnLayout[columnIndex] = columnLayout; } diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs b/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs index a2cdec3f..bb443a05 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs @@ -11,7 +11,6 @@ internal class Loading : Microsoft.UI.Xaml.Controls.ContentControl { public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(Loading), new PropertyMetadata(default(bool), IsLoadingPropertyChanged)); - [SuppressMessage("", "IDE0052")] private FrameworkElement? presenter; public Loading() diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs index 0625da8d..c4b16a8d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs @@ -62,7 +62,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation /// public void Remove(Uri uriForCachedItem) { - Remove(new ReadOnlySpan(uriForCachedItem)); + Remove(new ReadOnlySpan(ref uriForCachedItem)); } /// @@ -76,7 +76,7 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation string folder = GetCacheFolder(); string[] files = Directory.GetFiles(folder); - List filesToDelete = new(); + List filesToDelete = []; foreach (ref readonly Uri uri in uriForCachedItems) { string filePath = Path.Combine(folder, GetCacheFileName(uri)); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CommandLineBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Core/CommandLineBuilder.cs index 9837e368..ca8af27f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CommandLineBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CommandLineBuilder.cs @@ -12,7 +12,8 @@ namespace Snap.Hutao.Core; internal sealed class CommandLineBuilder { private const char WhiteSpace = ' '; - private readonly Dictionary options = new(); + + private readonly Dictionary options = []; /// /// 当符合条件时添加参数 diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Ini/IniSerializer.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Ini/IniSerializer.cs index c8dfff1c..d51ddcfe 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Ini/IniSerializer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Ini/IniSerializer.cs @@ -18,7 +18,7 @@ internal static class IniSerializer /// Ini 元素集合 public static List Deserialize(FileStream fileStream) { - List results = new(); + List results = []; using (StreamReader reader = new(fileStream)) { while (reader.ReadLine() is { } line) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs index bffefbb1..1e3be7d6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs @@ -34,7 +34,7 @@ internal sealed class SeparatorCommaInt32EnumerableConverter : JsonConverter EnumerateNumbers(string source) { // TODO: Use Collection Literals - foreach (StringSegment id in new StringTokenizer(source, new[] { Comma })) + foreach (StringSegment id in new StringTokenizer(source, [Comma])) { yield return int.Parse(id.AsSpan(), CultureInfo.CurrentCulture); } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs index 06f24566..c4135c78 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppInstanceExtension.cs @@ -37,7 +37,7 @@ internal static class AppInstanceExtension SetEvent(redirectEventHandle); }); - ReadOnlySpan handles = new(redirectEventHandle); + ReadOnlySpan handles = new(ref redirectEventHandle); CoWaitForMultipleObjects((uint)CWMO_FLAGS.CWMO_DEFAULT, INFINITE, handles, out uint _); // TODO: Release handle diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueExtension.cs index 01df78e1..692004e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueExtension.cs @@ -45,9 +45,7 @@ internal static class DispatcherQueueExtension }); blockEvent.Wait(); -#pragma warning disable CA1508 exceptionDispatchInfo?.Throw(); -#pragma warning restore CA1508 } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs index 4741e8a9..8056c4b7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs @@ -8,6 +8,7 @@ using static Windows.Win32.PInvoke; namespace Snap.Hutao.Core.Windowing.HotKey; [SuppressMessage("", "CA1001")] +[Injection(InjectAs.Singleton, typeof(IHotKeyController))] [ConstructorGenerated] internal sealed partial class HotKeyController : IHotKeyController { @@ -53,10 +54,10 @@ internal sealed partial class HotKeyController : IHotKeyController while (!token.IsCancellationRequested) { INPUT[] inputs = - { + [ CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN), CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP), - }; + ]; if (SendInput(inputs.AsSpan(), sizeof(INPUT)) is 0) { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs index 706e7547..aa7e8a6b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs @@ -204,6 +204,6 @@ internal sealed class WindowController // 48 is the navigation button leftInset RectInt32 dragRect = StructMarshal.RectInt32(48, 0, options.TitleBar.ActualSize).Scale(scale); - appTitleBar.SetDragRectangles(dragRect.ToArray()); + appTitleBar.SetDragRectangles([dragRect]); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs index 52f22eae..e6598b80 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs @@ -8,7 +8,7 @@ namespace Snap.Hutao.Core.Windowing; internal static class WindowExtension { - private static readonly ConditionalWeakTable WindowControllers = new(); + private static readonly ConditionalWeakTable WindowControllers = []; public static void InitializeController(this TWindow window, IServiceProvider serviceProvider) where TWindow : Window, IWindowOptionsSource diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs index 7a7aabbd..c3e9ac21 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs @@ -33,7 +33,8 @@ internal sealed class WindowSubclass : IDisposable this.window = window; this.options = options; this.serviceProvider = serviceProvider; - hotKeyController = new HotKeyController(serviceProvider); + + hotKeyController = serviceProvider.GetRequiredService(); } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs index 555b285b..c6897d7f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs @@ -80,7 +80,7 @@ internal static partial class EnumerableExtension public static Dictionary ToDictionaryIgnoringDuplicateKeys(this IEnumerable source, Func keySelector) where TKey : notnull { - Dictionary dictionary = new(); + Dictionary dictionary = []; foreach (TSource value in source) { @@ -94,7 +94,7 @@ internal static partial class EnumerableExtension public static Dictionary ToDictionaryIgnoringDuplicateKeys(this IEnumerable source, Func keySelector, Func elementSelector) where TKey : notnull { - Dictionary dictionary = new(); + Dictionary dictionary = []; foreach (TSource value in source) { diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs index 95a3adfa..68b066d5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs @@ -69,7 +69,7 @@ internal static partial class EnumerableExtension [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List EmptyIfNull(this List? source) { - return source ?? new(); + return source ?? []; } public static List GetRange(this List list, in Range range) diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.NameValueCollection.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.NameValueCollection.cs new file mode 100644 index 00000000..9d3840c0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.NameValueCollection.cs @@ -0,0 +1,24 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Specialized; + +namespace Snap.Hutao.Extension; + +internal static partial class EnumerableExtension +{ + public static bool TryGetValue(this NameValueCollection collection, string name, [NotNullWhen(true)] out string? value) + { + if (collection.AllKeys.Contains(name)) + { + if (collection.GetValues(name) is [string single]) + { + value = single; + return true; + } + } + + value = string.Empty; + return false; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs index c4976887..eaa5e998 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs @@ -30,20 +30,6 @@ internal static partial class EnumerableExtension return source ?? Enumerable.Empty(); } - /// - /// 将源转换为仅包含单个元素的枚举 - /// - /// 源的类型 - /// 源 - /// 集合 -#if NET8_0 - [Obsolete("Use C# 12 Collection Literal instead")] -#endif - public static IEnumerable Enumerate(this TSource source) - { - yield return source; - } - /// /// 寻找枚举中唯一的值,找不到时 /// 回退到首个或默认值 diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtension.cs deleted file mode 100644 index 7c631bd3..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtension.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using System.Runtime.CompilerServices; - -namespace Snap.Hutao.Extension; - -/// -/// 对象拓展 -/// -internal static class ObjectExtension -{ - /// - /// 转换到只有1长度的数组 - /// - /// 数据类型 - /// 源 - /// 数组 -#if NET8_0 - [Obsolete("Use C# 12 Collection Literals")] -#endif - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] ToArray(this T source) - { - // TODO: use C# 12 collection literals - // [ source ] - return new[] { source }; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs index d5cc8023..301302fc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs @@ -1,6 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using System.Collections.Frozen; + namespace Snap.Hutao.Model.InterChange.Achievement; /// @@ -15,11 +17,7 @@ internal sealed class UIAF /// public const string CurrentVersion = "v1.1"; - // TODO use FrozenSet - private static readonly HashSet SupportedVersion = new() - { - CurrentVersion, - }; + private static readonly FrozenSet SupportedVersion = FrozenSet.ToFrozenSet([CurrentVersion]); /// /// 信息 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs index 8a56acb3..d19644b8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.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.Model.InterChange.Inventory; @@ -16,10 +17,7 @@ internal sealed class UIIF /// public const string CurrentVersion = "v1.0"; - private static readonly ImmutableList SupportedVersion = new List() - { - CurrentVersion, - }.ToImmutableList(); + private static readonly FrozenSet SupportedVersion = FrozenSet.ToFrozenSet([CurrentVersion]); /// /// 信息 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Monster/MonsterBaseValue.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Monster/MonsterBaseValue.cs index 49be55d8..fe446a02 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Monster/MonsterBaseValue.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Monster/MonsterBaseValue.cs @@ -58,8 +58,8 @@ internal sealed class MonsterBaseValue : BaseValue { get { - return new() - { + return + [ FightPropertyFormat.ToNameValue(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, FireSubHurt), FightPropertyFormat.ToNameValue(FightProperty.FIGHT_PROP_WATER_SUB_HURT, WaterSubHurt), FightPropertyFormat.ToNameValue(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, GrassSubHurt), @@ -68,7 +68,7 @@ internal sealed class MonsterBaseValue : BaseValue FightPropertyFormat.ToNameValue(FightProperty.FIGHT_PROP_ICE_SUB_HURT, IceSubHurt), FightPropertyFormat.ToNameValue(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, RockSubHurt), FightPropertyFormat.ToNameValue(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, PhysicalSubHurt), - }; + ]; } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs index 1e7efec6..ee6bab37 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs @@ -129,10 +129,7 @@ internal sealed partial class AchievementDbService : IAchievementDbService using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return appDbContext.Achievements - .AsNoTracking() - .Where(i => i.ArchiveId == archiveId) - .ToList(); + return [.. appDbContext.Achievements.AsNoTracking().Where(i => i.ArchiveId == archiveId)]; } } @@ -154,7 +151,7 @@ internal sealed partial class AchievementDbService : IAchievementDbService using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return appDbContext.AchievementArchives.AsNoTracking().ToList(); + return [.. appDbContext.AchievementArchives.AsNoTracking()]; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementStatisticsService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementStatisticsService.cs index d7cf8deb..f588b786 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementStatisticsService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementStatisticsService.cs @@ -21,7 +21,7 @@ internal sealed partial class AchievementStatisticsService : IAchievementStatist { await taskContext.SwitchToBackgroundAsync(); - List results = new(); + List results = []; foreach (AchievementArchive archive in await achievementDbService.GetAchievementArchiveListAsync().ConfigureAwait(false)) { int finishedCount = await achievementDbService diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs index 96121b9a..d1a2a707 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs @@ -21,14 +21,14 @@ internal sealed partial class AppOptions : DbStoreOptions { private readonly List> supportedBackdropTypesInner = CollectionsNameValue.ListFromEnum(); - private readonly List> supportedCulturesInner = new() - { + private readonly List> supportedCulturesInner = + [ ToNameValue(CultureInfo.GetCultureInfo("zh-Hans")), ToNameValue(CultureInfo.GetCultureInfo("zh-Hant")), ToNameValue(CultureInfo.GetCultureInfo("en")), ToNameValue(CultureInfo.GetCultureInfo("ko")), ToNameValue(CultureInfo.GetCultureInfo("ja")), - }; + ]; private string? gamePath; private string? powerShellPath; @@ -126,7 +126,7 @@ internal sealed partial class AppOptions : DbStoreOptions string? paths = Environment.GetEnvironmentVariable("Path"); if (!string.IsNullOrEmpty(paths)) { - foreach (StringSegment path in new StringTokenizer(paths, ';'.ToArray())) + foreach (StringSegment path in new StringTokenizer(paths, [';'])) { if (path is { HasValue: true, Length: > 0 }) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs index c16003bb..7bf6e4ff 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs @@ -243,7 +243,7 @@ internal sealed partial class AvatarInfoDbBulkOperation if (distinctCount < dbInfos.Count) { avatarInfoDbService.RemoveAvatarInfoRangeByUid(uid); - dbInfos = new(); + dbInfos = []; } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs index 0a0c4914..ab917c1b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs @@ -19,7 +19,7 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return appDbContext.AvatarInfos.AsNoTracking().Where(i => i.Uid == uid).ToList(); + return [.. appDbContext.AvatarInfos.AsNoTracking().Where(i => i.Uid == uid)]; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index 490548ec..67b1ecb8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -38,7 +38,7 @@ internal sealed partial class CultivationService : ICultivationService Guid projectId = cultivateProject.InnerId; List entities = cultivationDbService.GetInventoryItemListByProjectId(projectId); - List results = new(); + List results = []; foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id.Value)) { InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id); @@ -64,7 +64,7 @@ internal sealed partial class CultivationService : ICultivationService List resultEntries = new(entries.Count); foreach (CultivateEntry entry in entries) { - List entryItems = new(); + List entryItems = []; foreach (CultivateItem item in await cultivationDbService.GetCultivateItemListByEntryIdAsync(entry.InnerId).ConfigureAwait(false)) { entryItems.Add(new(item, materials.Single(m => m.Id == item.ItemId))); @@ -94,7 +94,7 @@ internal sealed partial class CultivationService : ICultivationService CancellationToken token) { await taskContext.SwitchToBackgroundAsync(); - List resultItems = new(); + List resultItems = []; Guid projectId = cultivateProject.InnerId; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs index e243efe7..be8ff4a4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs @@ -66,11 +66,12 @@ internal struct GachaLogFetchContext /// /// 卡池类型 /// 查询 + [SuppressMessage("", "SA1010")] public void ResetForProcessingType(GachaConfigType configType, in GachaLogQuery query) { DbEndId = null; CurrentType = configType; - ItemsToAdd = new(); + ItemsToAdd = []; FetchStatus = new(configType); QueryOptions = new(query, configType); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs index 2a2bbc38..ee2bd7af 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs @@ -4,7 +4,8 @@ using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Service.Metadata; using Snap.Hutao.View.Dialog; -using Snap.Hutao.Web.Request.QueryString; +using System.Collections.Specialized; +using System.Web; namespace Snap.Hutao.Service.GachaLog.QueryProvider; @@ -27,10 +28,11 @@ internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryP if (isOk) { - QueryString query = QueryString.Parse(queryString); + NameValueCollection query = HttpUtility.ParseQueryString(queryString); + if (query.TryGetValue("auth_appid", out string? appId) && appId is "webview_gacha") { - string queryLanguageCode = query["lang"]; + string? queryLanguageCode = query["lang"]; if (metadataOptions.IsCurrentLocale(queryLanguageCode)) { return new(true, new(queryString)); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs index 0f76f308..c0dab42a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuerySTokenProvider.cs @@ -4,9 +4,10 @@ using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.User; using Snap.Hutao.ViewModel.User; -using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; +using Snap.Hutao.Web.Request; using Snap.Hutao.Web.Response; +using System.Collections.Specialized; namespace Snap.Hutao.Service.GachaLog.QueryProvider; @@ -37,7 +38,7 @@ internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvid if (authkeyResponse.IsOk()) { - return new(true, new(GachaLogQueryOptions.ToQueryString(data, authkeyResponse.Data, metadataOptions.LanguageCode))); + return new(true, new(ComposeQueryString(data, authkeyResponse.Data, metadataOptions.LanguageCode))); } else { @@ -49,4 +50,17 @@ internal sealed partial class GachaLogQuerySTokenProvider : IGachaLogQueryProvid return new(false, SH.MustSelectUserAndUid); } } + + [SuppressMessage("", "SA1010")] + private static string ComposeQueryString(GenAuthKeyData genAuthKeyData, GameAuthKey gameAuthKey, string lang) + { + NameValueCollection collection = []; + collection.Set("lang", lang); + collection.Set("auth_appid", genAuthKeyData.AuthAppId); + collection.Set("authkey", gameAuthKey.AuthKey); + collection.Set("authkey_ver", $"{gameAuthKey.AuthKeyVersion:D}"); + collection.Set("sign_type", $"{gameAuthKey.SignType:D}"); + + return collection.ToQueryString(); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs index 3b37e5f9..f19565ad 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs @@ -4,10 +4,11 @@ using Snap.Hutao.Core.IO; using Snap.Hutao.Service.Game; using Snap.Hutao.Service.Metadata; -using Snap.Hutao.Web.Request.QueryString; +using System.Collections.Specialized; using System.IO; using System.Text; using System.Text.RegularExpressions; +using System.Web; namespace Snap.Hutao.Service.GachaLog.QueryProvider; @@ -84,8 +85,8 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv return new(false, SH.ServiceGachaLogUrlProviderCacheUrlNotFound); } - QueryString query = QueryString.Parse(result.TrimEnd("#/log")); - string queryLanguageCode = query["lang"]; + NameValueCollection query = HttpUtility.ParseQueryString(result.TrimEnd("#/log")); + string? queryLanguageCode = query["lang"]; if (metadataOptions.IsCurrentLocale(queryLanguageCode)) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs index ac89edb7..2e3eba2d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs @@ -108,7 +108,7 @@ internal sealed partial class MetadataOptions : IOptions /// /// 语言代码 /// 是否为当前语言名称 - public bool IsCurrentLocale(string languageCode) + public bool IsCurrentLocale(string? languageCode) { if (string.IsNullOrEmpty(languageCode)) { diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 142dbe5e..1139cf54 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -1,7 +1,7 @@  WinExe - net8.0-windows10.0.19041.0 + net8.0-windows10.0.22621.0 10.0.19041.0 Snap.Hutao app.manifest @@ -278,6 +278,7 @@ + @@ -304,6 +305,7 @@ + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs index 35aecde4..8d9ba7db 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs @@ -252,6 +252,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav } } + [SuppressMessage("", "SA1010")] [Command("ExportAsUIAFToFileCommand")] private async Task ExportAsUIAFToFileAsync() { @@ -260,7 +261,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav string fileName = $"{achievementService.CurrentArchive?.Name}.json"; Dictionary> fileTypes = new() { - [SH.ViewModelAchievementExportFileType] = ".json".Enumerate().ToList(), + [SH.ViewModelAchievementExportFileType] = [".json"], }; FileSavePicker picker = pickerFactory diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteWebViewerSource.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteWebViewerSource.cs index 830c060b..4207da74 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteWebViewerSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteWebViewerSource.cs @@ -6,7 +6,6 @@ using Snap.Hutao.View.Control; using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Bridge; using Snap.Hutao.Web.Hoyolab; -using Snap.Hutao.Web.Request.QueryString; namespace Snap.Hutao.ViewModel.DailyNote; @@ -19,7 +18,7 @@ internal sealed class DailyNoteWebViewerSource : IWebViewerSource public string GetSource(UserAndUid userAndUid) { - QueryString query = userAndUid.Uid.ToQueryString(); + string query = userAndUid.Uid.ToQueryString(); return $"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/?{query}"; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs index 6dbf21d8..983bed24 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs @@ -231,6 +231,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel } } + [SuppressMessage("", "SA1010")] [Command("ExportToUIGFJsonCommand")] private async Task ExportToUIGFJsonAsync() { @@ -238,7 +239,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel { Dictionary> fileTypes = new() { - [SH.ViewModelGachaLogExportFileType] = ".json".Enumerate().ToList(), + [SH.ViewModelGachaLogExportFileType] = [".json"], }; FileSavePicker picker = pickerFactory.GetFileSavePicker( diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs index 93331755..ce20ac4e 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs @@ -22,11 +22,12 @@ 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 = new(); + List matches = []; - foreach (StringSegment segment in new StringTokenizer(input, ' '.Enumerate().ToArray())) + foreach (StringSegment segment in new StringTokenizer(input, [' '])) { string value = segment.ToString(); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs index 5352e0c6..11d31ea4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs @@ -2,8 +2,8 @@ // Licensed under the MIT license. using Snap.Hutao.Service.GachaLog.QueryProvider; -using Snap.Hutao.Web.Hoyolab.Takumi.Binding; -using Snap.Hutao.Web.Request.QueryString; +using System.Collections.Specialized; +using System.Web; namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; @@ -45,7 +45,7 @@ internal struct GachaLogQueryOptions /// size /// end_id /// - private readonly QueryString innerQuery; + private readonly NameValueCollection innerQuery; /// /// 构造一个新的祈愿记录请求配置 @@ -60,36 +60,17 @@ internal struct GachaLogQueryOptions // 对于每个类型我们需要单独创建 // 对应类型的 GachaLogQueryOptions Type = queryType; - innerQuery = QueryString.Parse(query.Query); - - // innerQuery.Set("lang", "zh-cn"); - innerQuery.Set("gacha_type", (int)queryType); - innerQuery.Set("size", Size); - } - - /// - /// 转换到查询字符串 - /// - /// 生成信息 - /// 验证包装 - /// 语言 - /// 查询 - public static string ToQueryString(GenAuthKeyData genAuthKeyData, GameAuthKey gameAuthKey, string lang) - { - QueryString queryString = new(); - queryString.Set("lang", lang); - queryString.Set("auth_appid", genAuthKeyData.AuthAppId); - queryString.Set("authkey", Uri.EscapeDataString(gameAuthKey.AuthKey)); - queryString.Set("authkey_ver", gameAuthKey.AuthKeyVersion); - queryString.Set("sign_type", gameAuthKey.SignType); - - return queryString.ToString(); + innerQuery = HttpUtility.ParseQueryString(query.Query); + innerQuery.Set("gacha_type", $"{queryType:D}"); + innerQuery.Set("size", $"{Size}"); } public readonly string ToQueryString() { // Make the cached end id into query. - innerQuery.Set("end_id", EndId); - return innerQuery.ToString(); + innerQuery.Set("end_id", $"{EndId:D}"); + string? query = innerQuery.ToString(); + ArgumentException.ThrowIfNullOrEmpty(query); + return query; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs index 4dcb9eda..1610cd37 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUidExtension.cs @@ -1,18 +1,20 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Web.Request.QueryString; +using Snap.Hutao.Web.Request; +using System.Collections.Specialized; namespace Snap.Hutao.Web.Hoyolab; internal static class PlayerUidExtension { - public static QueryString ToQueryString(this in PlayerUid playerUid) + [SuppressMessage("", "SA1010")] + public static string ToQueryString(this in PlayerUid playerUid) { - QueryString queryString = new(); - queryString.Set("role_id", playerUid.Value); - queryString.Set("server", playerUid.Region); + NameValueCollection collection = []; + collection.Set("role_id", playerUid.Value); + collection.Set("server", playerUid.Region); - return queryString; + return collection.ToQueryString(); } } \ No newline at end of file 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 9627299a..0cae6d25 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,12 +14,13 @@ 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) { - List sets = new(); - foreach (StringSegment segment in new StringTokenizer(source, Separator.ToArray())) + List sets = []; + foreach (StringSegment segment in new StringTokenizer(source, [Separator])) { if (segment is { HasValue: true, Length: > 0 }) { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/NameValueCollectionExtension.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/NameValueCollectionExtension.cs new file mode 100644 index 00000000..660a28c0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Request/NameValueCollectionExtension.cs @@ -0,0 +1,41 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Specialized; +using System.Text; +using System.Web; + +namespace Snap.Hutao.Web.Request; + +internal static class NameValueCollectionExtension +{ + public static string ToQueryString(this NameValueCollection collection) + { + int count = collection.Count; + if (count == 0) + { + return string.Empty; + } + + StringBuilder sb = new(); + string?[] keys = collection.AllKeys; + for (int i = 0; i < count; i++) + { + string? key = keys[i]; + if (collection.GetValues(key) is { } values) + { + foreach (string value in values) + { + if (!string.IsNullOrEmpty(key)) + { + sb.Append(key).Append('='); + } + + sb.Append(HttpUtility.UrlEncode(value)).Append('&'); + } + } + } + + return sb.Length > 0 ? sb.ToString(0, sb.Length - 1) : string.Empty; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs deleted file mode 100644 index dd22f7d0..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryString.cs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Web.Request.QueryString; - -/// -/// querystring serializer/deserializer -/// -[HighQuality] -internal readonly struct QueryString -{ - private readonly Dictionary> dictionary = new(); - - /// - /// Nothing - /// - public QueryString() - { - } - - /// - /// Gets the first value of the first parameter with the matching name. - /// Throws if a parameter with a matching name could not be found. - /// O(n) where n = Count of the current object. - /// - /// The parameter name to find. - /// query - public string this[string name] - { - get - { - if (TryGetValue(name, out string? value)) - { - return value; - } - - throw new KeyNotFoundException($"A parameter with name '{name}' could not be found."); - } - } - -#if NET8_0 - - private static QueryString Parse(ReadOnlySpan value) - { - // TODO: .NET 8 ReadOnlySpan Split - return default; - } -#endif - - /// - /// Parses a query string into a object. Keys/values are automatically URL decoded. - /// - /// - /// The query string to deserialize. - /// Valid input would be something like "a=1&b=5". - /// URL decoding of keys/values is automatically performed. - /// Also supports query strings that are serialized using ; instead of &, like "a=1;b=5" - /// A new QueryString represents the url query - public static QueryString Parse(string? queryString) - { - if (string.IsNullOrWhiteSpace(queryString)) - { - return new QueryString(); - } - - int questionMarkIndex = queryString.AsSpan().IndexOf('?'); - queryString = queryString[(questionMarkIndex + 1)..]; - - string[] pairs = queryString.Split('&', ';'); - QueryString answer = new(); - foreach (string pair in pairs) - { - string name; - string? value; - int indexOfEquals = pair.IndexOf('=', StringComparison.Ordinal); - if (indexOfEquals == -1) - { - name = pair; - value = string.Empty; - } - else - { - name = pair[..indexOfEquals]; - value = pair[(indexOfEquals + 1)..]; - } - - answer.Add(name, value); - } - - return answer; - } - - /// - /// Gets the first value of the first parameter with the matching name. If no parameter with a matching name exists, returns false. - /// - /// The parameter name to find. - /// The parameter's value will be written here once found. - /// value - public bool TryGetValue(string name, [NotNullWhen(true)] out string? value) - { - if (dictionary.TryGetValue(name, out List? values)) - { - value = values.First(); - return true; - } - - value = null; - return false; - } - - /// - /// Gets the values of the parameter with the matching name. If no parameter with a matching name exists, sets to null and returns false. - /// - /// The parameter name to find. - /// The parameter's values will be written here once found. - /// values - public bool TryGetValues(string name, [NotNullWhen(true)] out string?[]? values) - { - if (dictionary.TryGetValue(name, out List? storedValues)) - { - values = storedValues.ToArray(); - return true; - } - - values = null; - return false; - } - - /// - /// Returns the count of parameters in the current query string. - /// - /// count of the queries - public int Count() - { - return dictionary.Select(i => i.Value.Count).Sum(); - } - - /// - /// Adds a query string parameter to the query string. - /// - /// The name of the parameter. - /// The optional value of the parameter. - public void Add(string name, string value) - { - if (!dictionary.TryGetValue(name, out List? values)) - { - values = new List(); - dictionary[name] = values; - } - - values.Add(value); - } - - /// - public void Set(string name, object value) - { - Set(name, value.ToString() ?? string.Empty); - } - - /// - /// Sets a query string parameter. If there are existing parameters with the same name, they are removed. - /// - /// The name of the parameter. - /// The optional value of the parameter. - public void Set(string name, string value) - { - dictionary[name] = new() { value }; - } - - /// - /// Determines if the query string contains at least one parameter with the specified name. - /// - /// The parameter name to look for. - /// True if the query string contains at least one parameter with the specified name, else false. - public bool Contains(string name) - { - return dictionary.ContainsKey(name); - } - - /// - /// Determines if the query string contains a parameter with the specified name and value. - /// - /// The parameter name to look for. - /// The value to look for when the name has been matched. - /// True if the query string contains a parameter with the specified name and value, else false. - public bool Contains(string name, string value) - { - return dictionary.TryGetValue(name, out List? values) && values.Contains(value); - } - - /// - /// Removes the first parameter with the specified name. - /// - /// The name of parameter to remove. - /// True if the parameters were removed, else false. - public bool Remove(string name) - { - if (dictionary.TryGetValue(name, out List? values)) - { - if (values.Count == 1) - { - dictionary.Remove(name); - } - else - { - values.RemoveAt(0); - } - - return true; - } - - return false; - } - - /// - /// Removes all parameters with the specified name. - /// - /// The name of parameters to remove. - /// True if the parameters were removed, else false. - public bool RemoveAll(string name) - { - return dictionary.Remove(name); - } - - /// - /// Removes the first parameter with the specified name and value. - /// - /// The name of the parameter to remove. - /// value - /// True if parameter was removed, else false. - public bool Remove(string name, string value) - { - if (dictionary.TryGetValue(name, out List? values)) - { - if (values.RemoveFirstWhere(i => Equals(i, value))) - { - // If removed last value, remove the key - if (values.Count == 0) - { - dictionary.Remove(name); - } - - return true; - } - } - - return false; - } - - /// - /// Removes all parameters with the specified name and value. - /// - /// The name of parameters to remove. - /// The value to match when deciding whether to remove. - /// The count of parameters removed. - public int RemoveAll(string name, string value) - { - if (dictionary.TryGetValue(name, out List? values)) - { - int countRemoved = values.RemoveAll(i => Equals(i, value)); - - // If removed last value, remove the key - if (values.Count == 0) - { - dictionary.Remove(name); - } - - return countRemoved; - } - - return 0; - } - - /// - /// Serializes the key-value pairs into a query string, using the default & separator. - /// Produces something like "a=1&b=5". - /// URL encoding of keys/values is automatically performed. - /// Null values are not written (only their key is written). - /// - /// query - public override string ToString() - { - return string.Join('&', GetParameters()); - } - - private IEnumerable GetParameters() - { - foreach ((string name, List values) in dictionary) - { - foreach (string value in values) - { - yield return new QueryStringParameter(name, value); - } - } - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryStringParameter.cs b/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryStringParameter.cs deleted file mode 100644 index 4c652b12..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Request/QueryString/QueryStringParameter.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Web.Request.QueryString; - -/// -/// A single query string parameter (name and value pair). -/// -[HighQuality] -internal struct QueryStringParameter -{ - /// - /// The name of the parameter. Cannot be null. - /// - public string Name; - - /// - /// The value of the parameter (or null if there's no value). - /// - public string Value; - - /// - /// Initializes a new query string parameter with the specified name and optional value. - /// - /// The name of the parameter. Cannot be null. - /// The optional value of the parameter. - internal QueryStringParameter(string name, string value) - { - Name = name; - Value = value; - } - - /// - public override readonly string ToString() - { - return $"{Name}={Value}"; - } -} diff --git a/src/Snap.Hutao/Temp.txt b/src/Snap.Hutao/Temp.txt new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/src/Snap.Hutao/Temp.txt @@ -0,0 +1 @@ + \ No newline at end of file