Sharp.Common
一个功能丰富的 C# 通用工具库,提供依赖注入、函数式错误处理、字符串扩展、JSON 序列化等常用功能。
特性
- ✅ 轻量级 DI 容器 - 线程安全的依赖注入容器,支持单例和瞬态生命周期,带变更通知
- ✅ Result 模式 - 函数式错误处理,避免异常,支持 LINQ 和异步操作
- ✅ 自定义异常 - 带错误码和建议的异常类型
- ✅ 字符串扩展 - URL 验证、安全类型转换等实用方法
- ✅ 日期时间扩展 - 日期格式化、时间戳转换、时区转换、工作日计算
- ✅ 加密工具 - AES/RSA加密、MD5/SHA256哈希、Base64编解码、密码哈希、HMAC签名
- ✅ 验证工具 - 邮箱、手机号、身份证、URL验证,与Result模式无缝集成
- ✅ JSON 序列化 - 预配置的 JSON 序列化,支持 camelCase 和 IPAddress
- ✅ 系统工具 - 动态端口分配、管理员权限检查
快速开始
安装
将 Common 项目引用添加到你的项目中:
<ItemGroup>
<ProjectReference Include="path\to\Sharp\Common\Common.csproj"/>
</ItemGroup>
基础使用
1. 依赖注入容器 (Global)
using Common;
// 注册单例服务
var config = new AppConfig { AppName = "MyApp" };
Global.RegisterSingleton(config);
// 获取服务
var retrievedConfig = Global.Get<AppConfig>();
// 订阅变更通知
Global.Subscribe<AppConfig>(e =>
{
Console.WriteLine($"Config changed: {e.OldValue?.AppName} -> {e.NewValue.AppName}");
});
// 更新服务(触发通知)
Global.Update(new AppConfig { AppName = "UpdatedApp" });
// 注册瞬态服务(每次获取都创建新实例)
Global.RegisterTransient(() => new RequestContext());
2. Result 模式
using Common.Results;
// 创建成功结果
var success = Result.Success(42);
// 创建失败结果
var failure = Result.Failure<int>("Something went wrong");
var failureWithCode = Result.Failure<int>(404L, "Not found");
// 使用 Try 安全执行操作
var result = Result.Try(() => int.Parse("123"));
// 函数式链式操作
var processedResult = Result.Success(10)
.Map(x => x * 2) // 转换值
.Where(x => x > 15, "Too small") // 过滤
.Bind(x => Result.Success(x.ToString()));
// 条件执行
result
.OnSuccess(value => Console.WriteLine($"Success: {value}"))
.OnFailure((ex, msg) => Console.WriteLine($"Error: {msg}"));
// LINQ 查询语法
var combined = from x in Result.Success(5)
from y in Result.Success(10)
select x + y;
3. 字符串扩展
using Common.ExpandFun;
// URL 验证
bool isValid = "https://example.com".IsValidUrl(); // true
// 安全类型转换
int port = "8080".ToInt(80); // 8080
int invalid = "abc".ToInt(80); // 80 (使用默认值)
int? nullable = "123".TryToInt(); // 123
double price = "99.99".ToDouble(); // 99.99
double? weight = "invalid".TryToDouble(); // null
4. JSON 序列化
using Common.Utils;
// 序列化(使用 camelCase)
var person = new Person { Name = "John", Age = 30 };
var json = JsonHelper.ToJson(person);
// {"name": "John", "age": 30}
// 反序列化
var loaded = JsonHelper.FromJson<Person>(json);
// 支持 IPAddress
var networkInfo = new NetworkInfo
{
ServerIp = IPAddress.Parse("192.168.1.1")
};
var jsonWithIp = JsonHelper.ToJson(networkInfo);
5. 系统工具
using Common.Utils;
// 获取可用端口
int port = SystemHelper.GetAvailablePort();
Console.WriteLine($"Server listening on port {port}");
// 检查管理员权限(仅 Windows)
if (OperatingSystem.IsWindows())
{
bool isAdmin = SystemHelper.IsAdministrator();
if (!isAdmin)
{
Console.WriteLine("Please run as administrator");
}
}
6. 加密工具
using Common.Utils;
// AES 加密/解密
var encrypted = CryptoHelper.AesEncrypt("敏感数据", "MyPassword123");
var decrypted = CryptoHelper.AesDecrypt(encrypted, "MyPassword123");
// RSA 公钥加密
var (publicKey, privateKey) = CryptoHelper.GenerateRsaKeys();
var rsaEncrypted = CryptoHelper.RsaEncrypt("Secret", publicKey);
var rsaDecrypted = CryptoHelper.RsaDecrypt(rsaEncrypted, privateKey);
// 哈希
var md5 = CryptoHelper.Md5Hash("data");
var sha256 = CryptoHelper.Sha256Hash("data");
// 密码哈希和验证(PBKDF2)
var hashedPassword = CryptoHelper.HashPassword("MySecurePassword");
bool isValid = CryptoHelper.VerifyPassword("MySecurePassword", hashedPassword);
// HMAC 签名
var signature = CryptoHelper.HmacSha256("message", "secret");
bool verified = CryptoHelper.VerifyHmacSha256("message", signature, "secret");
7. 日期时间扩展
using Common.ExpandFun;
var date = DateTime.Now;
// 日期格式化
var chinese = date.ToChineseDateString(); // "2024年01月15日"
var friendly = date.ToFriendlyString(); // "5分钟前"
var iso = date.ToIso8601String(); // "2024-01-15T12:30:45.123Z"
// 时间戳转换
long timestamp = date.ToUnixTimeSeconds();
var fromTimestamp = DateTimeExpand.FromUnixTimeSeconds(timestamp);
// 日期计算
var monthStart = date.StartOfMonth();
var monthEnd = date.EndOfMonth();
var nextWorkDay = date.AddWorkDays(5);
int workDays = date.WorkDaysBetween(date.AddDays(10));
int age = birthDate.CalculateAge();
// 时间范围判断
bool isToday = date.IsToday();
bool isWeekend = date.IsWeekend();
bool inRange = date.IsBetween(start, end);
8. 验证工具
using Common.Utils;
using Common.Results;
// 基础验证
bool isValidEmail = ValidationHelper.IsValidEmail("user@example.com");
bool isValidMobile = ValidationHelper.IsValidChinaMobile("13812345678");
bool isValidIdCard = ValidationHelper.IsValidChinaIdCard("110105199001150011");
// Result 模式集成
var emailResult = ValidationHelper.ValidateEmail("user@example.com");
if (emailResult.IsSuccess)
{
Console.WriteLine($"Valid email: {emailResult.Value}");
}
// 密码强度验证
var passwordResult = ValidationHelper.ValidatePassword(
"MyPass123!",
minLength: 8,
requireDigit: true,
requireUppercase: true,
requireSpecialChar: true
);
// 组合验证
var result = ValidationHelper.ValidateAll(
userInput,
i => ValidationHelper.ValidateNotEmpty(i, "用户名"),
i => ValidationHelper.ValidateLength(i, 3, 20, "用户名"),
i => ValidationHelper.ValidateRegex(i, "^[a-zA-Z0-9_]+$", "用户名只能包含字母、数字和下划线")
);
// 从身份证提取信息
var birthDate = ValidationHelper.ExtractBirthDateFromIdCard("110105199001150011");
var gender = ValidationHelper.ExtractGenderFromIdCard("110105199001150011");
9. 自定义异常
using Common.ExpandException;
// 创建带错误码和建议的异常
throw new ExceptionExpand(
404L,
"Resource not found",
"Check the resource ID and try again"
);
// 使用预定义异常
throw ExampleExceptionEnum.ProcessGetException;
// 在 Result 中使用
var exception = new ExceptionExpand(1001L, "Business error", "Check input");
var result = Result.Failure<string>(exception);
Console.WriteLine(result.ErrorCode); // 1001
详细文档
Global - 依赖注入容器
注册服务
// 注册单例(直接实例)
Global.RegisterSingleton(new AppConfig());
// 注册单例(工厂方法,延迟初始化)
Global.RegisterSingleton(() => new DatabaseConnection("connection-string"));
// 注册瞬态服务
Global.RegisterTransient(() => new RequestContext { Id = Guid.NewGuid() });
// 通用注册方法
Global.Register(() => new CacheService(), ServiceLifetime.Singleton);
获取服务
// 直接获取(不存在会抛异常)
var config = Global.Get<AppConfig>();
// 安全获取
if (Global.TryGet<ILogger>(out var logger))
{
logger.Log("Found logger");
}
// 获取或使用默认值
var logger = Global.GetOrDefault<ILogger>(new ConsoleLogger());
变更通知
// 订阅变更
Global.Subscribe<AppConfig>(e =>
{
Console.WriteLine($"Changed at: {e.ChangedAt}");
Console.WriteLine($"Old: {e.OldValue?.AppName}");
Console.WriteLine($"New: {e.NewValue.AppName}");
});
// 更新服务(触发通知)
Global.Update(new AppConfig { AppName = "New Version" });
// 取消订阅
Global.Unsubscribe<AppConfig>(handler);
服务管理
// 检查服务是否注册
bool exists = Global.IsRegistered<AppConfig>();
// 注销服务
Global.Unregister<AppConfig>();
// 清除所有服务
Global.Clear();
// 获取服务数量
int count = Global.Count;
Result - 函数式错误处理
创建 Result
// 成功
var success = Result.Success(); // 无返回值
var successWithValue = Result.Success(42);
// 失败
var failure1 = Result.Failure<int>("Error message");
var failure2 = Result.Failure<int>(new Exception("Error"));
var failure3 = Result.Failure<int>(404L, "Not found");
Try 模式
// 同步
var result = Result.Try(() => int.Parse("123"));
// 异步
var asyncResult = await Result.TryAsync(async () =>
{
await Task.Delay(100);
return 42;
});
函数式操作
// Map: 转换成功值
var doubled = Result.Success(10).Map(x => x * 2);
// Bind: 链式操作(返回 Result)
var chained = Result.Success(10)
.Bind(x => x > 5 ? Result.Success(x * 2) : Result.Failure<int>("Too small"));
// Where/Ensure: 过滤和验证
var filtered = Result.Success(10)
.Where(x => x > 5, "Value must be > 5")
.Ensure(x => x < 100, "Value must be < 100");
值提取
var result = Result.Success(42);
// 不同的提取方式
int? nullable = result.GetOrNullable(); // 42
int withDefault = result.GetOrDefault(0); // 42
int orThrow = result.GetOrThrow(); // 42 或抛出异常
int orElse = result.GetOrElse(() => 0); // 42 或执行函数
条件执行
result
.OnSuccess(value => Console.WriteLine($"Success: {value}"))
.OnFailure((ex, msg) => Console.WriteLine($"Error: {msg}"));
// Match 模式
var message = result.Match(
onSuccess: value => $"Success: {value}",
onFailure: (ex, msg) => $"Error: {msg}"
);
LINQ 支持
var result = from x in Result.Success(5)
from y in Result.Success(10)
where x > 0 && y > 0
select x + y;
错误码支持
var result = Result.Failure<int>(404L, "Not found");
// 检查错误码
if (result.HasErrorCode(404L))
{
Console.WriteLine("Resource not found");
}
// 获取错误码
long code = result.GetErrorCodeOrDefault(-1);
// 从 ExceptionExpand 创建
var exception = new ExceptionExpand(1001L, "Error", "Suggestion");
var fromException = ResultExtensions.FromExceptionExpand<int>(exception);
扩展方法
StringExpand
// URL 验证
"https://example.com".IsValidUrl(); // true
"not-a-url".IsValidUrl(); // false
// 整数转换
"123".ToInt(); // 123
"abc".ToInt(999); // 999 (默认值)
"456".TryToInt(); // 456?
// 浮点数转换
"3.14".ToDouble(); // 3.14
"abc".ToDouble(0.0); // 0.0 (默认值)
"2.5".TryToDouble(); // 2.5?
JsonHelper
// 序列化
var json = JsonHelper.ToJson(myObject);
// 反序列化
var obj = JsonHelper.FromJson<MyType>(json);
// 特性
// - 使用 camelCase 命名
// - 格式化输出(缩进)
// - 支持枚举转字符串
// - 支持 IPAddress 类型
SystemHelper
// 获取可用端口(用于动态端口绑定)
int port = SystemHelper.GetAvailablePort();
// 检查管理员权限(仅 Windows)
if (OperatingSystem.IsWindows())
{
bool isAdmin = SystemHelper.IsAdministrator();
}
实际应用场景
场景 1:配置热更新
// 订阅配置变更
Global.Subscribe<AppConfig>(e =>
{
Console.WriteLine($"Configuration updated: v{e.OldValue?.Version} -> v{e.NewValue.Version}");
// 重新初始化依赖配置的组件
RefreshComponents();
});
// 注册初始配置
Global.RegisterSingleton(LoadConfiguration());
// 文件监控器检测到配置文件变更
void OnConfigFileChanged()
{
var newConfig = LoadConfiguration();
Global.Update(newConfig); // 自动通知所有订阅者
}
场景 2:数据验证流水线
public Result<User> ValidateAndCreateUser(string email, string password, int age)
{
return email.AsResult()
.Ensure(e => !string.IsNullOrEmpty(e), 400L, "Email is required")
.Ensure(e => e.Contains("@"), 400L, "Invalid email format")
.Bind(e => ValidatePassword(password)
.Map(_ => e))
.Bind(e => ValidateAge(age)
.Map(_ => e))
.Map(e => new User { Email = e, Age = age });
}
private Result<string> ValidatePassword(string password)
{
return password.AsResult()
.Ensure(p => p.Length >= 8, 400L, "Password must be at least 8 characters");
}
private Result<int> ValidateAge(int age)
{
return age.AsResult()
.Ensure(a => a >= 18, 400L, "User must be at least 18 years old")
.Ensure(a => a <= 120, 400L, "Invalid age");
}
场景 3:集成测试动态端口
[TestClass]
public class IntegrationTests
{
private TestServer _server;
[TestInitialize]
public void Setup()
{
// 动态分配端口,避免端口冲突
var port = SystemHelper.GetAvailablePort();
_server = new TestServer(port);
_server.Start();
}
[TestCleanup]
public void Cleanup()
{
_server?.Stop();
}
[TestMethod]
public async Task TestApiEndpoint()
{
var client = new HttpClient();
var response = await client.GetAsync($"http://localhost:{_server.Port}/api/health");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
场景 4:API 错误处理
public async Task<Result<User>> GetUserAsync(int userId)
{
return await Result.TryAsync(async () =>
{
var response = await _httpClient.GetAsync($"/api/users/{userId}");
if (!response.IsSuccessStatusCode)
{
return response.StatusCode switch
{
HttpStatusCode.NotFound => Result.Failure<User>(404L, "User not found"),
HttpStatusCode.Unauthorized => Result.Failure<User>(401L, "Unauthorized"),
_ => Result.Failure<User>(500L, "Server error")
};
}
var json = await response.Content.ReadAsStringAsync();
var user = JsonHelper.FromJson<User>(json);
return user != null
? Result.Success(user)
: Result.Failure<User>("Failed to parse user data");
});
}
// 使用
var result = await GetUserAsync(123);
result
.OnSuccess(user => Console.WriteLine($"Found user: {user.Name}"))
.OnFailure((ex, msg, code) =>
{
if (code == 404L)
Console.WriteLine("User not found, creating new user...");
else
Console.WriteLine($"Error {code}: {msg}");
});
测试
项目包含完整的单元测试,位于 test/Common.Test 目录:
GlobalTests.cs- DI 容器测试(包含线程安全测试)ResultTests.cs- Result 模式测试(包含 LINQ 和异步测试)ExceptionExpandTests.cs- 自定义异常测试StringExpandTests.cs- 字符串扩展测试JsonHelperTests.cs- JSON 序列化测试SystemHelperTests.cs- 系统工具测试
运行测试:
cd test/Common.Test
dotnet test
最佳实践
DI 容器
-
生命周期选择
- 使用 Singleton 用于:配置对象、数据库连接池、缓存服务
- 使用 Transient 用于:请求上下文、操作上下文
-
变更通知
- 订阅者应该是轻量级的,避免长时间阻塞
- 订阅者中不要抛出异常(已被容器捕获和忽略)
-
线程安全
- 容器本身是线程安全的
- 单例服务的初始化是线程安全的(双重检查锁定)
Result 模式
-
何时使用 Result
- 可预期的业务错误(验证失败、资源不存在)
- 外部 API 调用
- 需要传递错误码的场景
-
何时使用异常
- 不可预期的程序错误(NullReferenceException、OutOfMemoryException)
- 系统级错误
-
链式操作
- 使用
Map转换成功值 - 使用
Bind链接返回 Result 的操作 - 使用
Ensure进行验证 - 失败会自动传播,不需要手动检查
- 使用
项目结构
Sharp/Common/
├── Global.cs # DI 容器
├── ExpandException/
│ ├── ExceptionExpand.cs # 自定义异常
│ └── ExampleExceptionEnum.cs # 预定义异常
├── ExpandFun/
│ └── StringExpand.cs # 字符串扩展
├── Results/
│ ├── Result.cs # Result 核心类
│ └── ResultExtensions.cs # Result 扩展方法
└── Utils/
├── JsonHelper.cs # JSON 工具
└── SystemHelper.cs # 系统工具
许可
本项目为内部库,供团队项目使用。
贡献
欢迎提交 Issue 和 Pull Request。
版本历史
v1.0.0
- ✅ 轻量级 DI 容器(支持生命周期和变更通知)
- ✅ Result 模式(完整的函数式错误处理)
- ✅ 自定义异常(带错误码)
- ✅ 字符串扩展(URL 验证、类型转换)
- ✅ JSON 序列化(camelCase、IPAddress 支持)
- ✅ 系统工具(动态端口、管理员检查)