RuoVea.ExUtil
10.0.0.4
dotnet add package RuoVea.ExUtil --version 10.0.0.4
NuGet\Install-Package RuoVea.ExUtil -Version 10.0.0.4
<PackageReference Include="RuoVea.ExUtil" Version="10.0.0.4" />
<PackageVersion Include="RuoVea.ExUtil" Version="10.0.0.4" />
<PackageReference Include="RuoVea.ExUtil" />
paket add RuoVea.ExUtil --version 10.0.0.4
#r "nuget: RuoVea.ExUtil, 10.0.0.4"
#:package RuoVea.ExUtil@10.0.0.4
#addin nuget:?package=RuoVea.ExUtil&version=10.0.0.4
#tool nuget:?package=RuoVea.ExUtil&version=10.0.0.4
🔧 RuoVea.ExUtil
通用 .NET 工具库 — 涵盖类型转换、JSON 序列化、日期格式化、数据验证、HTTP 请求、控制台输出、表达式树构建、汉字拼音、系统监控、Shell 命令等 15+ 个模块,提供丰富的
object/string扩展方法与静态工具类,并内置中、英、法、日、粤语、越南语等 7 种语言的异常提示。
📖 目录
📋 概览
RuoVea.ExUtil 为 .NET 开发者提供了日常开发所需的基础工具集,避免在类型转换、JSON 序列化、参数校验等通用逻辑上重复造轮子。
┌──────────────────────────────────────────────────────────┐
│ RuoVea.ExUtil │
├──────────────────────────────────────────────────────────┤
│ 类型转换 字符串处理 JSON 序列化 │
│ ├─ ToInt ├─ FirstUpper ├─ Json2Str │
│ ├─ ToDouble ├─ ToCamel ├─ ToObject<T> │
│ ├─ ToDecimal ├─ ContainsChinese├─ ToList<T> │
│ ├─ ToDate ├─ FilterEmoji ├─ DataTableToJson │
│ ├─ ToBool ├─ Truncate └─ LongToStringConverter│
│ └─ GetBytes └─ StrToListLong │
│ │
│ 数据验证 日期格式化 HTTP / 网络 │
│ ├─ Valid (80+) ├─ ToDateTimeS.. ├─ HttpPost / HttpGet │
│ ├─ Check (16) ├─ ToChineseDate ├─ GetLanIp │
│ └─ Attributes ├─ GetTimeStamp └─ UrlEncode │
│ └─ UnixTime │
│ │
│ 表达式树 系统监控 拼音 / 唯一标识 │
│ ├─ And / Or ├─ ExMachine ├─ PinYin │
│ ├─ True / False├─ ExShell ├─ Unique │
│ └─ Compose └─ ConsoleHelper └─ CreateNo │
├──────────────────────────────────────────────────────────┤
│ 国际化: zh-CN | zh-TW | zh-HK | en-US | fr-FR | ja-JP | vi-VN │
└──────────────────────────────────────────────────────────┘
设计原则
| 原则 | 说明 |
|---|---|
| 全静态访问 | 所有工具方法均为 static,无需实例化、无需 DI 注册 |
| 扩展方法优先 | 以 this 扩展方法为主要入口,提供流畅的链式调用体验 |
| 容错返回 | 类型转换方法在失败时返回默认值(0 / false / null),不抛异常 |
| 参数校验内置 | Check 类提供带本地化异常消息的参数校验,自动抛出 ArgumentException |
📦 安装
.NET CLI
# .NET 8.0
dotnet add package RuoVea.ExUtil --version 8.0.1.4
# .NET 10.0
dotnet add package RuoVea.ExUtil --version 10.0.0.3
Package Manager
Install-Package RuoVea.ExUtil -Version 8.0.1.4
PackageReference
<PackageReference Include="RuoVea.ExUtil" Version="8.0.1.4" />
依赖项
| 依赖包 | 版本 | 说明 |
|---|---|---|
| Newtonsoft.Json | 13.0.4 | JSON 序列化/反序列化核心依赖 |
支持的 Target Framework
| TFM | 最低版本 |
|---|---|
net8.0 |
8.0.1.4 |
net10.0 |
10.0.0.3 |
⚡ 30 秒快速开始
1. 导入命名空间
using RuoVea.ExUtil; // 扩展方法、Common、ConsoleHelper、LinqExtensions、HttpUtils
using RuoVea.ExUtil.Validate; // Valid 验证类
using RuoVea.ExUtil.ExString; // Str(拼音、唯一标识)
using RuoVea.ExUtil.Time; // UnixTime
using RuoVea.ExUtil.Machine; // ExMachine、ExShell
2. 第一行工具代码
// <inheritdoc cref="Extensions.ToInt(object)"/>
// 类型转换 — 失败返回默认值,不抛异常
int age = "25".ToInt(); // 25
int invalid = "abc".ToInt(); // 0
// <inheritdoc cref="Extensions.GetTimeStamp(DateTime)"/>
// 日期格式化 — 获取当前时间戳
long timestamp = DateTime.Now.GetTimeStamp();
// <inheritdoc cref="Valid.IsEmail(string, bool)"/>
// 数据验证 — 邮箱格式校验
bool isEmail = Valid.IsEmail("test@example.com"); // true
3. 使用扩展方法(更流畅)
// <inheritdoc cref="Extensions.ToDateTimeString(DateTime, bool)"/>
string now = DateTime.Now.ToDateTimeString(); // "2024-01-01 12:30:45"
// <inheritdoc cref="Extensions.ContainsChinese(string)"/>
bool hasCn = "Hello中国".ContainsChinese(); // true
// <inheritdoc cref="Extensions.ToObject{T}(string)"/>
var user = "{\"Name\":\"张三\"}".ToObject<User>();
// <inheritdoc cref="Str.PinYin(string)"/>
string py = Str.PinYin("中国"); // "zg"
30 秒内你完成了: 类型安全转换 → 时间戳获取 → 邮箱格式校验 → 日期格式化 → 中文检测 → JSON 反序列化 → 拼音转换。
🧩 核心场景
场景一:类型转换(Convert 扩展)
┌──────────┐ ToInt() / ToDouble() / ToDate() ┌──────────────┐
│ object │ ────────────────────────────────────▶ │ 目标类型 │
│ (任意输入) │ 失败时返回默认值,不抛异常 │ (int/double…) │
└──────────┘ └──────────────┘
用例: 处理外部数据源(HTTP 参数、数据库结果、CSV)的类型安全转换
数值转换
// <inheritdoc cref="Extensions.ToInt(object)"/>
int i1 = "123".ToInt(); // 123
int i2 = "12.8".ToInt(); // 13(先转 double 再取整)
int i3 = "abc".ToInt(); // 0(容错返回默认值)
int? ni = "".ToIntOrNull(); // null
// <inheritdoc cref="Extensions.ToDouble(object)"/>
double d1 = "3.14".ToDouble(); // 3.14
double d2 = "3.14159".ToDouble(2); // 3.14(四舍五入到指定小数位)
// <inheritdoc cref="Extensions.ToDecimal(object)"/>
decimal m1 = "99.99".ToDecimal(); // 99.99
decimal? mn = "".ToDecimalOrNull(); // null
日期与布尔转换
// <inheritdoc cref="Extensions.ToDate(object)"/>
DateTime dt1 = "2024-01-01".ToDate(); // 2024-01-01
DateTime dt2 = "20240101".ToDate(); // 支持 yyyyMMdd 格式
DateTime dt3 = "invalid".ToDate(); // DateTime.MinValue
DateTime? dtN = "".ToDateOrNull(); // null
// <inheritdoc cref="Extensions.ToBool(object)"/>
bool b1 = "true".ToBool(); // true
bool b2 = "是".ToBool(); // true(支持中文)
bool b3 = "0".ToBool(); // false
bool b4 = "yes".ToBool(); // true(支持英文)
字典与字节转换
// <inheritdoc cref="Extensions.ToQueryString(Dictionary{string, string}, bool)"/>
var dict = new Dictionary<string, string> { ["name"] = "张三", ["age"] = "25" };
string qs = dict.ToQueryString(); // "name=%E5%BC%A0%E4%B8%89&age=25"
// <inheritdoc cref="Extensions.GetBytes(string)"/>
byte[] bytes = "Hello".GetBytes(); // UTF-8 编码
// <inheritdoc cref="Extensions.UrlEncode(string)"/>
string encoded = "张三".UrlEncode(); // "%E5%BC%A0%E4%B8%89"
❗ 注意事项:
ToDate在转换失败时返回DateTime.MinValue而非null,如需可空判断请使用ToDateOrNull。
场景二:JSON 序列化与反序列化
┌──────────┐ Json2Str() / Serialize() ┌──────────────┐
│ 对象 │ ──────────────────────────▶ │ JSON 字符串 │
│ / 字典 │ │ │
└──────────┘ └──────┬───────┘
│
ToObject<T>() / ToList<T>() / ToJObject()
│
▼
┌──────────────┐
│ 强类型对象 │
└──────────────┘
// <inheritdoc cref="Extensions.Json2Str(object, bool)"/>
var obj = new { Name = "张三", Age = 25 };
string json = obj.Json2Str(); // {"Name":"张三","Age":25}(默认 yyyy-MM-dd HH:mm:ss)
// <inheritdoc cref="Extensions.Json2Str(object, string, bool)"/>
string json2 = obj.Json2Str("yyyyMMdd"); // 自定义日期格式
// <inheritdoc cref="Extensions.ToObject{T}(string)"/>
var user = "{\"Name\":\"李四\",\"Age\":30}".ToObject<User>();
// <inheritdoc cref="Extensions.ToList{T}(string)"/>
var list = "[{\"Name\":\"A\"},{\"Name\":\"B\"}]".ToList<User>();
// <inheritdoc cref="Extensions.ToJObject(string)"/>
JObject jo = "{\"key\":\"value\"}".ToJObject();
// <inheritdoc cref="Extensions.DataTableToJson(DataTable)"/>
string dtJson = dataTable.DataTableToJson();
// <inheritdoc cref="Extensions.ReaderJson(IDataReader)"/>
string drJson = dataReader.ReaderJson();
// <inheritdoc cref="Extensions.Serialize(object, JsonSerializerSettings)"/>
string serialized = obj.Serialize(); // 使用 Newtonsoft 标准序列化
Long 类型 JS 安全转换
// <inheritdoc cref="LongToStringConverter"/>
// 防止 JavaScript 大整数精度丢失(JS 安全整数仅 53 位)
// 配置示例:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new LongToStringConverter());
string safeJson = JsonConvert.SerializeObject(new { Id = 1234567890123456789L }, settings);
// → {"Id":"1234567890123456789"} // 以字符串形式输出
标记为 [Obsolete] 的方法
| 旧方法 | 推荐替代 | 说明 |
|---|---|---|
obj.ToJson() |
obj.ObjToJsonString() |
反射手动序列化,已废弃 |
array.ToJson() |
array.EnumerableToJson() |
集合手动序列化,已废弃 |
dataSet.ToJson() |
dataSet.DataSetToJson() |
DataSet 序列化,已废弃 |
dt.ToJson() |
dt.DataTableToJson() |
DataTable 序列化,已废弃 |
场景三:数据验证(Valid / Check)
┌──────────┐ Valid.IsXxx() / Check.IsXxx() ┌──────────────┐
│ 输入值 │ ───────────────────────────────▶ │ bool / 异常 │
│ string │ │ │
└──────────┘ └──────────────┘
用例: 表单校验、API 参数校验、数据入库前验证
Valid 静态验证类(80+ 方法)
// <inheritdoc cref="Valid.IsEmail(string, bool)"/>
bool isEmail = Valid.IsEmail("test@example.com"); // true
bool existsEmail = Valid.HasEmail("联系: test@example.com"); // true
// <inheritdoc cref="Valid.IsPhoneNumber(string)"/>
bool isPhone = Valid.IsPhoneNumber("13800138000"); // true
// <inheritdoc cref="Valid.IsIdCard(string)"/>
bool isId = Valid.IsIdCard("110101199001011234"); // true
// <inheritdoc cref="Valid.IsGuid(string)"/>
bool isGuid = Valid.IsGuid(Guid.NewGuid().ToString()); // true
// <inheritdoc cref="Valid.IsUrl(string)"/>
bool isUrl = Valid.IsUrl("https://example.com"); // true
// <inheritdoc cref="Valid.IsBase64String(string)"/>
bool isB64 = Valid.IsBase64String("SGVsbG8="); // true
// <inheritdoc cref="Valid.IsDomain(string)"/>
bool isDomain = Valid.IsDomain("example.com"); // true
// <inheritdoc cref="Valid.IsMac(string)"/>
bool isMac = Valid.IsMac("00-11-22-33-44-55"); // true
密码强度验证:
// <inheritdoc cref="Valid.IsPasswordOne(string)"/>
Valid.IsPasswordOne("Abc@123"); // 6-25 位含特殊字符
// <inheritdoc cref="Valid.IsPassworld(object, int, int)"/>
Valid.IsPassworld("StrongP@ss1", 8, 32);
// <inheritdoc cref="Valid.IsSuperPassworld(object, int, int)"/>
Valid.IsSuperPassworld("SuperStr0ng!", 8, 32); // 更高强度密码
中文/字符验证:
Valid.IsChineseWord("中文"); // 是否中文词组
Valid.IsContainsChinese("Hello中国"); // 是否包含中文
Valid.IsOnlyChinese("中国"); // 是否纯中文
Valid.IsContainsNumber("abc123"); // 是否包含数字
Valid.IsOnlyNumber("12345"); // 是否纯数字
Valid.IsSzzmChinese("张三"); // 数字/字母/中文
SQL 注入防护:
// <inheritdoc cref="Valid.IsBadString(string)"/>
bool safe = !Valid.IsBadString(userInput); // 检测 SQL 注入关键字
// 检测: select, insert, delete, from, drop, update, truncate, exec, xp_ 等
Valid 方法速查表
| 分类 | 方法 |
|---|---|
| 邮箱/手机 | IsEmail, HasEmail, IsQqEmail, IsPhoneNumber, IsMobileNumber, IsPhone, IsMobile |
| 身份/银行卡 | IsIdCard, IsBandCard |
| 密码 | IsPasswordOne, IsPasswordTwo, IsPassworld, IsSuperPassworld |
| 网络 | IsUrl, IsUri, IsDomain, IsMainDomainUrl, IsIpAddress, IsMac |
| 账号/版本 | IsLoginName, IsVersion |
| 中文/字符 | IsChinese, IsChineseWord, IsOnlyChinese, IsContainsChinese, IsContainsNumber, IsOnlyNumber, IsNumber, IsInteger, IsUnicode, IsWideWord, IsNarrowWord, IsUpperCaseChar, IsLowerCaseChar, IsNormalChar |
| 数值 | IsPositiveInteger, IsInt32, IsDouble, IsDecimal, IsNumber |
| 格式 | IsBase64String, IsGuid, IsColorValue, IsTime, IsDate, IsDateTime, IsLengthStr |
| 业务 | IsChinesePostalCode, IsPostCode, IsQQ, IsWeChartNumber, IsFax, IsSafeSqlString, IsBadString, IsRepeat, IsPostfix, IsSzzm, IsSzzmChinese, IsNzx |
Check 参数校验类(16 个方法 — 抛出本地化异常)
// <inheritdoc cref="Check.IsNotEmpty(Guid, string)"/>
Check.IsNotEmpty(userId, nameof(userId)); // 空 Guid → ArgumentException
// <inheritdoc cref="Check.IsNotEmpty(string, string)"/>
Check.IsNotEmpty(name, nameof(name)); // 空字符串 → ArgumentException
// <inheritdoc cref="Check.IsNotNull(object, string, string)"/>
Check.IsNotNull(dependency, nameof(dependency)); // null → ArgumentNullException
// <inheritdoc cref="Check.IsNotOutOfRange(int, int, int, string)"/>
Check.IsNotOutOfRange(age, 0, 150, nameof(age)); // 超出范围 → ArgumentOutOfRangeException
// <inheritdoc cref="Check.IsNotInPast(DateTime, string)"/>
Check.IsNotInPast(deliveryDate, nameof(deliveryDate));
// <inheritdoc cref="Check.IsNotExistsFile(string, string)"/>
Check.IsNotExistsFile(filePath, nameof(filePath)); // 文件不存在 → ArgumentException
// <inheritdoc cref="Check.IsNotNegativeOrZero(int, string)"/>
Check.IsNotNegativeOrZero(pageSize, nameof(pageSize));
⚠️ 注意事项:
Valid返回bool,不抛异常;Check不满足时抛出带本地化消息的ArgumentException。两者各司其职:Valid用于表单验证,Check用于 API 前置参数校验。
场景四:日期时间格式化
┌────────────┐ ToDateTimeString() / ToChineseDateString() ┌──────────────┐
│ DateTime │ ──────────────────────────────────────────────▶ │ 格式化字符串 │
└────────────┘ └──────────────┘
用例: 日志时间戳、报表日期展示、国际化日期显示
// <inheritdoc cref="Extensions.ToDateTimeString(DateTime, bool)"/>
DateTime.Now.ToDateTimeString(); // "2024-01-01 12:30:45"
DateTime.Now.ToDateTimeString(true); // "2024-01-01 12:30"(移除秒)
// <inheritdoc cref="Extensions.ToDateString(DateTime)"/>
DateTime.Now.ToDateString(); // "2024-01-01"
// <inheritdoc cref="Extensions.ToTimeString(DateTime)"/>
DateTime.Now.ToTimeString(); // "12:30:45"
// <inheritdoc cref="Extensions.ToMillisecondString(DateTime)"/>
DateTime.Now.ToMillisecondString(); // "2024-01-01 12:30:45.123"
// <inheritdoc cref="Extensions.ToChineseDateString(DateTime)"/>
DateTime.Now.ToChineseDateString(); // "2024年1月1日"
// <inheritdoc cref="Extensions.ToChineseDateTimeString(DateTime, bool)"/>
DateTime.Now.ToChineseDateTimeString(); // "2024年1月1日 12时30分45秒"
DateTime.Now.ToChineseDateTimeString(true); // "2024年1月1日 12时30分"(移除秒)
// <inheritdoc cref="Extensions.GetTimeStamp(DateTime)"/>
long ts = DateTime.Now.GetTimeStamp(); // Unix 时间戳(秒)
// <inheritdoc cref="Extensions.GetTimeSpmpToDate(object)"/>
DateTime dt = 1704067200L.GetTimeSpmpToDate(); // 时间戳 → 本地 DateTime
DateTimeOffset 互转
// <inheritdoc cref="Extensions.ConvertToDateTime(DateTimeOffset)"/>
DateTime local = dateTimeOffset.ConvertToDateTime();
// <inheritdoc cref="Extensions.ConvertToDateTimeOffset(DateTime)"/>
DateTimeOffset dto = DateTime.Now.ConvertToDateTimeOffset();
UnixTime(自定义 Unix 时间戳处理)
// <inheritdoc cref="UnixTime.Ticks()"/>
long ticks = UnixTime.Ticks(); // 当前 UTC+8 区 Unix 时间戳(秒)
// <inheritdoc cref="UnixTime.ToDateTime(long)"/>
DateTime dt = UnixTime.ToDateTime(1704067200); // Unix 时间戳 → DateTime
// <inheritdoc cref="UnixTime.ToInt64(DateTime)"/>
long ts = UnixTime.ToInt64(DateTime.Now); // DateTime → Unix 时间戳(秒)
⚠️ 已知缺陷:
UnixTime.Ticks()硬编码- 8 * 60 * 60,仅适用于 UTC+8(中国时区)场景,非东八区环境需自行处理。
场景五:字符串处理与中文检测
┌──────────┐ FirstUpper / ToCamel / Truncate ┌──────────────┐
│ string │ ──────────────────────────────────▶ │ 处理后的字符串 │
└──────────┘ └──────────────┘
用例: 命名转换、HTML 解析、内容安全检测、列表拼接
大小写与命名转换
// <inheritdoc cref="Extensions.FirstUpper(string)"/>
"hello".FirstUpper(); // "Hello"
// <inheritdoc cref="Extensions.FirstLower(string)"/>
"Hello".FirstLower(); // "hello"
// <inheritdoc cref="Extensions.ToCamel(string, bool, bool)"/>
"hello_world".ToCamel(); // "HelloWorld"(首字母大写驼峰)
"hello_world".ToCamel(firstCharacterUpper: false); // "helloWorld"(小驼峰)
// <inheritdoc cref="Extensions.ToCamelAny(string, bool, bool, bool)"/>
"hello_world".ToCamelAny(); // "HelloWorld"(去除下划线后驼峰)
中文与内容检测
// <inheritdoc cref="Extensions.ContainsChinese(string)"/>
"Hello中国".ContainsChinese(); // true
// <inheritdoc cref="Extensions.ContainsNumber(string)"/>
"abc123".ContainsNumber(); // true
// <inheritdoc cref="Extensions.FilterEmoji(string)"/>
string clean = "Hello😀World".FilterEmoji(); // "HelloWorld"
正则匹配与列表操作
// <inheritdoc cref="Extensions.IsMatch(string, string)"/>
bool match = "abc123".IsMatch(@"\d+"); // true
// <inheritdoc cref="Extensions.Splice{T}(IEnumerable{T}, string, string)"/>
new[] { "a", "b", "c" }.Splice(separator: ","); // "a,b,c"
new[] { "a", "b", "c" }.Splice(quotes: "'"); // "'a','b','c'"
// <inheritdoc cref="Extensions.StrToListString(string)"/>
"a,b,c".StrToListString(); // List<string> { "a", "b", "c" }
// <inheritdoc cref="Extensions.StrToListLong(string)"/>
"1,2,3".StrToListLong(); // List<long> { 1, 2, 3 }
// <inheritdoc cref="Extensions.Truncate(string, int, int, string)"/>
"很长的文本内容".Truncate(5, endChar: "..."); // "很长的文..."
// <inheritdoc cref="Extensions.Distinct(string)"/>
"aabbbcc".Distinct(); // "abc"
// <inheritdoc cref="Extensions.GetHtmlImageUrlList(string)"/>
string[] imgs = html.GetHtmlImageUrlList(); // 提取 HTML 中所有 <img> 的 src
时间范围分割
// <inheritdoc cref="Extensions.Splitting(string, char)"/>
var (begin, end) = "2024-01-01/2024-01-31".Splitting('/');
// begin = "2024-01-01", end = "2024-01-31"
场景六:HTTP 请求
┌──────────┐ HttpPost() / HttpGet() ┌──────────────┐
│ Client │ ──────────────────────────▶ │ Response │
│ (string) │ 同步/异步,支持自定义 Header │ (string) │
└──────────┘ └──────────────┘
用例: 调用第三方 API、WebHook 回调、数据抓取
// <inheritdoc cref="HttpUtils.HttpGet(string, Dictionary{string, string})"/>
string html = HttpUtils.HttpGet("https://api.example.com/data");
// <inheritdoc cref="HttpUtils.HttpGetAsync(string, Dictionary{string, string})"/>
string asyncHtml = await HttpUtils.HttpGetAsync("https://api.example.com/data");
// <inheritdoc cref="HttpUtils.HttpPost(string, string, string, int, Dictionary{string, string})"/>
string resp = HttpUtils.HttpPost(
url: "https://api.example.com/submit",
postData: "{\"key\":\"value\"}",
contentType: "application/json",
timeOut: 30,
headers: new Dictionary<string, string> { ["Authorization"] = "Bearer xxx" }
);
// <inheritdoc cref="HttpUtils.HttpPostAsync(string, string, string, int, Dictionary{string, string})"/>
string asyncResp = await HttpUtils.HttpPostAsync(
"https://api.example.com/submit",
"{\"key\":\"value\"}",
"application/json"
);
⚠️ 已知缺陷: 每次调用都新建
HttpClient实例,高并发场景下可能导致 Socket 端口耗尽(TIME_WAIT)。生产环境建议使用IHttpClientFactory管理 HttpClient 生命周期,或引入单例模式的HttpClient。
场景七:表达式树动态查询
┌──────────┐ And() / Or() / Compose() ┌──────────────────┐
│ 表达式1 │ ─────────────────────────▶ │ 组合后的表达式 │
│ 表达式2 │ 表达式树动态拼接 │ Expression<T> │
└──────────┘ └──────────────────┘
用例: 动态 LINQ 查询条件拼接、多条件筛选、仓储模式查询
// <inheritdoc cref="LinqExtensions.True{T}"/>
Expression<Func<User, bool>> where = LinqExtensions.True<User>();
// <inheritdoc cref="LinqExtensions.And{T}"/>
if (!string.IsNullOrEmpty(name))
where = where.And(u => u.Name.Contains(name));
if (age > 0)
where = where.And(u => u.Age >= age);
// <inheritdoc cref="LinqExtensions.Or{T}"/>
where = where.Or(u => u.IsVip); // 或条件
// 最终查询
var users = dbContext.Users.Where(where).ToList();
表达式构建器辅助方法:
// <inheritdoc cref="LinqExtensions.Property(Expression, string)"/>
var propExpr = Expression.Parameter(typeof(User), "u")
.Property("Name"); // 等价于 u.Name
// <inheritdoc cref="LinqExtensions.GreaterThan(Expression, Expression)"/>
var gtExpr = leftExpr.GreaterThan(rightExpr); // 等价于 left > right
// <inheritdoc cref="LinqExtensions.Call(Expression, string, Expression[])"/>
var methodExpr = instance.Call("ToString"); // 等价于 instance.ToString()
// <inheritdoc cref="LinqExtensions.ToLambda{T}(Expression, ParameterExpression[])"/>
var lambda = body.ToLambda<Func<User, bool>>(param);
场景八:树形结构构建
┌──────────────┐ TreeBuildUtil<T>.Build() ┌──────────────────┐
│ 扁平列表 │ ────────────────────────────▶ │ 树形层级结构 │
│ List<T> │ GetId() / GetPid() 排序 │ List<T> (带子节点) │
└──────────────┘ └──────────────────┘
用例: 菜单树、组织架构、分类导航、地区级联
// <inheritdoc cref="ITreeNode"/>
// 实体类必须实现 ITreeNode 接口
public class MenuDto : ITreeNode
{
public long Id { get; set; }
public long ParentId { get; set; }
public string Name { get; set; }
public long GetId() => Id;
public long GetPid() => ParentId;
public void SetChildren(IList children) => Children = children?.Cast<MenuDto>().ToList();
public List<MenuDto> Children { get; set; }
}
// <inheritdoc cref="TreeBuildUtil{T}.Build(List{T})"/>
var flatList = new List<MenuDto>
{
new() { Id = 1, ParentId = 0, Name = "系统管理" },
new() { Id = 2, ParentId = 1, Name = "用户管理" },
new() { Id = 3, ParentId = 1, Name = "角色管理" },
new() { Id = 4, ParentId = 0, Name = "业务管理" },
};
var tree = new TreeBuildUtil<MenuDto>().Build(flatList);
// 返回两个根节点(Id=1 和 Id=4),各自包含子节点
场景九:系统监控
┌────────────┐ GetMachineUseInfo() ┌──────────────────┐
│ 操作系统 │ ──────────────────────▶ │ CPU / RAM / 磁盘 │
│ (跨平台) │ GetMachineBaseInfo() │ 网络 / 运行时间 │
└────────────┘ └──────────────────┘
用例: 服务器健康检查、运维监控面板、诊断信息收集
// <inheritdoc cref="ExMachine.GetInstance()"/>
var machine = ExMachine.GetInstance();
// <inheritdoc cref="ExMachine.GetMachineUseInfo()"/>
dynamic usage = machine.GetMachineUseInfo();
// usage.TotalRam → "16 GB"
// usage.RamRate → 45
// usage.CpuRate → 23
// usage.DiskRate → 67
// usage.RunTime → "01 天 12 小时 30 分 45 秒"
// <inheritdoc cref="ExMachine.GetMachineBaseInfo()"/>
dynamic baseInfo = machine.GetMachineBaseInfo();
// 返回 List<dynamic>:
// - HostName, MemTotal, SystemOs, OsArchitecture, ProcessorCount, Is64BitProcess
// <inheritdoc cref="ExMachine.GetCpuRate()"/>
string cpuRate = machine.GetCpuRate(); // Windows: wmic, Linux: top, Mac: top
// <inheritdoc cref="ExMachine.GetRamInfo()"/>
dynamic ram = machine.GetRamInfo(); // Total, Used, Free
Shell 命令执行
// <inheritdoc cref="ExShell.Bash(string)"/>
string result = ExShell.Bash("ls -la"); // Linux/macOS
// <inheritdoc cref="ExShell.Cmd(string, string)"/>
string winResult = ExShell.Cmd("ipconfig", ""); // Windows
⚠️ 已知缺陷:
ExShell可能存在命令注入风险,请做好输入校验,避免直接拼接用户输入到命令参数。
场景十:汉字拼音与唯一标识
┌──────────┐ PinYin(text) ┌──────────┐
│ 汉字 │ ───────────────▶ │ 拼音简码 │
│ "中国" │ │ "zg" │
└──────────┘ └──────────┘
┌──────────┐ Unique() ┌──────────────────────────────┐
│ Guid │ ─────────────────▶ │ 无连字符的 32 位字符串 │
│ NewGuid │ │ "a1b2c3d4e5f6..." │
└──────────┘ └──────────────────────────────┘
// <inheritdoc cref="Str.PinYin(string)"/>
Str.PinYin("中国"); // "zg"
Str.PinYin("中国人"); // "zgr"
Str.PinYin("Hello"); // "hello"(非汉字原样返回)
// <inheritdoc cref="Str.Unique()"/>
string uid = Str.Unique(); // "a1b2c3d4e5f67890abcdef1234567890"
// <inheritdoc cref="Str.ResolvePinYinByFile(string)"/>
// 基于 Const.ChinesePinYin 文件查找拼音(48KB 中文映射表)
string py = Str.ResolvePinYinByFile("中"); // "z"
⚠️ 注意事项:
Str.PinYin返回拼音首字母小写,内部使用Encoding.Default处理字节码,在非 Windows 平台上可能因系统编码差异导致拼音解析结果不同。
Common 基础工具
// <inheritdoc cref="Common.TimerStart()"/>
var watch = Common.TimerStart();
// ... 执行操作 ...
double ms = Common.TimerEnd(watch); // 返回耗时(毫秒)
// <inheritdoc cref="Common.CreateNo(string)"/>
string no = Common.CreateNo(); // 自动生成编号(基于日期+随机数)
// 格式示例: "0612451236202410241234"(自定义 MMHHmmyyyyssdd 格式)
// <inheritdoc cref="Common.RndSalt(int)"/>
string salt = Common.RndSalt(16); // 生成 16 位随机盐(大小写字母+数字)
// <inheritdoc cref="Common.RndNum(int)"/>
string rnd = Common.RndNum(6); // 生成 6 位随机数字(0-9)
已修复:
Common.CreateNo已移除Thread.Sleep(100),改用共享static Random+lock保护,锁持有时间从约 100ms 降至 < 0.01ms。
⚙️ 模块速查表
| 模块 | 类/文件 | 命名空间 | 入口方式 | 主要功能 |
|---|---|---|---|---|
| 类型转换 | Extensions.Convert.cs |
RuoVea.ExUtil |
object.ToInt() 等 |
数值/日期/布尔/字节/字典转换 |
| 日期时间 | Extensions.DateTime.cs |
RuoVea.ExUtil |
DateTime.ToDateTimeString() 等 |
格式化/时间戳/DateTimeOffset 互转 |
| JSON 处理 | Extensions.Json.cs |
RuoVea.ExUtil |
string.ToObject<T>() 等 |
序列化/反序列化/DataTable/DataSet |
| Nullable | Extensions.Nullable.cs |
RuoVea.ExUtil |
T.SafeValue() 等 |
安全取值/去重/截断/列表转字符串 |
| Object | Extensions.Object.cs |
RuoVea.ExUtil |
obj.ChangeType<T>() |
DateOnly/TimeOnly 兼容/反射类型转换 |
| 字符串 | Extensions.String.cs |
RuoVea.ExUtil |
string.FirstUpper() 等 |
大小写/驼峰/中文检测/HTML 解析/Emoji |
| 验证 | Extensions.Validate.cs |
RuoVea.ExUtil |
obj.IsEmpty() 等 |
空值检查/参数校验扩展 |
| 数据验证 | Valid.cs |
RuoVea.ExUtil.Validate |
Valid.IsEmail() |
80+ 个静态验证方法 |
| 参数校验 | Check.cs |
RuoVea.ExUtil |
Check.IsNotEmpty() |
16 个带本地化异常的校验方法 |
| 验证特性 | Attributes/ |
RuoVea.ExUtil |
[Email] 等 |
14 个数据注解验证特性 |
| 表达式树 | LinqExtensions.cs |
RuoVea.ExUtil |
LinqExtensions.True<T>() |
动态 LINQ 查询/条件组合 |
| HTTP 请求 | HttpUtils.cs |
RuoVea.ExUtil |
HttpUtils.HttpGet() |
同步/异步 GET/POST |
| 枚举扩展 | EnumExtension.cs |
RuoVea.ExUtil |
Enum.GetDic<TEnum>() |
枚举字典/文本(TextAttribute) |
| 控制台输出 | ConsoleHelper.cs |
RuoVea.ExUtil |
ConsoleHelper.WriteColorLine() |
ANSI 彩色输出/线程安全 |
| 树形结构 | TreeBuildUtil.cs |
RuoVea.ExUtil |
new TreeBuildUtil<T>().Build() |
扁平列表转层级树 |
| Long 转换 | LongToStringConverter.cs |
RuoVea.ExUtil |
配置 JsonSerializerSettings |
JS 安全 Long 序列化 |
| 系统监控 | ExMachine.cs |
RuoVea.ExUtil.Machine |
ExMachine.GetInstance() |
CPU/RAM/磁盘/网络/运行时间 |
| Shell 命令 | ExShell.cs |
RuoVea.ExUtil.Machine |
ExShell.Bash() / ExShell.Cmd() |
跨平台命令执行 |
| 时间工具 | Time.cs |
RuoVea.ExUtil |
静态方法 | 中文星期/ISO 周/日期差计算 |
| Unix 时间 | UnixTime.cs |
RuoVea.ExUtil.Time |
UnixTime.Ticks() |
自定义 Unix 时间戳互转 |
| 字符串工具 | Str.cs |
RuoVea.ExUtil.ExString |
Str.PinYin() / Str.Unique() |
拼音/唯一标识/上传路径分配 |
| 常量 | Const.cs |
RuoVea.ExUtil.ExString |
Const.ChinesePinYin |
48KB 中文拼音映射表/字母/数字 |
| 公共方法 | Common.cs |
RuoVea.ExUtil |
Common.TimerStart() |
计时器/编号生成/随机数 |
| 公共工具 | CommonUtils.cs |
RuoVea.ExUtil |
CommonUtils.AssigendPath() |
上传路径/文件类型判断/局域网 IP |
| XML 操作 | Xml.cs |
RuoVea.ExUtil |
Xml.WriteCsproj() |
.csproj 文件条目写入 |
| 异常类 | Exceptions/ |
RuoVea.ExUtil |
new ConnctionException() |
自定义异常: Connction/Paramiter/ExceptionEx/Fmsg |
| 国际化 | Language/ |
RuoVea.ExUtil.Language |
i18n.ResourceManager |
7 语言资源文件 |
🛡️ 错误处理与日志
类型转换容错设计
类型转换方法在失败时不抛异常,返回安全默认值:
// <inheritdoc>
int value = "invalid".ToInt(); // 返回 0,不抛异常
double d = "".ToDouble(); // 返回 0
DateTime dt = "bad".ToDate(); // 返回 DateTime.MinValue
bool b = "maybe".ToBool(); // 返回 false
// 如需区分"转换失败"与"值为零",使用可空版本
int? ni = "invalid".ToIntOrNull(); // 返回 null
Check 异常校验
Check 类在校验不通过时抛出带本地化消息的异常:
// <inheritdoc>
public void CreateUser(string name, Guid userId, int age)
{
Check.IsNotEmpty(name, nameof(name));
// → ArgumentException: "name 字段不能为空" 或 "The name field cannot be empty"
Check.IsNotEmpty(userId, nameof(userId));
// → ArgumentException: "userId 必须是有效的 GUID"
Check.IsNotOutOfRange(age, 0, 150, nameof(age));
// → ArgumentOutOfRangeException: "age 取值范围为 0 到 150"
}
推荐模式:Valid + Check 分层校验
// <inheritdoc>
public class UserService
{
/// <summary>
/// Valid: 表单场景 — 收集所有校验失败信息
/// </summary>
public List<string> ValidateForm(UserDto dto)
{
var errors = new List<string>();
if (!Valid.IsEmail(dto.Email))
errors.Add("邮箱格式不正确");
if (!Valid.IsPhoneNumber(dto.Phone))
errors.Add("手机号格式不正确");
return errors;
}
/// <summary>
/// Check: API 场景 — 快速失败,抛异常
/// </summary>
public void CreateUser(UserDto dto)
{
Check.IsNotEmpty(dto.Name, nameof(dto.Name));
Check.IsNotNull(dto, nameof(dto));
// ... 业务逻辑
}
}
🧵 线程安全
| 组件 | 线程安全 | 说明 |
|---|---|---|
Extensions (类型转换) |
✅ 是 | 纯计算,无共享状态 |
Extensions (JSON) |
✅ 是 | 每次调用不维护静态缓存 |
Valid |
✅ 是 | 纯静态方法,无共享可变状态 |
Check |
✅ 是 | 仅抛出异常,无共享状态 |
ConsoleHelper |
✅ 是 | 内部使用 lock 保护 Console.WriteLine |
LinqExtensions |
✅ 是 | 表达式树构建为纯函数 |
HttpUtils |
⚠️ 否 | 每次新建 HttpClient,资源方面不安全 |
Common |
⚠️ 否 | CreateNo 使用 lock 但含 Thread.Sleep,_sn 静态计数 |
Str |
✅ 是 | 纯计算(含 Encoding.Default 潜在编码差异) |
UnixTime |
✅ 是 | 纯计算 |
ExMachine |
✅ 是 | 单例,但监控数据为瞬时值 |
TreeBuildUtil<T> |
✅ 是 | 实例方法,无共享状态 |
i18n |
✅ 是 | ResourceManager 线程安全 |
⚠️ 已知缺陷与注意事项
本节记录包中的已知 Bug 和设计陷阱,建议在代码审查和集成时重点关注。
1. ICCareAttribute — 身份证验证使用了邮箱正则(Bug)
文件: Validate/Attributes/ICCareAttribute.cs
标签: [ICCare](标注为"身份证"验证)
实际: 构造函数中使用了 EMAIL 正则表达式
// ❌ 标注为身份证验证,实际校验的却是邮箱格式
[ICCare]
public string IdCard { get; set; } // 实际会校验邮箱格式!
影响: 使用
[ICCare]特性校验身份证号将错误地应用邮箱正则。如需身份证验证请直接使用Valid.IsIdCard(string)。
2. EmailAttribute — 忽略自定义错误消息参数
文件: Validate/Attributes/EmailAttribute.cs
构造参数: msg(自定义错误消息)
实际: 构造函数接收 msg 参数后未使用,始终返回硬编码的英文错误消息
3. HttpUtils — 已修复为共享静态 HttpClient
文件: HttpUtils.cs
状态: ✅ 已修复 — 使用共享静态 HttpClient + HttpRequestMessage 模式
说明: 不再每次请求新建 HttpClient,避免高并发下 Socket 端口耗尽
4. ConsoleHelper — 始终输出 ANSI 转义码
文件: ConsoleHelper.cs
风险: 在不支持 ANSI 的终端(旧版 Windows 控制台)中显示乱码
建议: 检测 Console.IsOutputRedirected 或使用 VirtualTerminalProcessing
5. UnixTime.Ticks() — 硬编码 UTC+8 偏移
文件: Time/UnixTime.cs
Ticks() 方法: return ... - 8 * 60 * 60
影响: 非东八区环境获取的时间戳不正确
建议: 使用 DateTimeOffset.UtcNow.ToUnixTimeSeconds() 替代
6. Common.CreateNo — 已优化锁持有时间
文件: Common/Common.cs
状态: ✅ 已修复 — 移除 Thread.Sleep(100),使用共享 static Random
效果: 锁持有时间从 ~100ms 降至 <0.01ms,吞吐量提升 10000x
### 7. Valid.IsColorValue — 长度判断笔误
```text
文件: Validate/Valid.cs
代码: value.Length != 33 // ❌ 应为 != 3(十六进制颜色值为 3 或 6 位)
影响: 导致 3 位颜色值(如 "#FFF")校验永远失败
8. Encoding.Default 跨平台不一致
文件: Str.cs (PinYin), Extensions.DateTime.cs
问题: Encoding.Default 在 Windows 上是 GBK/code-page,Linux 上是 UTF-8
影响: 汉字拼音解析在不同操作系统上结果可能不同
9. Extensions.Json — [Obsolete] 方法仍被引用
多个标记为 [Obsolete] 的方法(ToJson, ListToJson 等)使用了手写的字符串拼接 JSON 逻辑,存在转义不完整、性能差的隐患。新代码请使用 ObjToJsonString、EnumerableToJson、DataTableToJson 等替代方法。
🌍 国际化
Check 类和验证特性中的错误消息支持以下语言,根据 CultureInfo.CurrentUICulture 自动切换:
| 语言 | 区域代码 |
|---|---|
| 简体中文 | zh-CN |
| 繁体中文(台湾) | zh-TW |
| 粤语(香港) | zh-HK |
| 英语 | en-US |
| 法语 | fr-FR |
| 日语 | ja-JP |
| 越南语 | vi-VN |
// 当前线程 UI 文化为 fr-FR 时:
Check.IsNotEmpty("", "UserName");
// → ArgumentException: "Le champ UserName ne peut pas être vide."
📄 License
MIT License © RuoVea
🔗 相关资源: NuGet Gallery · 问题反馈
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Newtonsoft.Json (>= 13.0.4)
NuGet packages (7)
Showing the top 5 NuGet packages that depend on RuoVea.ExUtil:
| Package | Downloads |
|---|---|
|
RuoVea.ExSugar
Sqlsugar扩展 快速注入,支持简体中文、繁体中文、粤语、日语、法语、英语.使用方式:service.AddSqlsugar();继承RestFulLog 重写异常日志,操作日志,差异日志 |
|
|
RuoVea.ExFilter
注入 进行全局的异常日志收集、执行操作日志、参数验证,支持简体中文、繁体中文、粤语、日语、法语、英语.services.ExceptionSetup();// 注入 全局错误日志处 services.ExceptionSetup(ExceptionLog actionOptions);// 注入 全局错误日志处 services.ExceptionSetup(builder.Configuration.GetSection("AopOption:ExceptionLog"));// 注入 全局错误日志处 services.RequestActionSetup();// 注入 请求日志拦截 [执行操作日志、参数验证 ] services.RequestActionSetup(RequestLog actionOptions);// 注入 请求日志拦截 [执行操作日志、参数验证 ] services.RequestActionSetup(builder.Configuration.GetSection("AopOption:RequestLog"));// 注入 请求日志拦截 [执行操作日志、参数验证 ] services.ResourceSetup();//对资源型信息进行过滤 services.ResultSetup();//对结果进行统一 services.ApISafeSetup(AppSign actionOptions);//接口安全校验 services.ApISafeSetup(builder.Configuration.GetSection("AopOption:AppSign"));//接口安全校验 services.ApISignSetup(AppSign actionOptions);//签名验证 ( appKey + signKey + timeStamp + data ); services.ApISignSetup(builder.Configuration.GetSection("AopOption:AppSign"));//签名验证 ( appKey + signKey + timeStamp + data ); services.AddValidateSetup();//模型校验 services.AddUiFilesZipSetup();//将前端UI压缩文件进行解压 不进行接口安全校验 -> NonAplSafeAttribute 不签名验证 -> NonAplSignAttribute 不进行全局的异常日志收集 -> NonExceptionAttribute 不对资源型信息进行过滤 -> NonResourceAttribute 不对结果进行统一 -> NonRestfulResultAttribute |
|
|
RuoVea.ExWeb
CorsUrls、IPLimit、SafeIps、Jwt 配置 |
|
|
RuoVea.ExGlobal
web 注入 全局错误日志、操作日志记录 |
|
|
PBKDF2.Key
PBKDF2(Password-Based Key Derivation Function)是一个用来导出密钥的函数,常用于生成加密的密码。它的基本原理是通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥。如果重复的次数足够大,破解的成本就会变得很高。而盐值的添加也会增加“彩虹表”攻击的难度。 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.0.4 | 268 | 6/24/2026 |
| 10.0.0.3 | 542 | 5/28/2026 |
| 10.0.0.2 | 614 | 1/27/2026 |
| 9.0.0.2 | 1,263 | 1/27/2026 |
| 9.0.0.1 | 153 | 1/26/2026 |
| 8.0.1.5 | 414 | 6/24/2026 |
| 8.0.1.4 | 1,000 | 5/28/2026 |
| 8.0.1.3 | 1,634 | 1/27/2026 |
| 8.0.1.2 | 297 | 1/26/2026 |
| 7.0.1.4 | 642 | 5/28/2026 |
| 7.0.1.3 | 1,779 | 1/27/2026 |
| 7.0.1.2 | 189 | 1/26/2026 |
| 6.0.14.4 | 749 | 5/28/2026 |
| 6.0.14.3 | 2,157 | 1/27/2026 |
| 6.0.14.2 | 173 | 1/26/2026 |
| 5.0.20.4 | 162 | 5/28/2026 |
| 5.0.20.3 | 251 | 1/27/2026 |
| 5.0.20.2 | 189 | 1/26/2026 |
| 2.1.2.4 | 123 | 1/27/2026 |
| 2.0.0.3 | 124 | 1/27/2026 |