Files
sharp-basic/src/Sharp/Common
2025-11-13 19:12:19 +08:00
..
2025-11-13 18:14:51 +08:00
2025-11-13 19:12:19 +08:00
2025-11-13 19:12:19 +08:00
2025-11-13 19:12:19 +08:00
2025-11-13 18:14:51 +08:00
2025-11-13 18:14:51 +08:00
2025-11-13 19:12:19 +08:00

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 容器

  1. 生命周期选择

    • 使用 Singleton 用于:配置对象、数据库连接池、缓存服务
    • 使用 Transient 用于:请求上下文、操作上下文
  2. 变更通知

    • 订阅者应该是轻量级的,避免长时间阻塞
    • 订阅者中不要抛出异常(已被容器捕获和忽略)
  3. 线程安全

    • 容器本身是线程安全的
    • 单例服务的初始化是线程安全的(双重检查锁定)

Result 模式

  1. 何时使用 Result

    • 可预期的业务错误(验证失败、资源不存在)
    • 外部 API 调用
    • 需要传递错误码的场景
  2. 何时使用异常

    • 不可预期的程序错误(NullReferenceException、OutOfMemoryException)
    • 系统级错误
  3. 链式操作

    • 使用 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 支持)
  • ✅ 系统工具(动态端口、管理员检查)