龙腾猫跃 hai 5 meses
pai
achega
9158a69b62

+ 9 - 7
Shared/Extensions/EnumerableExtensions.cs

@@ -11,7 +11,7 @@ public static class EnumerableExtensions {
     /// 该方法的效率较低,建议仅在小型列表上使用,或换用 DistinctBy。
     /// </summary>
     public static IEnumerable<T> Distinct<T>(this IList<T> list, Func<T, T, bool> comparer) {
-        for (int i = 0; i < list.Count; i++) {
+        for (int i = list.Count - 1; i >= 0; i--) { // 反向遍历以保留最早出现的重复项
             if (!list.Skip(i + 1).Any(item => comparer(list[i], item))) yield return list[i];
         }
     }
@@ -108,13 +108,13 @@ public static class EnumerableExtensions {
     /// 选择最小值对应的对象。
     /// 若没有元素则返回 null。
     /// </summary>
-    public static TKey? MinBy<TKey, TValue>(this IEnumerable<TKey> List, Func<TKey, TValue> Selector) where TValue : IComparable<TValue> {
-        using var enumerator = List.GetEnumerator();
+    public static TKey? MinBy<TKey, TValue>(this IEnumerable<TKey> source, Func<TKey, TValue> selector) where TValue : IComparable<TValue> {
+        using var enumerator = source.GetEnumerator();
         if (!enumerator.MoveNext()) { return default; }
         TKey minItem = enumerator.Current;
-        TValue minValue = Selector(minItem);
+        TValue minValue = selector(minItem);
         while (enumerator.MoveNext()) {
-            TValue value = Selector(enumerator.Current);
+            TValue value = selector(enumerator.Current);
             if (value.CompareTo(minValue) >= 0) { continue; }
             minItem = enumerator.Current;
             minValue = value;
@@ -231,8 +231,10 @@ public static class EnumerableExtensions {
     /// 对集合的每个元素执行指定操作。
     /// </summary>
     public static IEnumerable<T> ForAll<T>(this IEnumerable<T> source, Action<T> action) {
-        foreach (T item in source) action(item);
-        return source;
+        foreach (T item in source) {
+            action(item);
+            yield return item;
+        }
     }
 
 }

+ 23 - 23
Shared/General/ConcurrentList.cs

@@ -6,59 +6,59 @@
 public class ConcurrentList<T> : IList<T>, IList, IEnumerable, IEnumerable<T> {
 
     private readonly List<T> _items = [];
-    public readonly object syncLock = new();
 
     // 构造函数
     public ConcurrentList() { }
     public ConcurrentList(IEnumerable<T> data) => _items = new(data);
     public static implicit operator ConcurrentList<T>(List<T> data) => new(data);
-    public static implicit operator List<T>(ConcurrentList<T> data) { lock (data.syncLock) { return new(data._items); } }
+    public static implicit operator List<T>(ConcurrentList<T> data) { lock (data._syncLock) { return new(data._items); } }
 
     // 枚举器
     /// <summary>
     /// 返回此列表的副本。
     /// </summary>
-    public IEnumerator<T> GetEnumerator() { lock (syncLock) return _items.ToList().GetEnumerator(); }
+    public IEnumerator<T> GetEnumerator() { lock (_syncLock) return _items.ToList().GetEnumerator(); }
     /// <summary>
     /// 返回此列表的副本。
     /// </summary>
-    IEnumerator IEnumerable.GetEnumerator() { lock (syncLock) return _items.ToList().GetEnumerator(); }
+    IEnumerator IEnumerable.GetEnumerator() { lock (_syncLock) return _items.ToList().GetEnumerator(); }
 
     // 成员
-    public int Count { get { lock (syncLock) { return _items.Count; } } }
+    public int Count { get { lock (_syncLock) { return _items.Count; } } }
     public bool IsReadOnly => false;
     bool IList.IsFixedSize => false;
     bool ICollection.IsSynchronized => true;
-    object ICollection.SyncRoot => syncLock;
+    private readonly object _syncLock = new();
+    object ICollection.SyncRoot => _syncLock;
 
     public T this[int index] {
-        get { lock (syncLock) { return _items[index]; } }
-        set { lock (syncLock) { _items[index] = value; } }
+        get { lock (_syncLock) { return _items[index]; } }
+        set { lock (_syncLock) { _items[index] = value; } }
     }
     object? IList.this[int index] {
         get => this[index];
-        set { lock (syncLock) { _items[index] = (T) value!; } }
+        set { lock (_syncLock) { _items[index] = (T) value!; } }
     }
 
-    public void Add(T item) { lock (syncLock) { _items.Add(item); } }
-    int IList.Add(object? value) { lock (syncLock) { _items.Add((T) value!); return _items.Count - 1; } }
+    public void Add(T item) { lock (_syncLock) { _items.Add(item); } }
+    int IList.Add(object? value) { lock (_syncLock) { _items.Add((T) value!); return _items.Count - 1; } }
 
-    public void Insert(int index, T item) { lock (syncLock) { _items.Insert(index, item); } }
-    void IList.Insert(int index, object? value) { lock (syncLock) { _items.Insert(index, (T) value!); } }
+    public void Insert(int index, T item) { lock (_syncLock) { _items.Insert(index, item); } }
+    void IList.Insert(int index, object? value) { lock (_syncLock) { _items.Insert(index, (T) value!); } }
 
-    public bool Remove(T item) { lock (syncLock) { return _items.Remove(item); } }
-    void IList.Remove(object? value) { lock (syncLock) { if (value is T t) _items.Remove(t); } }
+    public bool Remove(T item) { lock (_syncLock) { return _items.Remove(item); } }
+    void IList.Remove(object? value) { lock (_syncLock) { if (value is T t) _items.Remove(t); } }
 
-    public void RemoveAt(int index) { lock (syncLock) { _items.RemoveAt(index); } }
-    public void Clear() { lock (syncLock) { _items.Clear(); } }
+    public void RemoveAt(int index) { lock (_syncLock) { _items.RemoveAt(index); } }
+    public void Clear() { lock (_syncLock) { _items.Clear(); } }
 
-    public bool Contains(T item) { lock (syncLock) { return _items.Contains(item); } }
-    bool IList.Contains(object? value) { lock (syncLock) { return value is T t && _items.Contains(t); } }
+    public bool Contains(T item) { lock (_syncLock) { return _items.Contains(item); } }
+    bool IList.Contains(object? value) { lock (_syncLock) { return value is T t && _items.Contains(t); } }
 
-    public int IndexOf(T item) { lock (syncLock) { return _items.IndexOf(item); } }
-    int IList.IndexOf(object? value) { lock (syncLock) { return value is T t ? _items.IndexOf(t) : -1; } }
+    public int IndexOf(T item) { lock (_syncLock) { return _items.IndexOf(item); } }
+    int IList.IndexOf(object? value) { lock (_syncLock) { return value is T t ? _items.IndexOf(t) : -1; } }
 
-    public void CopyTo(T[] array, int arrayIndex) { lock (syncLock) { _items.CopyTo(array, arrayIndex); } }
-    void ICollection.CopyTo(Array array, int index) { lock (syncLock) { ((ICollection) _items).CopyTo(array, index); } }
+    public void CopyTo(T[] array, int arrayIndex) { lock (_syncLock) { _items.CopyTo(array, arrayIndex); } }
+    void ICollection.CopyTo(Array array, int index) { lock (_syncLock) { ((ICollection) _items).CopyTo(array, index); } }
 
 }

+ 33 - 11
Shared/General/Logger.cs

@@ -91,6 +91,20 @@ public static class Logger {
         => Log(ex, message, LogLevel.Warning, behavior, filePath);
     public static void Error(Exception ex, string? message = null, LogBehavior behavior = LogBehavior.AlertThenFeedback, [CallerFilePath] string filePath = "") 
         => Log(ex, message, LogLevel.Error, behavior, filePath);
+
+    /// <summary>
+    /// 在不调用 Logger 的情况下,将信息直接发送到输出窗口。
+    /// </summary>
+    [Conditional("DEBUG")]
+    public static void SendToDebug(string message, LogLevel level = LogLevel.Error, [CallerFilePath] string filePath = "")
+        => Debug.WriteLine($"{BaseLogger.GetLogPrefix(level, filePath)}{message}");
+    /// <summary>
+    /// 在不调用 Logger 的情况下,将信息直接发送到输出窗口。
+    /// </summary>
+    [Conditional("DEBUG")]
+    public static void SendToDebug(Exception ex, string? message = null, LogLevel level = LogLevel.Error, [CallerFilePath] string filePath = "")
+        => Debug.WriteLine($"{BaseLogger.GetLogPrefix(level, filePath)}{(message is null ? "" : $"{message}:")}{ex.GetDisplay(true)}");
+
 }
 
 /// <summary>
@@ -111,7 +125,9 @@ public class BaseLogger {
             var formattedMessage = Format(message, level, filePath, null);
             Output(formattedMessage, level);
             HandleBehavior(message, formattedMessage, behavior, null);
-        } catch {}
+        } catch (Exception logEx) {
+            Logger.SendToDebug(logEx, "打印日志失败");
+        }
     }
     public virtual void Log(Exception ex, string? message, LogLevel level, LogBehavior behavior, string filePath) {
         if (MinLevel > level) return;
@@ -121,19 +137,23 @@ public class BaseLogger {
             formattedMessage = Format(formattedMessage, level, filePath, ex);
             Output(formattedMessage, level);
             HandleBehavior(message, formattedMessage, behavior, ex);
-        } catch { }
+        } catch (Exception logEx) {
+            Logger.SendToDebug(logEx, "打印日志失败");
+        }
     }
 
     /// <summary>
     /// 格式化日志文本。
     /// </summary>
     protected virtual string Format(string text, LogLevel level, string filePath, Exception? ex) {
-        string prefix = $"{DateTime.Now:HH':'mm':'ss'.'fff} {level.ToString().First()} {(Thread.CurrentThread.Name is null ? "" : $"<{Thread.CurrentThread.Name}> ")}[{Path.GetFileName(filePath).BeforeFirst(".")}] ";
+        string prefix = GetLogPrefix(level, filePath);
         return text
             .ReplaceLineEndings("\n", mergeMultiple: true).Split(['\n'], StringSplitOptions.RemoveEmptyEntries)
             .Select(t => prefix + t)
             .Join("\r\n");
     }
+    public static string GetLogPrefix(LogLevel level, string filePath) 
+        => $"{DateTime.Now:HH':'mm':'ss'.'fff} {level.ToString().First()} {(Thread.CurrentThread.Name is null ? "" : $"<{Thread.CurrentThread.Name}> ")}[{Path.GetFileName(filePath).BeforeFirst(".")}] ";
 
     /// <summary>
     /// 输出格式化后的日志文本。
@@ -159,7 +179,7 @@ public class FileLogger : BaseLogger, IDisposable {
     /// <inheritdoc/>
     protected override void Output(string formattedText, LogLevel level) {
         base.Output(formattedText, level);
-        if (!writerAvaliable && queue.Count >= 100) return; // 在 writer 就绪前,最多缓存 100 条日志
+        if (!writerAvailable && queue.Count >= 100) return; // 在 writer 就绪前,最多缓存 100 条日志
         queue.Enqueue(formattedText);
         queuedEvent.Set();
     }
@@ -167,7 +187,7 @@ public class FileLogger : BaseLogger, IDisposable {
     private readonly ConcurrentQueue<string> queue = new();
     private readonly AutoResetEvent queuedEvent = new(false);
     private StreamWriter? writer;
-    private volatile bool writerAvaliable = false;
+    private volatile bool writerAvailable = false;
 
     /// <summary>
     /// 将日志立即写入文件。
@@ -175,10 +195,12 @@ public class FileLogger : BaseLogger, IDisposable {
     /// </summary>
     public void Flush() {
         try {
-            if (writer is null || !writerAvaliable) return;
-            lock (flushLock) 
+            if (writer is null || !writerAvailable) return;
+            lock (flushLock)
                 while (queue.TryDequeue(out string line)) writer.WriteLine(line); // writer 指定了 AutoFlush
-        } catch { }
+        } catch (Exception logEx) {
+            Logger.SendToDebug(logEx, "写入日志失败");
+        }
     }
     private readonly object flushLock = new();
 
@@ -206,7 +228,7 @@ public class FileLogger : BaseLogger, IDisposable {
             // 写入新日志文件
             try {
                 writer = new StreamWriter(PathUtils.WithLongPath(Path.Combine(logFolder, $"{fileNamePrefix}1{fileNameSuffix}")), append: true) { AutoFlush = true };
-                writerAvaliable = true;
+                writerAvailable = true;
                 Logger.Info("日志初始化成功");
                 while (true) {
                     queuedEvent.WaitOne();
@@ -214,14 +236,14 @@ public class FileLogger : BaseLogger, IDisposable {
                 }
             } catch (Exception ex) {
                 Logger.Warning(ex, "写入日志文件失败", LogBehavior.Toast);
-                writerAvaliable = false;
+                writerAvailable = false;
             }
         }) { Name = nameof(FileLogger), Priority = ThreadPriority.Lowest, IsBackground = true };
         thread.Start();
     }
 
     public void Dispose() {
-        writerAvaliable = false;
+        writerAvailable = false;
         if (writer is not null) ((IDisposable) writer).Dispose();
         queuedEvent.Dispose();
     }

+ 13 - 6
Shared/General/OneOf.cs

@@ -1,6 +1,6 @@
 namespace MeloongCore;
 public readonly struct OneOf<T0, T1> {
-    private readonly int _index;
+    private readonly int _index = -1; // -1 表示未初始化(这是一个 struct,所以这可以让默认值不为 0)
     private readonly T0? _value0;
     private readonly T1? _value1;
     public static implicit operator OneOf<T0, T1>(T0 value) => new(0, value, default);
@@ -17,7 +17,9 @@ public readonly struct OneOf<T0, T1> {
     /// 对不同类型执行不同的操作,并返回同一个值。
     /// </summary>
     public TResult Switch<TResult>(Func<T0, TResult> f0, Func<T1, TResult> f1) => _index switch {
-        0 => f0(_value0!), _ => f1(_value1!)
+        0 => f0(_value0!), 
+        1 => f1(_value1!),
+        _ => throw new InvalidOperationException()
     };
     /// <summary>
     /// 对不同类型执行不同的操作。
@@ -25,6 +27,7 @@ public readonly struct OneOf<T0, T1> {
     public void Switch(Action<T0> f0, Action<T1> f1) {
         if (_index == 0) f0(_value0!);
         else if (_index == 1) f1(_value1!);
+        else throw new InvalidOperationException();
     }
     /// <summary>
     /// 判断当前的类型是否为 T。
@@ -36,7 +39,7 @@ public readonly struct OneOf<T0, T1> {
     };
     /// <summary>
     /// 假定当前的类型为 T,并返回该值。
-    /// 若当前值的类型不为 T,则抛出 InvalidOperationException。
+    /// 若当前值的类型不为 T,则抛出 <see cref="InvalidOperationException"/>。
     /// </summary>
     public T As<T>() => _index switch {
         0 when typeof(T) == typeof(T0) => (T)(object)_value0!, 
@@ -46,7 +49,7 @@ public readonly struct OneOf<T0, T1> {
 }
 
 public readonly struct OneOf<T0, T1, T2> {
-    private readonly int _index;
+    private readonly int _index = -1; // -1 表示未初始化(这是一个 struct,所以这可以让默认值不为 0)
     private readonly T0? _value0;
     private readonly T1? _value1;
     private readonly T2? _value2;
@@ -67,7 +70,10 @@ public readonly struct OneOf<T0, T1, T2> {
     /// 对不同类型执行不同的操作,并返回同一个值。
     /// </summary>
     public TResult Switch<TResult>(Func<T0, TResult> f0, Func<T1, TResult> f1, Func<T2, TResult> f2) => _index switch {
-        0 => f0(_value0!), 1 => f1(_value1!), _ => f2(_value2!)
+        0 => f0(_value0!), 
+        1 => f1(_value1!), 
+        2 => f2(_value2!),
+        _ => throw new InvalidOperationException()
     };
     /// <summary>
     /// 对不同类型执行不同的操作。
@@ -76,6 +82,7 @@ public readonly struct OneOf<T0, T1, T2> {
         if (_index == 0) f0(_value0!);
         else if (_index == 1) f1(_value1!);
         else if (_index == 2) f2(_value2!);
+        else throw new InvalidOperationException();
     }
     /// <summary>
     /// 判断当前的类型是否为 T。
@@ -88,7 +95,7 @@ public readonly struct OneOf<T0, T1, T2> {
     };
     /// <summary>
     /// 假定当前的类型为 T,并返回该值。
-    /// 若当前值的类型不为 T,则抛出 InvalidOperationException。
+    /// 若当前值的类型不为 T,则抛出 <see cref="InvalidOperationException"/>。
     /// </summary>
     public T As<T>() => _index switch {
         0 when typeof(T) == typeof(T0) => (T) (object) _value0!,

+ 2 - 2
Shared/Utils/DirectoryUtils.cs

@@ -59,7 +59,7 @@ public static class DirectoryUtils {
     /// </summary>
     public static void Move(string sourceDirName, string destDirName) {
         if (!Exists(sourceDirName)) return;
-        Create(destDirName);
+        DirectoryUtils.Delete(destDirName); // Move 要求不存在对应文件夹
         Logger.Trace($"剪切文件夹:{sourceDirName} → {destDirName}");
         Directory.Move(PathUtils.WithLongPath(sourceDirName), PathUtils.WithLongPath(destDirName));
     }
@@ -73,7 +73,7 @@ public static class DirectoryUtils {
         if (toRecycleBin) {
             FileUtils.DeleteToRecycleBin(dirPath);
         } else {
-            Directory.Delete(PathUtils.WithLongPath(dirPath));
+            Directory.Delete(PathUtils.WithLongPath(dirPath), recursive:true);
         }
     }
 }

+ 9 - 9
Shared/Utils/EscapeUtils.cs

@@ -4,7 +4,7 @@ namespace MeloongCore;
 public static class EscapeUtils {
 
     /// <summary>
-    /// XML 转义。
+    /// WPF XML 转义。
     /// </summary>
     public static string? XmlEscape(string? str) {
         if (str is null) return null;
@@ -34,7 +34,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="Regex.Escape"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? RegexEscape(string? value) 
+    public static string? RegexEscape(string value) 
         => Regex.Escape(value);
 
     /// <summary>
@@ -42,7 +42,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="Regex.Unescape"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? RegexUnescape(string? value) 
+    public static string? RegexUnescape(string value) 
         => Regex.Unescape(value);
 
     /// <summary>
@@ -50,7 +50,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="Uri.EscapeDataString"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? UrlEscape(string? value) 
+    public static string? UrlEscape(string value) 
         => Uri.EscapeDataString(value);
 
     /// <summary>
@@ -58,7 +58,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="Uri.UnescapeDataString"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? UrlUnescape(string? value) 
+    public static string? UrlUnescape(string value) 
         => Uri.UnescapeDataString(value);
 
     /// <summary>
@@ -66,7 +66,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="WebUtility.UrlEncode"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? FormUrlEscape(string? value) 
+    public static string? FormUrlEscape(string value) 
         => WebUtility.UrlEncode(value);
 
     /// <summary>
@@ -74,7 +74,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="WebUtility.UrlDecode"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? FormUrlUnescape(string? value) 
+    public static string? FormUrlUnescape(string value) 
         => WebUtility.UrlDecode(value);
 
     /// <summary>
@@ -82,7 +82,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="WebUtility.HtmlEncode"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? HtmlEscape(string? value) 
+    public static string? HtmlEscape(string value) 
         => WebUtility.HtmlEncode(value);
 
     /// <summary>
@@ -90,7 +90,7 @@ public static class EscapeUtils {
     /// 等同于 <see cref="WebUtility.HtmlDecode"/>。
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static string? HtmlUnescape(string? value) 
+    public static string? HtmlUnescape(string value) 
         => WebUtility.HtmlDecode(value);
 
 }

+ 3 - 2
Shared/Utils/FileUtils.cs

@@ -37,7 +37,7 @@ public static class FileUtils {
     public static void Write(string filePath, Stream stream) {
         using FileStream fileStream = CreateAsStream(PathUtils.WithLongPath(filePath));
         if (stream.CanSeek && stream.Position != 0) stream.Seek(0, SeekOrigin.Begin);
-        Logger.Trace(() => $"写入文件:{filePath}({stream.GetType().Name} {stream.Length} 字节)");
+        Logger.Trace(() => $"写入文件:{filePath}({stream.GetType().Name}{(stream.CanSeek ? $" {stream.Length} 字节" : "")})");
         stream.CopyTo(fileStream);
     }
 
@@ -49,7 +49,7 @@ public static class FileUtils {
     /// 删除文件。
     /// </summary>
     public static void Delete(string filePath, bool toRecycleBin = false) {
-        if (!Exists(filePath)) return;
+        if (!FileUtils.Exists(filePath)) return;
         Logger.Trace($"{(toRecycleBin ? "将文件删除到回收站" : "删除文件")}:{filePath}");
         if (toRecycleBin) {
             DeleteToRecycleBin(filePath);
@@ -284,6 +284,7 @@ public static class FileUtils {
     public static void Move(string sourceFilePath, string destFilePath) {
         if (sourceFilePath == destFilePath) return; // 如果移动同一个文件则跳过
         DirectoryUtils.Create(destFilePath, isFilePath: true);
+        FileUtils.Delete(destFilePath);
         Logger.Trace($"剪切文件:{sourceFilePath} → {destFilePath}");
         File.Move(PathUtils.WithLongPath(sourceFilePath), PathUtils.WithLongPath(destFilePath));
     }

+ 1 - 1
Shared/Utils/HttpUtils.cs

@@ -7,7 +7,7 @@ public class HttpRequestCodeException(string message, HttpStatusCode statusCode,
     /// <summary>
     /// HTTP 请求的状态码。
     /// </summary>
-    public HttpStatusCode StatusCode = statusCode;
+    public HttpStatusCode StatusCode { get; set; } = statusCode;
     /// <summary>
     /// 远程服务器给予的回复。
     /// </summary>

+ 3 - 1
Shared/Utils/MathUtils.cs

@@ -91,10 +91,12 @@ public static class MathUtils {
         if (t > 0.9999999) return 1;
         double a = t;
         double b;
+        int maxIterations = 1000;
         do {
             b = 3 * a * ((0.33333333 + x1 - x2) * a * a + (x2 - 2 * x1) * a + x1);
             a += (t - b) * 0.5;
-        } while (!(Math.Abs(b - t) < accuracy)); // 精度
+            maxIterations--;
+        } while (!(Math.Abs(b - t) < accuracy) && maxIterations > 0);
         return 3 * a * ((0.33333333 + y1 - y2) * a * a + (y2 - 2 * y1) * a + y1);
     }
 

+ 5 - 2
Shared/Utils/PathUtils.cs

@@ -74,8 +74,11 @@ public static class PathUtils {
     /// <summary>
     /// 去除路径开头的 <c>\\?\</c>。
     /// </summary>
-    public static string WithoutLongPath(string path) 
-        => path.AfterLast(@"\\?\");
+    public static string WithoutLongPath(string path) {
+        if (path.StartsWithF(@"\\?\UNC\")) return @"\\" + path.AfterLast(@"\\?\UNC\");
+        if (path.StartsWithF(@"\\?\")) return path.AfterLast(@"\\?\");
+        return path;
+    }
 
     #endregion
 

+ 2 - 1
Wpf/Extensions/WpfExtensions.cs

@@ -3,6 +3,7 @@
 namespace MeloongCore.Wpf.Extensions;
 public static class WpfExtensions {
 
-    public static bool Any(this UIElementCollection? Arr) => Arr?.Count > 0;
+    public static bool Any(this UIElementCollection? arr) 
+        => arr?.Count > 0;
 
 }