2025-12-08 14:05:44 +08:00
2025-12-08 14:05:44 +08:00
2025-11-18 11:14:56 +08:00
2025-11-12 08:06:00 +08:00
2025-11-14 10:18:31 +08:00

sharp-basic

C# 公共基础核心(模块化、多项目可复用)

sharp-basic 旨在将常用基础能力拆分为可独立复用的模块(库项目),帮助业务项目即取即用,减少样板代码与重复实现。

当前仓库采用多项目结构,包含 Common、Config 等核心模块,以及 Logging、EventBus、NetClient/NetServer 等待实现或扩展的模块,同时提供 WPF/Client 示例项目便于演示与调试。

功能概览

  • 通用工具(Sharp.Common)
    • 结果类型 Result/Result:统一成功/失败表达、错误码/异常、Map/Bind/LINQ 风格链式操作、同步/异步 Try 包装
    • 轻量服务容器 Global:线程安全注册与获取(Singleton/Transient)、实例变更通知(订阅发布)、批量管理
    • 实用工具与扩展:JSON 序列化选项与 IPAddress 转换、端口探测、管理员权限检测、字符串解析等
  • 配置中心(Sharp.Config)
    • 统一构建器:支持 Json/Ini/Xml/Toml 多源叠加、可选/必选、基路径/文件提供器、环境变量、热重载
    • 强类型绑定与验证:DataAnnotations 递归验证、TryGet/异常详情、对象保存与读取(多格式序列化)
    • 扩展钩子:自定义 IConfigurationBuilder 管道(ConfigureBuilderHook)
  • 日志(Sharp.Log)【规划中】
    • 基于 Microsoft.Extensions.Logging 的统一抽象,适配 Console/File/Serilog/NLog,结构化日志与上下文
  • 事件总线(Sharp.EventBus)【规划中】
    • 进程内发布订阅、异步/顺序/粘性事件,支持 WPF 弱订阅;外部 Broker(Redis/RabbitMQ/Kafka)适配扩展
  • 网络(Sharp.NetClient / Sharp.NetServer)【规划中】
    • HttpClientFactory 封装、重试熔断超时、请求签名、统一 Result 返回;最小 API Host 与中间件
  • 桌面工具(Sharp.Wpf / Sharp.Client)
    • WPF 示例/样板,用于演示配置热更、事件订阅、日志等集成的参考

目录结构

src/Sharp/
  Common/       # 通用能力(Result、Global、Utils、扩展)
  Config/       # 配置集中管理与序列化
  Log/          # 日志抽象(规划中)
  EventBus/     # 事件总线(规划中)
  NetClient/    # 客户端网络(规划中)
  NetServer/    # 服务端网络(规划中)
  Wpf/          # WPF 示例
  Client/       # WPF 示例(另一个样板)
test/
  Common.Test/  # Common 核心用例(MSTest)

目标框架:.NET 8(net8.0 / net8.0-windows)。

快速开始

当前未发布 NuGet 包,可在解决方案内直接引用相关项目,或将模块作为子模块引入你的解决方案。

使用 Result 统一返回

using Common.Results;

Result<int> r1 = Result.Success(123);
Result r2 = Result.Failure("something wrong");

// Try 模式(同步/异步)
var r3 = Result.Try(() => int.Parse("456"));
var r4 = await Result.TryAsync(async () => { await Task.Delay(10); return 789; });

// Map/Bind 链式管道
var r5 = r3.Map(x => x * 2).Where(v => v > 500, "too small");

// 提取值(或异常)
int value = r5.GetOrDefault(0);            // 成功取值,否则默认
int must  = r5.GetOrThrow();               // 失败抛出异常

使用 Global 注册与订阅

using Common;

// 注册单例(实例/工厂),与瞬态
Global.RegisterSingleton(new AppConfig { AppName = "MyApp", Version = "1.0" });
Global.RegisterSingleton(() => new CacheService());
Global.RegisterTransient(() => new RequestContext());

// 订阅服务变更
Global.Subscribe<AppConfig>(e =>
{
    Console.WriteLine($"Config changed: {e.OldValue?.Version} -> {e.NewValue.Version}");
});

// 更新将触发订阅回调
Global.Update(new AppConfig { AppName = "MyApp", Version = "1.1" });

// 获取
var config = Global.Get<AppConfig>();

示例类型(自定义)

public sealed class AppConfig { public string AppName { get; set; } = ""; public string Version { get; set; } = ""; }
public sealed class CacheService { }
public sealed class RequestContext { public string RequestId { get; set; } = Guid.NewGuid().ToString(); }

配置管理(多源叠加/强类型/验证/保存)

using Sharp.Config;

// 1) 从多个文件构建(支持 Json/Ini/Xml/Toml),并启用环境变量与热重载
var cfg = ConfigBuilder.Build(opt =>
{
    opt.BasePath = AppContext.BaseDirectory;
    opt.ReloadOnChange = true;
    opt.IncludeEnvironmentVariables = true;
    opt.EnvironmentPrefix = "MYAPP_"; // 可选
    opt.Sources.Add(new ConfigSource { Path = "appsettings.json", Format = ConfigFormat.Json, Optional = false });
    opt.Sources.Add(new ConfigSource { Path = "appsettings.Development.json", Format = ConfigFormat.Json, Optional = true });
});

// 2) 强类型绑定 + 验证(DataAnnotations)
var options = cfg.Get<MyOptions>(validate: true);

// 3) 热重载回调
using var _ = cfg.OnReload(() => Console.WriteLine("Configuration reloaded"));

// 4) 保存/读取对象(跨格式序列化)
cfg.Save(options, path: "out/appsettings.toml", format: ConfigFormat.Toml);
var reloaded = cfg.LoadFromFile<MyOptions>("out/appsettings.toml", ConfigFormat.Toml);

public sealed class MyOptions
{
    [System.ComponentModel.DataAnnotations.Required]
    public string Name { get; set; } = string.Empty;
    public int Port { get; set; } = 8080;
}

示例 appsettings.json

{
  "name": "demo",
  "port": 5000
}

测试

  • 测试框架:MSTest(并行执行)。
  • 运行:
dotnet test

核心用例位于 test/Common.Test,覆盖 Result 与 Global 的主要功能与边界场景。

WPF 工具包(Sharp.Wpf)

提供即插即用的 WPF 常用基础能力:

  • 全局异常处理:统一捕获 UI/后台/未观察任务异常,支持弹窗与自定义日志
  • MVVM 基础:ViewModelBase(INPC/IsBusy)、RelayCommand、AsyncCommand
  • Dispatcher 封装:IDispatcher/DispatcherService,提供 UI 线程调用统一入口
  • 对话与文件对话:IDialogService、IFileDialogService
  • 常用转换器:Bool/NullOrEmpty 可见性、布尔取反、枚举描述
  • 附加行为:EventToCommand(事件转 ICommand)
  • 窗口工具:WindowPlacement 窗口位置/大小/状态持久化

引用

在你的 WPF 项目中添加项目引用:

<!-- Client/Client.csproj -->
<ItemGroup>
  <ProjectReference Include="..\Wpf\Wpf.csproj" />
  <!-- 或通过 NuGet: <PackageReference Include="Sharp.Wpf" Version="x.y.z" /> -->
  
</ItemGroup>

全局异常处理(App 启动)

// App.Startup.cs
using System.Windows;
using Sharp.Wpf.Exceptions;

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        new UiExceptionHandler().Register(new ExceptionHandlingOptions
        {
            ShowDialog = true,
            DialogTitle = "应用程序错误"
        });
    }
}

XAML 集成(窗口位置、行为、转换器)

<Window x:Class="Client.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:b="clr-namespace:Sharp.Wpf.Behaviors;assembly=Sharp.Wpf"
        xmlns:w="clr-namespace:Sharp.Wpf.Windowing;assembly=Sharp.Wpf"
        xmlns:conv="clr-namespace:Sharp.Wpf.Converters;assembly=Sharp.Wpf"
        Title="MainWindow"
        w:WindowPlacement.RememberPlacement="True"
        w:WindowPlacement.PlacementId="MainWindow">
  <Window.Resources>
    <conv:BoolToVisibilityConverter x:Key="Bool2Vis" />
  </Window.Resources>
  <StackPanel Margin="16">
    <Button Content="点击弹窗"
            b:EventToCommand.EventName="Click"
            b:EventToCommand.Command="{Binding ShowDialogCommand}" />
    <Button Content="触发异常"
            b:EventToCommand.EventName="Click"
            b:EventToCommand.Command="{Binding ThrowCommand}" />
    <TextBlock Text="正在处理..."
               Visibility="{Binding IsBusy, Converter={StaticResource Bool2Vis}}"/>
  </StackPanel>
</Window>

ViewModel 与命令

using System;
using System.Threading.Tasks;
using Sharp.Wpf.Commands;
using Sharp.Wpf.Mvvm;
using Sharp.Wpf.Services;

public sealed class MainViewModel : ViewModelBase
{
    private readonly IDialogService _dialog = new DialogService();

    public string Greeting { get; set; } = "欢迎使用 Sharp.Wpf";
    public IAsyncCommand ShowDialogCommand { get; }
    public RelayCommand ThrowCommand { get; }

    public MainViewModel()
    {
        ShowDialogCommand = new AsyncCommand(async () =>
        {
            try { IsBusy = true; await Task.Delay(300); await _dialog.ShowInfoAsync("Hello!"); }
            finally { IsBusy = false; }
        });

        ThrowCommand = new RelayCommand(() => throw new InvalidOperationException("演示未处理异常"));
    }
}

文件对话

var files = await new FileDialogService().OpenFilesAsync("Images|*.png;*.jpg");
var folder = await new FileDialogService().SelectFolderAsync();

路线图(Roadmap)

  • P0(最优先)
    • Logging 抽象与 Serilog 适配;EventBus 进程内 Typed 事件;Config 对接 IOptions 与变更广播
  • P1
    • Net.Client:HttpClientFactory 封装 + Result 模式;WPF Toolkit:MVVM/事件聚合/通知与主题
  • P2
    • 可靠性(Polly)、OpenTelemetry、外部 Broker 适配(Redis/RabbitMQ/Kafka)、敏感字段加密
  • P3
    • 文档/示例完善、CI/CD(自动打包发布)、更多单测与性能基准

贡献与规范

  • 欢迎通过 Issue/PR 参与贡献。
  • 代码风格:.NET 8、可空启用、简化 using,命名空间建议统一为 Sharp.*(Common 模块后续将统一为 Sharp.Common)。
  • 变更需保持向后兼容(次版本),破坏式变更请在主版本发布并注明迁移指南。

许可

本项目采用 GNU AGPLv3 许可证,详见根目录 LICENSE。

如需在私有环境或商业场景中使用且不希望开源网络服务端改动代码,请联系作者洽谈其他授权方式。

S
Description
C#公共基础核心
Readme AGPL-3.0
5.6 MiB
0 Stars 1 Watchers 0 Forks
Languages
C# 98.6%
Batchfile 0.9%
PowerShell 0.5%