dmfExtension.Core
.NET 工具库核心模块,对标 Java Hutool,提供字符串、集合、日期、对象、随机数、ID 生成、正则表达式、反射、枚举、DataTable、日志等日常开发常用工具方法。
目标框架
| 框架 |
版本 |
备注 |
| .NET |
8.0+ |
C# 12, 可空引用类型 |
| .NET Standard |
2.0 |
兼容 .NET Framework 4.6.1+, C# 10 |
#if NET8_0 条件编译用于在 net8.0 下提供优化实现(如 Random.Shared、System.HashCode、RandomNumberGenerator.Fill)并在 netstandard2.0 下回退到兼容实现。
项目结构
dmfExtension.Core/
├── Extensions/ # 扩展方法(链式调用)
│ ├── ObjectExtensions.cs
│ └── StringExtensions.cs
├── Utils/ # 静态工具类
│ ├── StringUtil.cs # 字符串判空/截取/格式化/命名转换/脱敏
│ ├── CollectionUtil.cs # 集合判空/创建/操作/分区/字典
│ ├── DateUtil.cs # 日期格式化/解析/运算/比较/年龄/工作日
│ ├── ObjectUtil.cs # 对象判空/比较/类型检查/转换
│ ├── RandomUtil.cs # 随机数/字符串/安全随机
│ ├── IdUtil.cs # UUID/Snowflake/ObjectId
│ ├── DataTableUtil.cs # DataTable 创建/查询/转换/合并
│ ├── LogUtil.cs # 日志(委托注入,零依赖)
│ ├── RegexUtil.cs # 正则匹配/验证/查找/替换
│ ├── ReflectUtil.cs # 反射属性/字段/方法/实例/特性
│ └── EnumUtil.cs # 枚举转换/描述/列表
├── Models/
│ ├── ConversionResult.cs # 泛型转换结果
│ ├── EnumItem.cs # 枚举项(值+名+描述+整数值)
│ └── SelectItem.cs # 下拉选择项(值+文本)
└── README.md
API 完整参考
1. StringUtil
字符串工具类,提供判空、截取、格式化、命名转换、脱敏等操作。
判空
| 方法 |
说明 |
null |
"" |
" " |
"abc" |
IsNullOrEmpty(str) |
是否为 null 或空字符串 |
true |
true |
false |
false |
IsNullOrWhiteSpace(str) |
是否为 null 或空白 |
true |
true |
true |
false |
IsNotEmpty(str) |
是否不为空 |
false |
false |
true |
true |
IsNotBlank(str) |
是否不为空白 |
false |
false |
false |
true |
截取
| 方法 |
说明 |
示例 |
Substring(str, start, length) |
安全截取(越界返回空串) |
Substring("abcdef", 1, 3) → "bcd" |
SubstringBefore(str, char) |
截取指定字符之前 |
SubstringBefore("a/b/c", '/') → "a" |
SubstringBefore(str, string) |
截取指定字符串之前 |
SubstringBefore("abc/def", "/") → "abc" |
SubstringAfter(str, char) |
截取指定字符之后 |
SubstringAfter("a/b/c", '/') → "b/c" |
SubstringAfter(str, string) |
截取指定字符串之后 |
SubstringAfter("abc/def", "/") → "def" |
SubstringBetween(str, before, after) |
截取两个字符串之间 |
SubstringBetween("{abc}", "{", "}") → "abc" |
SubstringBefore/After/Between 的 string 参数不接受 null,传入 null 抛出 ArgumentNullException。
格式化
| 方法 |
说明 |
示例 |
Format(template, args...) |
顺序替换 {0} {1} 占位符 |
Format("{0}岁,{1}", 25, "男") → "25岁,男" |
Join(separator, parts...) |
连接字符串数组 |
Join(",", "a", "b", "c") → "a,b,c" |
Join(separator, items) |
连接泛型集合 |
Join("-", new[]{1,2,3}) → "1-2-3" |
Join(separator, items, converter) |
带转换器连接 |
Join("-", list, s=>s.ToUpper()) |
Repeat(str, count) |
重复字符串 |
Repeat("abc", 3) → "abcabcabc" |
PadLeft/Right(str, width, char) |
填充对齐 |
PadLeft("abc", 5) → " abc" |
Format 方法按索引顺序替换,不是完整的 string.Format 实现。如果参数值中包含 {N} 模式,它会被替换,请注意避免。
命名转换
| 方法 |
说明 |
示例 |
ToCamelCase(str) |
驼峰 |
"HelloWorld" → "helloWorld" |
ToPascalCase(str) |
帕斯卡 |
"helloWorld" → "HelloWorld" |
ToSnakeCase(str) |
蛇形 |
"HelloWorld" → "hello_world" |
ToKebabCase(str) |
短横线 |
"HelloWorld" → "hello-world" |
FirstUpper(str) |
首字母大写 |
同 ToPascalCase |
FirstLower(str) |
首字母小写 |
同 ToCamelCase |
脱敏
| 方法 |
说明 |
示例 |
Mask(str, start, length, char) |
通用掩码 |
Mask("abcdef", 2, 3) → "ab***f" |
MaskPhone(phone) |
手机号 |
"13812345678" → "138****5678" |
MaskEmail(email) |
邮箱 |
"test@example.com" → "te***@example.com" |
MaskIdCard(idCard) |
身份证 |
"110101199001011234" → "1101**********1234" |
MaskBankCard(cardNumber) |
银行卡 |
"6222021234563456" → "6222 **** **** 3456" |
2. StringExtensions
为 StringUtil 方法提供链式调用扩展。
using dmfExtension.Core.Extensions;
"abc".IsNullOrEmpty(); // false
"HelloWorld".ToSnakeCase(); // "hello_world"
"13812345678".MaskPhone(); // "138****5678"
"test@example.com".MaskEmail(); // "te***@example.com"
3. CollectionUtil
集合工具类,提供判空、创建、分区、字典操作等。
判空与创建
| 方法 |
说明 |
IsNullOrEmpty(collection) |
判断集合是否为 null 或空 |
IsNotEmpty(collection) |
判断集合是否不为空 |
NewList(items...) |
从参数创建 List<T> |
NewSet(items...) |
从参数创建 HashSet<T> |
NewMap() |
创建空 Dictionary<K,V> |
NewMap(pairs) |
从键值对创建字典 |
AddAll(list, items...) |
批量添加元素到列表 |
集合操作
| 方法 |
说明 |
示例 |
RemoveNull(collection) |
移除所有 null 元素 |
仅支持引用类型 |
Distinct(collection) |
去重 |
保留首次出现的顺序 |
DistinctBy(collection, keySelector) |
按键去重 |
|
UnionAll(collections...) |
合并多个集合 |
惰性求值,yield return |
分区
| 方法 |
说明 |
示例 |
Partition(source, size) |
按大小分区 |
Partition([1,2,3,4,5], 2) → [[1,2],[3,4],[5]] |
PartitionBy(source, predicate) |
按谓词分区 |
PartitionBy([1,2,3,4], n=>n%2==0) → [[2,4],[1,3]] |
Partition 的 size 参数必须大于 0,否则抛出 ArgumentException。
字典操作
| 方法 |
说明 |
示例 |
GetOrDefault(dict, key, default) |
安全取值 |
键不存在返回默认值 |
GetOrCreate(dict, key, factory) |
获取或创建 |
键不存在时由 factory 创建 |
获取
| 方法 |
说明 |
FirstOrDefault(source, default) |
获取第一个元素 |
LastOrDefault(source, default) |
获取最后一个元素 |
4. DateUtil
日期时间工具类,提供格式化、解析、运算、比较、差值计算、年龄计算和工作日判断。
格式常量
| 常量 |
值 |
PATTERN_DATE |
"yyyy-MM-dd" |
PATTERN_TIME |
"HH:mm:ss" |
PATTERN_DATETIME |
"yyyy-MM-dd HH:mm:ss" |
PATTERN_DATETIME_MS |
"yyyy-MM-dd HH:mm:ss.fff" |
PATTERN_ISO |
"yyyy-MM-ddTHH:mm:ss.fffZ" |
当前时间
DateUtil.Now(); // DateTime.Now
DateUtil.Today(); // DateTime.Today
DateUtil.UtcNow(); // DateTime.UtcNow
格式化
DateUtil.Format(new DateTime(2026, 6, 29), "yyyy/MM/dd"); // "2026/06/29"
DateUtil.FormatDate(date); // "2026-06-29"
DateUtil.FormatTime(date); // "15:30:05"
DateUtil.FormatDateTime(date); // "2026-06-29 15:30:05"
DateUtil.FormatISO(date); // "2026-06-29T15:30:05.123Z"
解析
DateUtil.Parse("2026-06-29", "yyyy-MM-dd"); // DateTime(2026,6,29)
DateUtil.ParseDate("2026-06-29"); // DateTime(2026,6,29)
DateUtil.ParseDateTime("2026-06-29 15:30:00");
DateUtil.TryParse("2026-06-29", "yyyy-MM-dd", out var result); // true
解析方法对所有输入参数做非空检查,无效输入抛出 FormatException。TryParse 安全返回 bool。
日期运算
DateUtil.AddDays(date, 10); // +10 天
DateUtil.AddMonths(date, 1); // +1 月
DateUtil.AddYears(date, 1); // +1 年
DateUtil.AddHours(date, 2); // +2 小时
DateUtil.AddMinutes(date, 30); // +30 分钟
DateUtil.AddSeconds(date, 45); // +45 秒
DateUtil.StartOfDay(date); // 当天 00:00:00.000
DateUtil.EndOfDay(date); // 当天 23:59:59.999
DateUtil.StartOfMonth(date); // 当月 1 日 00:00:00.000
DateUtil.EndOfMonth(date); // 当月最后一天 23:59:59.999
DateUtil.StartOfYear(date); // 当年 1 月 1 日
DateUtil.EndOfYear(date); // 当年 12 月 31 日 23:59:59.999
所有运算保留 date.Kind 属性(Local/Utc/Unspecified)。
比较
DateUtil.Compare(d1, d2); // -1 / 0 / 1
DateUtil.IsSameDay(d1, d2); // 同一天
DateUtil.IsSameMonth(d1, d2); // 同一月
DateUtil.IsSameYear(d1, d2); // 同一年
DateUtil.IsLeapYear(2024); // true
DateUtil.DaysInMonth(2024, 2); // 29
差值计算
DateUtil.BetweenDays(start, end); // 天数差(绝对值)
DateUtil.BetweenHours(start, end); // 小时差
DateUtil.BetweenMinutes(start, end); // 分钟差
DateUtil.BetweenSeconds(start, end); // 秒差
DateUtil.BetweenMilliseconds(start, end); // 毫秒差
年龄计算
DateUtil.Age(new DateTime(1990, 1, 1)); // 基于今天
DateUtil.Age(new DateTime(1990, 1, 1), DateTime.Today);
工作日判断
DateUtil.IsWeekend(date); // 周六/日 → true
DateUtil.IsWeekday(date); // 周一至周五 → true
DateUtil.GetDayOfWeek(date); // DayOfWeek 枚举值
5. ObjectUtil
判空
| 方法 |
说明 |
IsNull(obj) |
判断对象是否为 null |
IsNotNull(obj) |
判断对象是否不为 null |
DefaultIfNull(obj, defaultValue) |
为 null 时返回默认值 |
DefaultIfNull(obj, factory) |
为 null 时通过工厂创建 |
比较与哈希
| 方法 |
说明 |
Equals(o1, o2) |
值相等比较 |
DeepEquals(o1, o2) |
递归属性比较(无循环引用保护) |
HashCode(objects...) |
多对象哈希组合 |
类型检查
| 方法 |
说明 |
IsNumber(obj) |
是否为数字类型 |
IsDateTime(obj) |
是否为 DateTime |
IsEnum(obj) |
是否为枚举 |
IsArray(obj) |
是否为数组 |
IsValueType(obj) |
是否为值类型 |
IsReferenceType(obj) |
是否为引用类型 |
转换
| 方法 |
说明 |
As(obj) |
安全转换,失败返回 default(T) |
Cast(obj) |
强制转换,失败抛 InvalidCastException |
ToString(obj) |
null → "" |
ToString(obj, nullValue) |
null → 自定义字符串 |
克隆
| 方法 |
说明 |
Clone(obj) |
要求实现 ICloneable |
DeepClone 已跳过(需要 System.Text.Json 依赖,按设计决策排除)。
6. RandomUtil
随机数工具类,提供基础随机、字符串随机和安全随机。
基础随机
| 方法 |
说明 |
RandomInt(min, max) |
[min, max) 随机整数 |
RandomLong(min, max) |
[min, max) 随机长整数 |
RandomDouble(min, max) |
[min, max) 随机双精度 |
RandomFloat(min, max) |
[min, max) 随机单精度 |
RandomBool() |
随机 boolean |
RandomItem(array) |
随机选取数组元素 |
RandomItem(enumerable) |
随机选取集合元素 |
min 必须小于 max,否则抛出 ArgumentException。
字符串随机
| 方法 |
说明 |
RandomString(length) |
随机字母数字字符串 |
RandomString(length, chars) |
从指定字符集随机 |
RandomNumber(length) |
纯数字字符串 |
RandomChinese(length) |
随机中文字符串 |
RandomChineseName() |
随机中文姓名 |
RandomEmail() |
随机邮箱地址 |
RandomPhone() |
随机手机号(中国大陆前缀) |
安全随机
| 方法 |
说明 |
SecureRandomBytes(length) |
加密级随机字节 |
SecureRandomString(length) |
加密级随机字符串(无模偏倚) |
SecureRandomBytes 使用 System.Security.Cryptography.RandomNumberGenerator。SecureRandomString 使用拒绝采样法避免模偏倚。net8.0 下使用 Random.Shared,netstandard2.0 下使用线程局部 Random。
7. IdUtil
唯一 ID 生成工具类,提供 UUID、Snowflake 和 ObjectId。
UUID
| 方法 |
说明 |
示例 |
UUID() |
标准 UUID |
"550e8400-e29b-41d4-a716-446655440000" |
SimpleUUID() |
无横线 UUID |
"550e8400e29b41d4a716446655440000" |
ShortUUID() |
8 位 Base62 短 ID |
"1aB3xY9z"(非唯一保证) |
Snowflake
| 方法 |
说明 |
SnowflakeId() |
生成 Snowflake ID (long) |
SnowflakeId(workerId, dataCenterId) |
指定配置生成(同时更新全局配置) |
SnowflakeIdString() |
字符串形式 |
ConfigureSnowflake(workerId, dataCenterId) |
预设 workerId/datacenterId |
默认 workerId=1, dataCenterId=1。workerId 范围 [0, 31],dataCenterId 范围 [0, 31]。系统时钟回拨时抛出 InvalidOperationException。
ObjectId
| 方法 |
说明 |
ObjectId() |
生成 24 位十六进制 ObjectId |
ObjectIdTimestamp(id) |
从 ObjectId 提取时间戳 |
实现格式:4 字节时间戳 + 2 字节机器 ID + 2 字节进程 ID + 3 字节自增计数器 + 1 字节填充。
8. DataTableUtil
DataTable 操作工具类。
创建
| 方法 |
说明 |
NewTable(name) |
创建 DataTable |
NewTable(name, columns...) |
从列名数组创建 |
AddColumn(table, name, type) |
添加列 |
行操作
| 方法 |
说明 |
AddRow(table, values...) |
添加行 |
RemoveRow(table, index) |
移除行 |
查询
| 方法 |
说明 |
Select(table, filter) |
按筛选表达式查询 |
FirstRow(table) |
第一行 |
LastRow(table) |
最后一行 |
FindRow(table, filter) |
匹配的第一行 |
转换
| 方法 |
说明 |
ToList(table, converter) |
转为 List |
ToDictionary(table, keySelector, valueSelector) |
转为字典 |
ToJson(table) |
转为 JSON 字符串 |
ToJson 输出所有值为字符串类型。特殊字符(" \ \n \r \t)已正确转义。
判断与合并
| 方法 |
说明 |
IsNullOrEmpty(table) |
是否为 null 或无行 |
IsNotEmpty(table) |
是否有行 |
Merge(tables...) |
合并多个 DataTable |
9. LogUtil
日志工具类,基于委托注入,零外部依赖。
级别
LogLevel.Trace < Debug < Info < Warn < Error < Fatal
配置
LogUtil.SetLogger((level, msg, ex) => { /* 自定义处理 */ });
LogUtil.SetMinLevel(LogLevel.Warn); // 只记录 Warn 及以上
LogUtil.UseConsole(); // 内置控制台输出
LogUtil.UseTrace(); // 内置 Trace 输出
日志方法
LogUtil.Trace("msg");
LogUtil.Debug("msg");
LogUtil.Info("msg");
LogUtil.Warn("msg");
LogUtil.Error("msg");
LogUtil.Error(exception); // 记录异常
LogUtil.Error(exception, "context");
LogUtil.Fatal("msg");
LogUtil.Fatal(exception, "context");
级别检查
LogUtil.IsTraceEnabled
LogUtil.IsDebugEnabled
LogUtil.IsInfoEnabled
LogUtil.IsWarnEnabled
LogUtil.IsErrorEnabled
LogUtil.IsFatalEnabled
10. RegexUtil
正则表达式工具类,提供常用验证和操作。
匹配
| 方法 |
说明 |
IsMatch(str, pattern) |
是否匹配 |
IsMatch(str, pattern, options) |
指定选项匹配 |
常用验证
| 方法 |
说明 |
IsEmail(str) |
邮箱格式 |
IsPhone(str) |
中国大陆手机号(1[3-9] 开头) |
IsIdCard(str) |
中国大陆身份证(18 位,含日期校验) |
IsPassport(str) |
护照号 |
IsIpAddress(str) |
IPv4 或 IPv6 |
IsIpV4Address(str) |
IPv4 |
IsIpV6Address(str) |
IPv6 |
IsUrl(str) |
HTTP/HTTPS URL |
IsDomain(str) |
域名 |
IsChinese(str) |
纯中文 |
IsChineseName(str) |
中文姓名(2-4 字) |
IsEnglish(str) |
纯英文字母 |
IsNumeric(str) |
数字(含小数) |
IsLetter(str) |
纯字母 |
IsAlphanumeric(str) |
字母数字 |
IsHexColor(str) |
十六进制颜色(#RRGGBB) |
IsDate(str, format) |
指定格式日期 |
IsDateTime(str, format) |
指定格式日期时间 |
所有验证方法对 null 输入返回 false。
查找与替换
| 方法 |
说明 |
Find(str, pattern) |
第一个匹配 |
Find(str, pattern, groupIndex) |
指定分组 |
FindAll(str, pattern) |
所有匹配 |
FindAll(str, pattern, groupIndex) |
所有匹配的指定分组 |
Replace(str, pattern, replacement) |
替换 |
Replace(str, pattern, evaluator) |
带评估器替换 |
转义
| 方法 |
说明 |
Escape(str) |
转义正则特殊字符 |
Unescape(str) |
反转义 |
11. ReflectUtil
反射工具类,提供属性、字段、方法、实例、特性操作。
属性操作
| 方法 |
说明 |
GetProperty(type, name) |
获取属性信息 |
GetPropertyValue(obj, name) |
获取属性值(支持嵌套 "A.B.C") |
SetPropertyValue(obj, name, value) |
设置属性值 |
GetProperties(obj) |
获取所有属性和值 |
SetProperties(obj, dict) |
批量设值 |
SetPropertyValue 对点分路径的中间节点为 null 时抛出 InvalidOperationException,对只读属性抛出 InvalidOperationException。
字段操作
| 方法 |
说明 |
GetField(type, name) |
获取字段信息 |
GetFieldValue(obj, name) |
获取字段值 |
SetFieldValue(obj, name, value) |
设置字段值 |
方法调用
| 方法 |
说明 |
InvokeMethod(obj, name, args...) |
调用实例方法 |
InvokeMethod(obj, name, types, args) |
指定参数类型调用 |
InvokeStaticMethod(type, name, args...) |
调用静态方法 |
实例创建
| 方法 |
说明 |
NewInstance(args...) |
创建泛型实例 |
NewInstance(type, args...) |
创建指定类型实例 |
类型与特性
| 方法 |
说明 |
GetType(name) |
按名称获取类型 |
HasAttribute(member, attrType) |
判断是否有特性 |
GetAttribute(member) |
获取特性实例 |
GetAttributes(member) |
获取所有特性 |
12. EnumUtil
转换
| 方法 |
说明 |
FromString(str) |
字符串解析枚举 |
FromString(str, ignoreCase) |
忽略大小写解析 |
FromInt(intValue) |
整数值转枚举 |
FromDescription(description) |
DescriptionAttribute 值转枚举 |
获取信息
| 方法 |
说明 |
GetDescription(value) |
获取 DescriptionAttribute |
GetName(value) |
获取枚举名称 |
GetValue(value) |
获取整数值 |
GetItems() |
获取所有项(含描述) |
检查
| 方法 |
说明 |
IsDefined(value) |
枚举值是否已定义 |
IsDefined(str) |
字符串是否对应已定义值 |
IsDefined(intValue) |
整数值是否对应已定义值 |
列表
| 方法 |
说明 |
GetValues() |
获取所有值 |
GetNames() |
获取所有名称 |
ToSelectItems() |
转为下拉选择列表 |
public enum Status
{
[Description("待处理")] Pending = 0,
[Description("进行中")] Processing = 1,
[Description("已完成")] Completed = 2
}
EnumUtil.GetDescription(Status.Pending); // "待处理"
EnumUtil.FromDescription<Status>("待处理"); // Status.Pending
EnumUtil.GetItems<Status>(); // [{Value=Pending, Name="Pending", Description="待处理", IntValue=0}, ...]
条件编译说明
文件使用 #if NET8_0 / #else / #endif 在以下位置处理两框架差异:
| 特性 |
net8.0 |
netstandard2.0 |
Dictionary<TKey, TValue>(IEnumerable<KeyValuePair<K,V>>) |
原生支持 |
手动循环添加 |
System.HashCode |
System.HashCode |
手动 unchecked 算术 |
Random 实例 |
Random.Shared(线程安全) |
[ThreadStatic] + 锁初始化 |
RandomNumberGenerator.Fill(byte[]) |
Fill |
rng.GetBytes |
Environment.ProcessId |
可用 |
Process.GetCurrentProcess().Id |
构建与测试
# 构建主库(双框架)
dotnet build dmfExtension.Core/dmfExtension.Core.csproj
# 运行单元测试
dotnet test dmfExtension.Core.Tests/dmfExtension.Core.Tests.csproj
# 查看测试覆盖率
dotnet test dmfExtension.Core.Tests/dmfExtension.Core.Tests.csproj --collect:"XPlat Code Coverage"
测试覆盖率
现有 272 个测试用例,覆盖全部 10 个工具类和 2 个扩展类:
| 模块 |
测试数 |
| StringUtil |
45 |
| CollectionUtil |
25 |
| DateUtil |
28 |
| ObjectUtil |
28 |
| RandomUtil |
20 |
| IdUtil |
16 |
| DataTableUtil |
23 |
| LogUtil |
10 |
| RegexUtil |
34 |
| ReflectUtil |
18 |
| EnumUtil |
18 |
| 合计 |
272 |