龙腾猫跃 4 месяцев назад
Родитель
Сommit
3af26fbd3e

+ 6 - 0
Shared/Extensions/EnumerableExtensions.cs

@@ -328,6 +328,12 @@ public static class EnumerableExtensions {
         foreach (var item in itemsToRemove) source.Remove(item);
     }
 
+    /// <summary>
+    /// 从集合中移除指定元素。
+    /// </summary>
+    public static IEnumerable<T> Except<T>(this IEnumerable<T> source, T element)
+        => source.Except([element]);
+
     /// <summary>
     /// 判断集合是否有且仅有一个元素。
     /// 在输入 null 时返回 false。

+ 23 - 23
Shared/General/ConcurrentList.cs

@@ -11,54 +11,54 @@ public class ConcurrentList<T> : IList<T>, IList, IEnumerable, IEnumerable<T> {
     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.SyncRoot) { return new(data._items); } }
 
     // 枚举器
     /// <summary>
     /// 返回此列表的副本。
     /// </summary>
-    public IEnumerator<T> GetEnumerator() { lock (_syncLock) return _items.ToList().GetEnumerator(); }
+    public IEnumerator<T> GetEnumerator() { lock (SyncRoot) return _items.ToList().GetEnumerator(); }
     /// <summary>
     /// 返回此列表的副本。
     /// </summary>
-    IEnumerator IEnumerable.GetEnumerator() { lock (_syncLock) return _items.ToList().GetEnumerator(); }
+    IEnumerator IEnumerable.GetEnumerator() { lock (SyncRoot) return _items.ToList().GetEnumerator(); }
 
     // 成员
-    public int Count { get { lock (_syncLock) { return _items.Count; } } }
+    public int Count { get { lock (SyncRoot) { return _items.Count; } } }
     public bool IsReadOnly => false;
     bool IList.IsFixedSize => false;
     bool ICollection.IsSynchronized => true;
-    private readonly object _syncLock = new();
-    object ICollection.SyncRoot => _syncLock;
+    public readonly object SyncRoot = new();
+    object ICollection.SyncRoot => SyncRoot;
 
     public T this[int index] {
-        get { lock (_syncLock) { return _items[index]; } }
-        set { lock (_syncLock) { _items[index] = value; } }
+        get { lock (SyncRoot) { return _items[index]; } }
+        set { lock (SyncRoot) { _items[index] = value; } }
     }
     object? IList.this[int index] {
         get => this[index];
-        set { lock (_syncLock) { _items[index] = (T) value!; } }
+        set { lock (SyncRoot) { _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 (SyncRoot) { _items.Add(item); } }
+    int IList.Add(object? value) { lock (SyncRoot) { _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 (SyncRoot) { _items.Insert(index, item); } }
+    void IList.Insert(int index, object? value) { lock (SyncRoot) { _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 (SyncRoot) { return _items.Remove(item); } }
+    void IList.Remove(object? value) { lock (SyncRoot) { 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 (SyncRoot) { _items.RemoveAt(index); } }
+    public void Clear() { lock (SyncRoot) { _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 (SyncRoot) { return _items.Contains(item); } }
+    bool IList.Contains(object? value) { lock (SyncRoot) { 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 (SyncRoot) { return _items.IndexOf(item); } }
+    int IList.IndexOf(object? value) { lock (SyncRoot) { 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 (SyncRoot) { _items.CopyTo(array, arrayIndex); } }
+    void ICollection.CopyTo(Array array, int index) { lock (SyncRoot) { ((ICollection) _items).CopyTo(array, index); } }
 
 }

+ 7 - 7
Shared/Utils/DirectoryUtils.cs

@@ -36,9 +36,9 @@ public static class DirectoryUtils {
     /// 返回指定路径下的所有文件,不以 \\?\ 开头。
     /// 如果文件夹不存在,返回空列表。
     /// </summary>
-    public static IEnumerable<string> GetFiles(string path, bool topDirectoryOnly = false, string searchPattern = "*") {
+    public static IEnumerable<string> EnumerateFiles(string path, bool includeSubDirectory = false, string searchPattern = "*") {
         if (!DirectoryUtils.Exists(path)) return [];
-        return Directory.EnumerateFiles(PathUtils.ForApi(path), searchPattern, topDirectoryOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories).
+        return Directory.EnumerateFiles(PathUtils.ForApi(path), searchPattern, includeSubDirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).
             Select(PathUtils.RemoveExtendedPrefix);
     }
 
@@ -46,9 +46,9 @@ public static class DirectoryUtils {
     /// 返回指定路径下的所有文件夹,不以分隔符结尾,不以 \\?\ 开头。
     /// 如果文件夹不存在,返回空列表。
     /// </summary>
-    public static IEnumerable<string> GetDirectories(string path, bool topDirectoryOnly = false, string searchPattern = "*") {
+    public static IEnumerable<string> EnumerateDirectories(string path, bool includeSubDirectory = false, string searchPattern = "*") {
         if (!DirectoryUtils.Exists(path)) return [];
-        return Directory.EnumerateDirectories(PathUtils.ForApi(path), searchPattern, topDirectoryOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories).
+        return Directory.EnumerateDirectories(PathUtils.ForApi(path), searchPattern, includeSubDirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).
             Select(PathUtils.RemoveExtendedPrefix);
     }
 
@@ -78,7 +78,7 @@ public static class DirectoryUtils {
         } else {
             // 实际的复制
             Logger.Trace($"复制文件夹:{sourceFolder} → {destFolder}");
-            foreach (var file in DirectoryUtils.GetFiles(sourceFolder).ToList()) FileUtils.Copy(file, file.Replace(sourceFolder, destFolder));
+            foreach (var file in DirectoryUtils.EnumerateFiles(sourceFolder, includeSubDirectory: true).ToList()) FileUtils.Copy(file, file.Replace(sourceFolder, destFolder));
         }
     }
 
@@ -142,8 +142,8 @@ public static class DirectoryUtils {
         static void DeleteInternal(string folder) {
             try {
                 folder = PathUtils.ForApi(folder);
-                foreach (string filePath in DirectoryUtils.GetFiles(folder, true)) FileUtils.Delete(filePath); // 删除文件
-                foreach (string str in DirectoryUtils.GetDirectories(folder, true)) DeleteInternal(str); // 递归删除子文件夹
+                foreach (string filePath in DirectoryUtils.EnumerateFiles(folder)) FileUtils.Delete(filePath); // 删除文件
+                foreach (string str in DirectoryUtils.EnumerateDirectories(folder)) DeleteInternal(str); // 递归删除子文件夹
                 ResilientUtils.RetryOn<IOException>(() => {
                     FileUtils.SetReadOnly(folder, false);
                     Directory.Delete(folder, true);

+ 3 - 3
Tests/Utils/DirectoryUtilsTest.cs

@@ -20,7 +20,7 @@ public class DirectoryUtilsTest : TestWithFolder {
         await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "a.txt"))).IsEqualTo("aaa");
         await Assert.That(FileUtils.ReadAsString(Path.Combine(src, "sub", "b file.txt"))).IsEqualTo("bbb");
         await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "sub", "b file.txt"))).IsEqualTo("bbb");
-        await Assert.That(DirectoryUtils.GetDirectories(Path.Combine(tempFolder))).Contains(dest); // 目标文件夹名
+        await Assert.That(DirectoryUtils.EnumerateDirectories(tempFolder, true)).Contains(dest); // 目标文件夹名
     }
 
     [Test]
@@ -38,8 +38,8 @@ public class DirectoryUtilsTest : TestWithFolder {
 
         await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "a.txt"))).IsEqualTo("aaa");
         await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "sub", "b file.txt"))).IsEqualTo("bbb");
-        if (src != dest) await Assert.That(DirectoryUtils.GetDirectories(Path.Combine(tempFolder))).DoesNotContain(src);
-        await Assert.That(DirectoryUtils.GetDirectories(Path.Combine(tempFolder))).Contains(dest); // 目标文件夹名
+        if (src != dest) await Assert.That(DirectoryUtils.EnumerateDirectories(tempFolder, true)).DoesNotContain(src);
+        await Assert.That(DirectoryUtils.EnumerateDirectories(tempFolder, true)).Contains(dest); // 目标文件夹名
     }
 
     [Test]