先说结论
MAUI 能不能"一套 C# 同时出 Windows 和 Android"?能,而且对业务型应用(表单、列表、数据采集、同步)体验相当完整。但它的共享是分层的:逻辑层≈100%,XAML 70--85%,平台能力层基本要重写。把这三行数字记住,选型时你就不会被"一次编写到处运行"的话术带偏。
工程结构:单项目多 TargetFramework
与"建两个工程"最大的不同,MAUI 是一个项目里声明多个目标框架:
xml
<TargetFrameworks>net8.0-android;net8.0-windows10.0.19041.0</TargetFrameworks>
平台差异代码用条件编译隔离在 Platforms/ 目录或 #if ANDROID / #elif WINDOWS 块里,共享层保持干净。启动入口 MauiProgram 用的是和 ASP.NET Core 同款的 ServiceCollection:
csharp
var builder = MauiApp.CreateBuilder();
builder.Services.AddSingleton<IPostRepository, PostRepository>();
builder.Services.AddTransient<MainPage>();
复用账本:按层拆给你看
- 领域模型 + 业务规则:纯 C# 类库,两端原样复用,单测只写一份;
- 数据访问 :
sqlite-net-pcl、HttpClient+ Polly,两端无差别; - ViewModel :
CommunityToolkit.Mvvm的[ObservableProperty]/[RelayCommand],从 WPF MVVM 平移,几乎零学习成本; - XAML :布局思想一致,但 Android 与 WinUI 3 的原生控件观感不同,需要
OnPlatform/OnIdiom微调,预算按"打八五折"估; - 平台能力:SecureStorage、Connectivity、Preferences 这些 Essential API 能兜住大半日常需求;涉及系统扫码、推送、文件关联就得分端写。
csharp
await SecureStorage.SetAsync("token", token); // 两端各自的加密存储,调用方无感
var online = Connectivity.Current.NetworkAccess == NetworkAccess.Internet;
两个大实话
- Windows 端走的是 WinUI 3,不是 WPF。存量 WPF 项目重度自定义控件的,别硬合------"桌面保留 WPF + MAUI 只出 Android"往往更省。
- 依赖库先过兼容清单再动手,Xamarin 时代的库不能假定可用,这一步半天,能救一周。
发布与国内合规
Android 签名打包一条命令(keystore 首次生成后永久复用):
bash
keytool -genkeypair -v -keystore myapp.keystore -alias myapp -keyalg RSA -keysize 2048 -validity 10000
dotnet publish -f net8.0-android -c Release -p:AndroidKeyStore=true ^
-p:AndroidSigningKeyStore=myapp.keystore -p:AndroidSigningKeyAlias=myapp ^
-p:AndroidSigningKeyPass=*** -p:AndroidSigningStorePass=***
Windows 端 dotnet publish -f net8.0-windows -c Release。
国内上架四件套缺一不可:权限最小化(Manifest 只声明实际使用的权限 + 运行时动态申请)、隐私政策(首启同意前零采集、第三方 SDK 全列清单)、App 备案(工信部,经云服务商办理,预留 2--4 周)、软著(商店开发者认证常要)。合规不是发布前补一下的事,排期里要给位置。
后端配 ASP.NET Core WebAPI:JWT + HTTPS + /api/v1/ 版本化,一套接口同时伺候老桌面端和新 APP。
选型判断标准
- 客户有 .NET 存量系统、要"业务型"APP、预算敏感 → MAUI 优先;
- 重动画、游戏化交互、强原生 SDK → 原生或 Flutter;
- 要 iOS 且没有 Mac 构建机 → 先把这条成本摆上桌再谈。
如果这篇对你有帮助,欢迎关注我,后续会持续写 .NET 桌面端 / 小程序 / APP 的实战交付经验。