浏览代码

增加ConcurrentHashSet

懒得勤快 3 年之前
父节点
当前提交
e465e3db97

+ 2 - 2
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -4,7 +4,7 @@
     <LangVersion>latest</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <CodeAnalysisRuleSet />
-    <Version>2.4.6.5</Version>
+    <Version>2.4.7</Version>
     <Authors>懒得勤快</Authors>
     <Description>Masuit.Tools基础公共库,包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。</Description>
     <Copyright>懒得勤快,长空X</Copyright>
@@ -42,7 +42,7 @@
 
   <ItemGroup>
     <PackageReference Include="DnsClient" Version="1.5.0" />
-    <PackageReference Include="HtmlSanitizer" Version="6.0.441" />
+    <PackageReference Include="HtmlSanitizer" Version="7.0.473" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
     <PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
     <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="4.7.0" />

+ 378 - 0
Masuit.Tools.Abstractions/Systems/ConcurrentHashSet.cs

@@ -0,0 +1,378 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading;
+
+namespace Masuit.Tools.Systems;
+
+/// <summary>
+/// 并发HashSet
+/// </summary>
+/// <typeparam name="T"></typeparam>
+public sealed class ConcurrentHashSet<T> : ISet<T>, IDisposable
+{
+    private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
+
+    private readonly HashSet<T> _hashSet = new();
+
+    public int Count
+    {
+        get
+        {
+            _lock.EnterWriteLock();
+            try
+            {
+                return _hashSet.Count;
+            }
+            finally
+            {
+                if (_lock.IsWriteLockHeld)
+                {
+                    _lock.ExitWriteLock();
+                }
+            }
+        }
+    }
+
+    public bool IsReadOnly => false;
+
+    public ConcurrentHashSet()
+    {
+    }
+
+    public ConcurrentHashSet(IEqualityComparer<T> comparer)
+    {
+        _hashSet = new HashSet<T>(comparer);
+    }
+
+    public ConcurrentHashSet(IEnumerable<T> collection)
+    {
+        _hashSet = new HashSet<T>(collection);
+    }
+
+    public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
+    {
+        _hashSet = new HashSet<T>(collection, comparer);
+    }
+
+    public ConcurrentHashSet(SerializationInfo info, StreamingContext context)
+    {
+        _hashSet = new HashSet<T>();
+        var iSerializable = (ISerializable)_hashSet;
+        iSerializable.GetObjectData(info, context);
+    }
+
+    public void OnDeserialization(object sender)
+    {
+        _hashSet.OnDeserialization(sender);
+    }
+
+    public void GetObjectData(SerializationInfo info, StreamingContext context)
+    {
+        _hashSet.GetObjectData(info, context);
+    }
+
+    IEnumerator IEnumerable.GetEnumerator()
+    {
+        return GetEnumerator();
+    }
+
+    public IEnumerator<T> GetEnumerator()
+    {
+        return _hashSet.GetEnumerator();
+    }
+
+    public void Add(T item)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            _hashSet.Add(item);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public void UnionWith(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        _lock.EnterReadLock();
+        try
+        {
+            _hashSet.UnionWith(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+
+            if (_lock.IsReadLockHeld)
+            {
+                _lock.ExitReadLock();
+            }
+        }
+    }
+
+    public void IntersectWith(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        _lock.EnterReadLock();
+        try
+        {
+            _hashSet.IntersectWith(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+
+            if (_lock.IsReadLockHeld)
+            {
+                _lock.ExitReadLock();
+            }
+        }
+    }
+
+    public void ExceptWith(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        _lock.EnterReadLock();
+        try
+        {
+            _hashSet.ExceptWith(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+
+            if (_lock.IsReadLockHeld)
+            {
+                _lock.ExitReadLock();
+            }
+        }
+    }
+
+    public void SymmetricExceptWith(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            _hashSet.SymmetricExceptWith(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool IsSubsetOf(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.IsSubsetOf(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool IsSupersetOf(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.IsSupersetOf(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool IsProperSupersetOf(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.IsProperSupersetOf(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool IsProperSubsetOf(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.IsProperSubsetOf(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool Overlaps(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.Overlaps(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool SetEquals(IEnumerable<T> other)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.SetEquals(other);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    bool ISet<T>.Add(T item)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.Add(item);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public void Clear()
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            _hashSet.Clear();
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool Contains(T item)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.Contains(item);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public void CopyTo(T[] array, int arrayIndex)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            _hashSet.CopyTo(array, arrayIndex);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public bool Remove(T item)
+    {
+        _lock.EnterWriteLock();
+        try
+        {
+            return _hashSet.Remove(item);
+        }
+        finally
+        {
+            if (_lock.IsWriteLockHeld)
+            {
+                _lock.ExitWriteLock();
+            }
+        }
+    }
+
+    public void Dispose()
+    {
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }
+
+    private void Dispose(bool disposing)
+    {
+        if (disposing && _lock != null)
+        {
+            _lock.Dispose();
+        }
+    }
+
+    ~ConcurrentHashSet()
+    {
+        Dispose(false);
+    }
+}

+ 2 - 2
Masuit.Tools.Abstractions/Systems/ConcurrentLimitedQueue.cs

@@ -34,7 +34,7 @@ namespace Masuit.Tools.Systems
         }
 
         /// <summary>
-        /// 
+        ///
         /// </summary>
         /// <param name="list"></param>
         /// <returns></returns>
@@ -57,4 +57,4 @@ namespace Masuit.Tools.Systems
             base.Enqueue(item);
         }
     }
-}
+}

+ 2 - 2
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -18,7 +18,7 @@ github:https://github.com/ldqk/Masuit.Tools
         <UserSecretsId>830c282f-f7c1-42be-8651-4cd06ac8e73f</UserSecretsId>
         <RepositoryType>Github</RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-        <Version>2.4.6.5</Version>
+        <Version>2.4.7</Version>
         <FileVersion>2.4.5.6</FileVersion>
         <Company>masuit.com</Company>
         <AssemblyVersion>2.4.5.6</AssemblyVersion>
@@ -31,7 +31,7 @@ github:https://github.com/ldqk/Masuit.Tools
 
     <ItemGroup>
         <PackageReference Include="DnsClient" Version="1.5.0" />
-        <PackageReference Include="HtmlSanitizer" Version="6.0.453" />
+        <PackageReference Include="HtmlSanitizer" Version="7.0.473" />
         <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="4.7.0" />
         <PackageReference Include="System.Management" Version="4.7.0" />

+ 1 - 1
Masuit.Tools.Net45/package.nuspec

@@ -4,7 +4,7 @@
     <!--*-->
     <id>Masuit.Tools.Net45</id>
     <!--*-->
-    <version>2.4.6.5</version>
+    <version>2.4.7</version>
     <title>Masuit.Tools</title>
     <!--*-->
     <authors>懒得勤快</authors>

+ 1 - 1
Masuit.Tools/Masuit.Tools.csproj

@@ -166,7 +166,7 @@
       <Version>1.5.0</Version>
     </PackageReference>
     <PackageReference Include="HtmlSanitizer">
-      <Version>6.0.453</Version>
+      <Version>7.0.473</Version>
     </PackageReference>
     <PackageReference Include="Microsoft.AspNet.Mvc">
       <Version>5.2.7</Version>

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -4,7 +4,7 @@
     <!--*-->
     <id>Masuit.Tools.Net</id>
     <!--*-->
-    <version>2.4.6.5</version>
+    <version>2.4.7</version>
     <title>Masuit.Tools</title>
     <!--*-->
     <authors>懒得勤快</authors>

+ 1 - 0
NetCoreTest/Program.cs

@@ -105,6 +105,7 @@ namespace NetCoreTest
 
     public class ClassDto
     {
+        [DeserializeOnlyJsonProperty]
         public string MyProperty { get; set; }
 
         public int Num { get; set; }

+ 41 - 1
README.md

@@ -381,6 +381,12 @@ public class MyClass
 
     [ComplexPassword]//密码复杂度校验
     public string Password { get; set; }
+    
+    [EnumOf] // 检测是否是有效枚举值
+    public MyEnum MyEnum { get; set; }
+    
+    [MinItemsCount(1)] // 检测集合元素最少1个
+    public List<string> Strs { get; set; }
 }
 ```
 ### 21.HTML操作
@@ -452,12 +458,15 @@ string display = MyEnum.Read.GetDisplay();// 获取Display标签的Name属性
 var value = typeof(MyEnum).GetValue("Read");//获取字符串表示值对应的枚举值
 string enumString = 0.ToEnumString(typeof(MyEnum));// 获取枚举值对应的字符串表示
 ```
-### 26.定长队列实现
+### 26.定长队列和ConcurrentHashSet实现
 `如果是.NET5及以上,推荐使用框架自带的Channel实现该功能`
 ```csharp
 LimitedQueue<string> queue = new LimitedQueue<string>(32);// 声明一个容量为32个元素的定长队列
 ConcurrentLimitedQueue<string> queue = new ConcurrentLimitedQueue<string>(32);// 声明一个容量为32个元素的线程安全的定长队列
 ```
+```csharp
+var set = new ConcurrentHashSet<string>(); // 用法和hashset保持一致
+```
 ### 27.反射操作
 ```csharp
 MyClass myClass = new MyClass();
@@ -633,6 +642,11 @@ var stdDev=list.Select(s=>s.ConvertTo<int>()).StandardDeviation(); // 求标准
 
 var pages=queryable.ToPagedList(1,10); // 分页查询
 var pages=await queryable.ToPagedListAsync(1,10); // 分页查询
+
+var nums=Enumerable.Range(1, 10).ExceptBy(Enumerable.Range(5, 10), i => i); // 按字段取差集
+var nums=Enumerable.Range(1, 10).IntersectBy(Enumerable.Range(5, 10), i => i); // 按字段取交集
+var nums=Enumerable.Range(1, 10).SequenceEqual(Enumerable.Range(5, 10), i => i); // 判断序列相等
+var nums=Enumerable.Range(1, 10).OrderByRandom(); // 随机排序
 ```
 ### 37.Mime类型
 ```csharp
@@ -749,6 +763,32 @@ var allchanges=dbContext.GetAllChanges();//获取增删改的实体字段信息
 a.Next(func1).Next(func2).Next(func3);
 "123".Next(s=>s.ToInt32()).Next(x=>x*2).Next(x=>Math.Log(x));
 ```
+### 48.Newtonsoft.Json的只允许字段反序列化行为的契约解释器DeserializeOnlyContractResolver
+该解释器针对类属性被DeserializeOnlyJsonPropertyAttribute标记的,在反序列化的时候生效,在序列化的时候忽略
+```csharp
+public class ClassDto
+    {
+        [DeserializeOnlyJsonProperty]
+        public string MyProperty { get; set; }
+
+        public int Num { get; set; }
+    }
+    
+    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()
+	{
+		ContractResolver = new DeserializeOnlyContractResolver() // 配置使用DeserializeOnlyContractResolver解释器
+	});
+```
+如果是WebAPI全局使用:
+```csharp
+		//在Startup.ConfigureServices中
+		services.AddMvc().AddNewtonsoftJson(options =>
+             {
+                 var resolver = new DeserializeOnlyContractResolver();
+                 resolver.NamingStrategy = new CamelCaseNamingStrategy();
+                 options.SerializerSettings.ContractResolver = resolver;
+             });
+```
     
 # Asp.Net MVC和Asp.Net Core的支持断点续传和多线程下载的ResumeFileResult