龙腾猫跃 6 месяцев назад
Родитель
Сommit
ff8cbe2ccc
5 измененных файлов с 43 добавлено и 33 удалено
  1. 6 5
      Extensions/EnumerableExtensions.cs
  2. 12 12
      Extensions/StringExtensions.cs
  3. 2 2
      General/MsDelta.cs
  4. 14 14
      General/OneOf.cs
  5. 9 0
      MeloongCore.csproj

+ 6 - 5
Extensions/EnumerableExtensions.cs

@@ -79,9 +79,9 @@ public static class EnumerableExtensions {
 
     /// <summary>
     /// 选择最大值对应的对象。
-    /// 若没有元素则返回 Nothing。
+    /// 若没有元素则返回 null。
     /// </summary>
-    public static T MaxBy<T, C>(this IEnumerable<T> source, Func<T, C> selector) where C : IComparable<C> {
+    public static T? MaxBy<T, C>(this IEnumerable<T> source, Func<T, C> selector) where C : IComparable<C> {
         using var enumerator = source.GetEnumerator();
         if (!enumerator.MoveNext()) return default;
         T maxItem = enumerator.Current;
@@ -96,9 +96,9 @@ public static class EnumerableExtensions {
     }
     /// <summary>
     /// 选择最小值对应的对象。
-    /// 若没有元素则返回 Nothing。
+    /// 若没有元素则返回 null。
     /// </summary>
-    public static T MinBy<T, C>(this IEnumerable<T> List, Func<T, C> Selector) where C : IComparable<C> {
+    public static T? MinBy<T, C>(this IEnumerable<T> List, Func<T, C> Selector) where C : IComparable<C> {
         using var enumerator = List.GetEnumerator();
         if (!enumerator.MoveNext()) { return default; }
         T minItem = enumerator.Current;
@@ -152,8 +152,9 @@ public static class EnumerableExtensions {
 
     /// <summary>
     /// 判断集合是否有且仅有一个元素。
+    /// 在输入 null 时返回 false。
     /// </summary>
-    public static bool IsSingle<T>(this IEnumerable<T> source) {
+    public static bool IsSingle<T>([AllowNull] this IEnumerable<T> source) {
         if (source is null) return false;
         if (source is IList<T> list) return list.Count == 1;
         using var enumerator = source.GetEnumerator();

+ 12 - 12
Extensions/StringExtensions.cs

@@ -7,7 +7,7 @@ public static class StringExtensions {
     /// 高速的 StartsWith。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool StartsWithF(this string value, string prefix, bool ignoreCase = false) {
+    public static bool StartsWithF([AllowNull] this string value, string prefix, bool ignoreCase = false) {
         if (value is null) return false;
         return value.StartsWith(prefix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
     }
@@ -15,7 +15,7 @@ public static class StringExtensions {
     /// 高速的 EndsWith。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool EndsWithF(this string value, string suffix, bool ignoreCase = false) {
+    public static bool EndsWithF([AllowNull] this string value, string suffix, bool ignoreCase = false) {
         if (value is null) return false;
         return value.EndsWith(suffix, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
     }
@@ -88,8 +88,8 @@ public static class StringExtensions {
     /// 获取在子字符串第一次出现之前的部分,例如对 2024/11/08 拆切 / 会得到 2024。
     /// 如果未找到子字符串则不裁切。
     /// </summary>
-    public static string BeforeFirst(this string str, string text, bool ignoreCase = false) {
-        int pos = string.IsNullOrEmpty(text) ? -1 : str.IndexOfF(text, ignoreCase);
+    public static string BeforeFirst(this string str, [AllowNull] string text, bool ignoreCase = false) {
+        int pos = string.IsNullOrEmpty(text) ? -1 : str.IndexOfF(text!, ignoreCase);
         if (pos >= 0)
             return str.Substring(0, pos);
         else
@@ -99,8 +99,8 @@ public static class StringExtensions {
     /// 获取在子字符串最后一次出现之前的部分,例如对 2024/11/08 拆切 / 会得到 2024/11。
     /// 如果未找到子字符串则不裁切。
     /// </summary>
-    public static string BeforeLast(this string str, string text, bool ignoreCase = false) {
-        int pos = string.IsNullOrEmpty(text) ? -1 : str.LastIndexOfF(text, ignoreCase);
+    public static string BeforeLast(this string str, [AllowNull] string text, bool ignoreCase = false) {
+        int pos = string.IsNullOrEmpty(text) ? -1 : str.LastIndexOfF(text!, ignoreCase);
         if (pos >= 0)
             return str.Substring(0, pos);
         else
@@ -111,10 +111,10 @@ public static class StringExtensions {
     /// 获取在子字符串第一次出现之后的部分,例如对 2024/11/08 拆切 / 会得到 11/08。
     /// 如果未找到子字符串则不裁切。
     /// </summary>
-    public static string AfterFirst(this string str, string text, bool ignoreCase = false) {
-        int pos = string.IsNullOrEmpty(text) ? -1 : str.IndexOfF(text, ignoreCase);
+    public static string AfterFirst(this string str, [AllowNull] string text, bool ignoreCase = false) {
+        int pos = string.IsNullOrEmpty(text) ? -1 : str.IndexOfF(text!, ignoreCase);
         if (pos >= 0)
-            return str.Substring(pos + text.Length);
+            return str.Substring(pos + text!.Length);
         else
             return str;
     }
@@ -122,10 +122,10 @@ public static class StringExtensions {
     /// 获取在子字符串最后一次出现之后的部分,例如对 2024/11/08 拆切 / 会得到 08。
     /// 如果未找到子字符串则不裁切。
     /// </summary>
-    public static string AfterLast(this string str, string text, bool ignoreCase = false) {
-        int pos = string.IsNullOrEmpty(text) ? -1 : str.LastIndexOfF(text, ignoreCase);
+    public static string AfterLast(this string str, [AllowNull] string text, bool ignoreCase = false) {
+        int pos = string.IsNullOrEmpty(text) ? -1 : str.LastIndexOfF(text!, ignoreCase);
         if (pos >= 0)
-            return str.Substring(pos + text.Length);
+            return str.Substring(pos + text!.Length);
         else
             return str;
     }

+ 2 - 2
General/MsDelta.cs

@@ -46,8 +46,8 @@ public static class MsDelta {
     [return: MarshalAs(UnmanagedType.Bool)]
     private static extern bool CreateDelta(
         long fileTypeSet, long setFlags, long resetFlags,
-        string sourceName, string targetName, string sourceOptionsName,
-        string targetOptionsName, DeltaInput globalOptions,
+        string sourceName, string targetName, string? sourceOptionsName,
+        string? targetOptionsName, DeltaInput globalOptions,
         IntPtr targetFileTime, uint hashAlgId, string deltaName);
     [DllImport("msdelta.dll", CharSet = CharSet.Unicode, SetLastError = true)]
     [return: MarshalAs(UnmanagedType.Bool)]

+ 14 - 14
General/OneOf.cs

@@ -2,33 +2,33 @@
 
 public readonly struct OneOf<T0, T1> {
     private readonly int _index;
-    private readonly T0 _value0;
-    private readonly T1 _value1;
+    private readonly T0? _value0;
+    private readonly T1? _value1;
     public static implicit operator OneOf<T0, T1>(T0 value) => new(0, value, default);
     public static implicit operator OneOf<T0, T1>(T1 value) => new(1, default, value);
-    private OneOf(int index, T0 value0, T1 value1) {
+    private OneOf(int index, T0? value0, T1? value1) {
         _index = index;
         _value0 = value0;
         _value1 = value1;
     }
 
     public TResult Match<TResult>(Func<T0, TResult> f0, Func<T1, TResult> f1) =>
-        _index switch {0 => f0(_value0), _ => f1(_value1)};
+        _index switch {0 => f0(_value0!), _ => f1(_value1!)};
     public void Switch(Action<T0> f0, Action<T1> f1) {
-        if (_index == 0) f0(_value0);
-        else if (_index == 1) f1(_value1);
+        if (_index == 0) f0(_value0!);
+        else if (_index == 1) f1(_value1!);
     }
 }
 
 public readonly struct OneOf<T0, T1, T2> {
     private readonly int _index;
-    private readonly T0 _value0;
-    private readonly T1 _value1;
-    private readonly T2 _value2;
+    private readonly T0? _value0;
+    private readonly T1? _value1;
+    private readonly T2? _value2;
     public static implicit operator OneOf<T0, T1, T2>(T0 value) => new(0, value, default, default);
     public static implicit operator OneOf<T0, T1, T2>(T1 value) => new(1, default, value, default);
     public static implicit operator OneOf<T0, T1, T2>(T2 value) => new(2, default, default, value);
-    private OneOf(int index, T0 value0, T1 value1, T2 value2) {
+    private OneOf(int index, T0? value0, T1? value1, T2? value2) {
         _index = index;
         _value0 = value0;
         _value1 = value1;
@@ -36,10 +36,10 @@ public readonly struct OneOf<T0, T1, T2> {
     }
 
     public TResult Match<TResult>(Func<T0, TResult> f0, Func<T1, TResult> f1, Func<T2, TResult> f2) =>
-        _index switch {0 => f0(_value0), 1 => f1(_value1), _ => f2(_value2)};
+        _index switch {0 => f0(_value0!), 1 => f1(_value1!), _ => f2(_value2!)};
     public void Switch(Action<T0> f0, Action<T1> f1, Action<T2> f2) {
-        if (_index == 0) f0(_value0);
-        else if (_index == 1) f1(_value1);
-        else if (_index == 2) f2(_value2);
+        if (_index == 0) f0(_value0!);
+        else if (_index == 1) f1(_value1!);
+        else if (_index == 2) f2(_value2!);
     }
 }

+ 9 - 0
MeloongCore.csproj

@@ -10,13 +10,22 @@
 		<Configurations>Debug;Release;Snapshot</Configurations>
 		<Deterministic>true</Deterministic>
 		<LangVersion>13.0</LangVersion>
+		<Nullable>enable</Nullable>
 	</PropertyGroup>
 
+	<ItemGroup>
+	  <PackageReference Include="Nullable" Version="1.3.1">
+	    <PrivateAssets>all</PrivateAssets>
+	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+	  </PackageReference>
+	</ItemGroup>
+
 	<ItemGroup>
 		<Using Include="System" />
 		<Using Include="System.ComponentModel" />
 		<Using Include="System.Collections" />
 		<Using Include="System.Collections.Generic" />
+		<Using Include="System.Diagnostics.CodeAnalysis" />
 		<Using Include="System.Linq" />
 		<Using Include="System.Text" />
 		<Using Include="System.IO" />