diff --git a/General/General.csproj b/General/General.csproj
new file mode 100644
index 0000000..ea741c5
--- /dev/null
+++ b/General/General.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net7.0
+ enable
+ enable
+ Soul2.$(MSBuildProjectName.Replace(" ", "_"))
+
+
+
diff --git a/General/utils/CollectionsUtils.cs b/General/utils/CollectionsUtils.cs
new file mode 100644
index 0000000..badad44
--- /dev/null
+++ b/General/utils/CollectionsUtils.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Soul2.General.utils
+{
+ public class CollectionsUtils
+ {
+ public static bool isNotEmpty(IEnumerable collection)
+ {
+ return !isEmpty(collection);
+ }
+
+ public static bool isEmpty(IEnumerable collection)
+ {
+ if (collection == null)
+ {
+ return true;
+ }
+ foreach (var item in collection)
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/General/utils/StringUtils.cs b/General/utils/StringUtils.cs
new file mode 100644
index 0000000..6ff2425
--- /dev/null
+++ b/General/utils/StringUtils.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Soul2.General.utils
+{
+ ///
+ /// 字符串工具类
+ /// By Soul2
+ ///
+ public static class StringUtils
+ {
+ ///
+ /// 判断空值(null或"")
+ ///
+ ///
+ ///
+ public static bool isEmpty(this string str)
+ {
+ return string.IsNullOrEmpty(str);
+ }
+ public static bool isNotEmpty(this string str)
+ {
+ return !string.IsNullOrEmpty(str);
+ }
+
+
+ ///
+ /// 判断空值(null、""或只有空格)
+ ///
+ /// 被判断的字符串
+ ///
+ public static bool isNotBlank(this string str) { return !string.IsNullOrWhiteSpace(str); }
+
+ public static bool IsBlank(this string str) { return string.IsNullOrWhiteSpace(str); }
+
+ ///
+ /// 任何一个为空
+ ///
+ ///
+ ///
+ public static bool isAnyBlank(params string[] strs)
+ {
+ foreach (var str in strs)
+ {
+ if (string.IsNullOrWhiteSpace(str))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 任何一个为空
+ /// 空格属于非空
+ ///
+ ///
+ ///
+ public static bool isAnyEmpty(params string[] strs)
+ {
+ foreach (var str in strs)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 任何一个不为空
+ ///
+ ///
+ ///
+ public static bool isNoneBlank(params string[] strs)
+ {
+ foreach (var str in strs)
+ {
+ if (string.IsNullOrWhiteSpace(str))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// 任何一个不为空
+ /// 空格属于非空
+ ///
+ ///
+ ///
+ public static bool isNoneEmpty(params string[] strs)
+ {
+ foreach (var str in strs)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// 如果不以val结尾,则添加到结尾
+ ///
+ ///
+ ///
+ public static void appendIfMissing(this string original, string str)
+ {
+ if (!original.EndsWith(str))
+ {
+ original += str;
+ }
+ }
+
+ ///
+ /// 如果不以val开头,则添加到开头
+ ///
+ ///
+ ///
+ public static void prependIfMissing(this string original, string str)
+ {
+ if (!original.StartsWith(str))
+ {
+ original = str + original;
+ }
+ }
+
+ ///
+ /// 翻转
+ /// 例:"123"->"321"
+ ///
+ ///
+ ///
+ public static string flip(this string original)
+ {
+ char[] chars = original.ToCharArray();
+ Array.Reverse(chars);
+ return new string(chars);
+ }
+
+ public static string subStrWith(this string original, int begin, int end)
+ {
+ if (begin < 0 || end > original.Length || begin > end)
+ {
+ throw new ArgumentException("Invalid input parameters");
+ }
+ return original.Substring(begin, end - begin);
+ }
+
+ }
+}
diff --git a/General/utils/TimerUtils.cs b/General/utils/TimerUtils.cs
new file mode 100644
index 0000000..f8f0459
--- /dev/null
+++ b/General/utils/TimerUtils.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Timers;
+using Timer = System.Timers.Timer;
+
+namespace Soul2.General.utils
+{
+ ///
+ /// 扩展System.Timers.Timer
+ /// By Soul2
+ ///
+ public static class TimerUtils
+ {
+ private const int default_loop_times = 512;
+
+ ///
+ /// 一次性计时器
+ /// 运行结束后自动销毁
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Timer startOnce(this Timer timer, double time, Action callback)
+ {
+ void onElapsed(object source, ElapsedEventArgs e)
+ {
+ callback(timer);
+ timer.destroy();
+ }
+ return run(timer, (int)(time * 1000), false, onElapsed);
+ }
+ public static Timer startOnce(this Timer timer, double time, Action callback)
+ {
+ return timer.startOnce(time, (t) => callback?.Invoke());
+ }
+
+ ///
+ /// 循环计时器
+ /// 可以指定运行次数
+ /// 运行结束后自动销毁
+ ///
+ /// 计时器
+ /// 时长
+ /// 次数
+ ///
+ ///
+ public static Timer startLoop(this Timer timer, double time, int times, Action callback)
+ {
+ int run_times = 0;
+ void onElapsed(object source, ElapsedEventArgs e)
+ {
+ run_times += 1;
+ if (run_times <= times)
+ {
+ callback(timer, run_times);
+ }
+ else
+ {
+ timer.destroy();
+ }
+ }
+ return run(timer, (int)(time * 1000), true, onElapsed);
+ }
+
+ public static Timer startLoop(this Timer timer, double time, int times, Action callback)
+ {
+ return timer.startLoop(time, times, (t, i) => callback?.Invoke(t));
+ }
+
+ public static Timer startLoop(this Timer timer, double time, int times, Action callback)
+ {
+ return timer.startLoop(time, times, (t, i) => callback?.Invoke(i));
+ }
+
+ public static Timer startLoop(this Timer timer, double time, int times, Action callback)
+ {
+ return timer.startLoop(time, times, (t, i) => callback?.Invoke());
+ }
+
+ ///
+ /// 循环计时器
+ /// 默认运行次数512
+ ///
+ /// 计时器
+ /// 时长
+ /// 次数
+ ///
+ ///
+ public static Timer startKeep(this Timer timer, double time, Action callback, int times = default_loop_times)
+ {
+ int run_times = 0;
+ void onElapsed(object source, ElapsedEventArgs e)
+ {
+ run_times += 1;
+ if (run_times <= times)
+ {
+ callback(timer, run_times);
+ }
+ else
+ {
+ timer.destroy();
+ }
+ }
+ return run(timer, (int)(time * 1000), true, onElapsed);
+ }
+ public static Timer startKeep(this Timer timer, double time, Action callback, int times = default_loop_times)
+ {
+ return timer.startKeep(time, (t, i) => callback?.Invoke(i));
+ }
+ public static Timer startKeep(this Timer timer, double time, Action callback, int times = default_loop_times)
+ {
+ return timer.startKeep(time, (t, i) => callback?.Invoke(t));
+ }
+ public static Timer startKeep(this Timer timer, double time, Action callback, int times = default_loop_times)
+ {
+ return timer.startKeep(time, (t, i) => callback?.Invoke());
+ }
+
+ public static void destroy(this Timer timer)
+ {
+ if (timer.Enabled)
+ {
+ timer.Stop();
+ }
+ timer.Dispose();
+ }
+
+ // -----------------------------------------
+
+ private static Timer run(Timer timer, int interval, bool loop, ElapsedEventHandler elapsed)
+ {
+ timer.Elapsed += elapsed;
+ timer.Interval = interval;
+ timer.AutoReset = loop;
+ timer.Start();
+ return timer;
+ }
+
+ public static Timer startOnce(double time, Action callback)
+ {
+ return new Timer().startOnce(time, callback);
+ }
+ public static Timer startOnce(double time, Action callback)
+ {
+ return new Timer().startOnce(time, callback);
+ }
+
+ // -----------------------------------------
+
+ public static Timer startLoop(double time, int times, Action callback)
+ {
+ return new Timer().startLoop(time, times, callback);
+ }
+ public static Timer startLoop(double time, int times, Action callback)
+ {
+ return new Timer().startLoop(time, times, callback);
+ }
+ public static Timer startLoop(double time, int times, Action callback)
+ {
+ return new Timer().startLoop(time, times, callback);
+ }
+ public static Timer startLoop(double time, int times, Action callback)
+ {
+ return new Timer().startLoop(time, times, callback);
+ }
+
+ // -----------------------------------------
+
+ public static Timer startKeep(double time, Action callback, int times = default_loop_times)
+ {
+ return new Timer().startKeep(time, callback, times);
+ }
+ public static Timer startKeep(double time, Action callback, int times = default_loop_times)
+ {
+ return new Timer().startKeep(time, callback, times);
+ }
+ public static Timer startKeep(double time, Action callback, int times = default_loop_times)
+ {
+ return new Timer().startKeep(time, callback, times);
+ }
+ public static Timer startKeep(double time, Action callback, int times = default_loop_times)
+ {
+ return new Timer().startKeep(time, callback, times);
+ }
+
+ }
+}
diff --git a/Soul2-Library.sln b/Soul2-Library.sln
new file mode 100644
index 0000000..f5640eb
--- /dev/null
+++ b/Soul2-Library.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33530.505
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "General", "General\General.csproj", "{8B24AB73-0D6F-426D-92D3-25CBE6ED718E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8B24AB73-0D6F-426D-92D3-25CBE6ED718E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B24AB73-0D6F-426D-92D3-25CBE6ED718E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B24AB73-0D6F-426D-92D3-25CBE6ED718E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B24AB73-0D6F-426D-92D3-25CBE6ED718E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {9A5A4059-DD4A-465F-9AF2-E036B3C730C6}
+ EndGlobalSection
+EndGlobal