当前位置: 首页 > news >正文

让一个 csproj 项目指定多个开发框架

可移植类库、共享项目、.NET Standard 项目都能够帮我们完成跨多个 .NET SDK 的单一项目开发,但它们的跨 SDK 开发都有些限制。现在,我们又有新的方式能够跨多个 .NET SDK 开发了,这就是使用新的 csproj 文件格式。


看看拥有多个开发框架的项目长什么样吧!

多 SDK 的项目
▲ 多 SDK 项目

是不是很激动?

新 csproj 文件

在 如何组织一个同时面向 UWP/WPF/.Net Core 控制台的 C# 项目解决方案 - walterlv 一文中我讲了 .NET Standard 的方式,这种方式优势非常明显,跟普通的开发方式一样,也是我最推荐的方式。但缺点是要求目标 SDK 支持对应的 .NET Standard 版本。

使用共享项目的方式则是直接共享了源码,只要在目标项目中指定了条件编译符,那么源码便能针对各种不同的目标框架进行分别编译。但缺点是对扩展插件的支持较差(可能是因为扩展插件难以判断项目的真实开发框架),而且 Visual Studio 本身对它的支持也有 BUG(例如切换编写文件所属的项目经常会失败)。

新的 csproj 文件能够指定多个开发框架。这样,我们便能同时编写适用于 .NET Framework 4.5 的和 .NET Standard 2.0 的代码,同时还能够得到 Visual Studio 和扩展插件较好的支持。

.NET Standard 和 .NET Core 项目在创建之时就已经是新的 csproj 格式了,但 .NET Framework 项目、UWP/WPF 项目依然使用旧风格的 csproj 文件。对于 .NET Framework 项目,可以通过 将 WPF、UWP 以及其他各种类型的旧样式的 csproj 文件迁移成新样式的 csproj 文件 - walterlv 一文进行迁移。不过对于 WPF/UWP 项目,根本就没有跨多个 SDK 的必要,就不要改了……

如果是新开项目——强烈建议先按照 .NET Standard 项目类型建好,再修改成多开发框架。

如何指定多个开发框架

只要是新 csproj 文件,指定多个开发框架真的是相当的简单。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
  </PropertyGroup>
  <!-- 这个文件里的其他内容 -->
</Project>

特别注意!!!TargetFramework 从单数形式变为了复数形式 TargetFrameworks!!!这个时候,TargetFramework 是编译时自动指定的。

如果是对以上多框架的项目进行单元测试,考虑到编译的目标平台是多个的,单元测试项目也需要指定多个目标框架。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net471;netcoreapp2.0</TargetFrameworks>
    <IsPackable>false</IsPackable>
  </PropertyGroup>
  <!-- 这个文件里的其他内容 -->
</Project>

多框架项目的坑以及如何避坑

微软的官方文档 How to: Configure Projects to Target Multiple Platforms - Microsoft Docs 中只说了如何指定多个目标框架,并没有提及设好了之后的坑。

如果多开发框架中包含了低版本的 .NET Framework,例如 4.0/4.5 等,那么这些坑才比较容易凸显——因为这些版本的 .NET Framework 与 .NET Standard 的第三方库差异较大。所以,我们需要有方法来解决其第三方库引用的差异。这时需要在 csproj 文件中指定包含条件。例如:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net471;netcoreapp2.0</TargetFrameworks>
    <OutputType Condition="'$(TargetFramework)'!='netcoreapp2.0'">Exe</OutputType>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <!-- 这里的引用是二者共有的 -->
  <ItemGroup>
    <PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
    <PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
  </ItemGroup>

  <!-- 这里的引用用于非 .NET Core 框架 -->
  <ItemGroup Condition="'$(TargetFramework)'!='netcoreapp2.0'">
    <PackageReference Include="Xxx" Version="1.0.*" />
  </ItemGroup>

  <!-- 这里的引用用于 .NET Core 框架 -->
  <ItemGroup Condition="'$(TargetFramework)'=='netcoreapp2.0'">
    <PackageReference Include="Yyy" Version="1.0.*" />
  </ItemGroup>

</Project>

参考资料

  • impromptu-interface/ImpromptuInterface.csproj at master · ekonbenefits/impromptu-interface
  • How to: Configure Projects to Target Multiple Platforms - Microsoft Docs

相关文章:

  • 在操作系统重启后恢复应用程序的工作状态
  • 生成代码,从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型
  • 应该抛出什么异常?不应该抛出什么异常?(.NET/C#)
  • 关闭模态窗口后,父窗口居然跑到了其他窗口的后面
  • 语义耦合(Semantic Coupling)
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • 在 Windows 安装期间将 MBR 磁盘转换为 GPT 磁盘
  • 解决大于 4GB 的 Windows 10 镜像在 UEFI 模式下的安装问题
  • 为什么 UEFI 方式启动的 U 盘必须使用 FAT32 文件系统?
  • 不再为命名而苦恼!使用 MSTestEnhancer 单元测试扩展,写契约就够了
  • Windows 10 自带那么多图标,去哪里找呢?
  • 如何删除 Windows 10 系统生成的 WindowsApps 文件夹
  • 命令“xxx.exe xxx”已退出,代码为 n。这些错误是什么意思?
  • 将 async/await 异步代码转换为安全的不会死锁的同步代码
  • 屏幕上那个灰色带有数字的框是什么?看着好难受!
  • Docker入门(二) - Dockerfile
  • HashMap ConcurrentHashMap
  • JDK 6和JDK 7中的substring()方法
  • jquery ajax学习笔记
  • laravel5.5 视图共享数据
  • React-Native - 收藏集 - 掘金
  • storm drpc实例
  • Unix命令
  • vagrant 添加本地 box 安装 laravel homestead
  • vue的全局变量和全局拦截请求器
  • Webpack 4 学习01(基础配置)
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 关于Flux,Vuex,Redux的思考
  • 近期前端发展计划
  • 蓝海存储开关机注意事项总结
  • 前嗅ForeSpider采集配置界面介绍
  • 使用common-codec进行md5加密
  • 收藏好这篇,别再只说“数据劫持”了
  • 最近的计划
  • 说说我为什么看好Spring Cloud Alibaba
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​ssh免密码登录设置及问题总结
  • #Spring-boot高级
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (分类)KNN算法- 参数调优
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (四)图像的%2线性拉伸
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .net 7 上传文件踩坑
  • .NET Core 通过 Ef Core 操作 Mysql
  • .Net Core 中间件验签
  • .Net CoreRabbitMQ消息存储可靠机制
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET文档生成工具ADB使用图文教程
  • .NET中的十进制浮点类型,徐汇区网站设计