在 MSBuild 中有一些特殊字符,如 $ @ % ' 等,本文介绍他们的含义,如何使用他们,以及你真的需要这些字符的时候如何编写他们。 特殊字符 MSBuild 中有这些特殊字符: $ @ % ' ; ? * 含义和用法 $ 引用一个属性或者环境变量。 <Project> <ItemGroup> &...
在项目文件 csproj 中或者 MSBuild 的 Target 中使用 % 引用集合中每一项的属性
在编写项目文件或者 MSBuild Target 文件的时候,我们经常会使用 <Foo Include="Identity" /> 来定义集合中的一项。在定义的同时,我们也会额外指定一些属性。 然而这些属性如何拿到并且使用呢?本文将介绍使用方法。 将下面的代码放到你项目文件的末尾,最后一个 </Project> 的前面,可以在编译的时候看到两个新的警告。 &...
使用 7-Zip 的命令行版本来压缩和解压文件
7-Zip 也有一个简短的名称 7z。它的原生 UI 确实不怎么好看,非常有年代感;不过 7-Zip 的强大之处不在于 UI,而在于其算法和各种文件的支持情况。不过,7-Zip 提供了命令行的版本,让你摒除一切杂念,专心处理压缩文件的工作。 本文介绍如何通过命令行来使用 7-Zip。因为使用命令行,所以你甚至可以自动化地完成压缩文件的各种处理。 如何找到 7-Zip 的命令行版本...
ClearType 的原理:Windows 上文本的亚像素控制
有位小伙伴问我为什么他电脑上的文本看起来比较虚。我去看了下,发现他电脑上关掉了 ClearType。 微软的 ClearType 技术通过控制亚像素来使得文本显示更为清晰。本文代理了解 Windows 系统上的文本是如何通过亚像素控制使得显示更为清晰的。 ClearType 打开和关闭之后的效果 看下图!同样的文本,在不同大小下以及开关 ClearType 下的显示效果: ...
C#/.NET 使用 git 命令行来操作 git 仓库
我们可以在命令行中操作 git,但是作为一名程序员,如果在大量重复的时候还手动敲命令行,那就太笨了。 本文介绍使用 C# 编写一个 .NET 程序来自动化地使用 git 命令行来操作 git 仓库。 这是一篇很基础的入门文章。 最简单的运行 git 命令的代码 在 .NET 中,运行一个命令只需要使用 Process.Start 开启一个子进程就好了。于是要运行一个 git ...
如何快速自定义 Visual Studio 中部分功能的快捷键
Visual Studio 中有些自带的快捷键与现有软件有冲突,那么如何修改这些快捷键让这些功能正常工作起来呢? 打开快捷键设置界面 在 Visual Studio 中打开 “工具 -> 选项”,打开选项设置界面。在其中找到 “环境 -> 键盘” 项。我们设置快捷键的地方就在这里。 修改一个现有功能的快捷键 默认情况下,在 Visual Studio 2019...
如何在 MSBuild 的项目文件 csproj 中获取绝对路径
通常我们能够在 csproj 文件中仅仅使用相对路径就完成大多数的编译任务。但是有些外部命令的执行需要用到绝对路径,或者对此外部工具来说,相对路径具有不同的含义。这个时候,就需要将相对路径在 csproj 中转换为绝对路径来使用。 本文介绍如何在项目文件 csproj 中将一个相对路径转换为绝对路径。 在 MSBuild 4.0 中,可以在 csproj 中编写调用 PowerShe...
MSBuild 如何编写带条件的属性、集合和任务 Condition?
在项目文件 csproj 中,通过编写带条件的属性(PropertyGroup)、集合(ItemGroup)和任务(Target)可以完成更加复杂的项目文件的功能。 本文介绍如何编写带条件的 MSBuild 项。 Condition 如果要给你的 MSBuild 项附加条件,那么加上 Condition 特性即可。 Condition 可以写在任何地方,例如 Property...
NullReferenceException,就不应该存在!
如果要你说出 .NET 中的三个异常,NullReferenceException 一定会成为其中一个;如果说出 .NET 中的一个异常,NullReferenceException 也会被大多数人说出来。它让这么多人印象深刻,是因为它在项目中实在是太常见了,常见到每一个 C#/.NET 入门者必然会遇到。 然而,这个异常本不应该存在! null The worst mi...
C# 跨设备前后端开发探索
每个人都拥有 好奇心,好奇心驱使着我们总是去尝试做一些有趣的事情。 带起你的好奇心,本文将使用 C# 开发各种各样好玩的东西。 本文内容已加入 2019 年 4 月 13 日的广州 .NET 俱乐部第 2 届线下沙龙。 0x00 序章 好奇心 每个人都拥有 好奇心,好奇心驱使着我们总是去尝试做一些有趣的事情。 比如这件事: 在好奇心的驱使下,我们立刻 尝试 我们...
从零开始学习 dotnet 编译过程和 Roslyn 源码分析
本文整理我和 林德熙 学习的 dotnet 编译知识、Roslyn 源码分析知识,NuGet 知识。通过阅读本文可以从零散的碎片化博客中得到从零开始学习的轨迹。 本文服务于 微软技术暨生态大会 2018 课程,你可以学习预编译框架相关的技术原理。 SourceYard 性能数据 SourceYard 通过将公共组件的源代码和产品源代码合并来提升性能。 以下是这部分的性能数据: ...
(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
每次使用 Visual Studio 的模板创建一个 UWP 程序,我们会在项目中发现大量的项目文件、配置、应用启动流程代码和界面代码。然而这些文件在 UWP 程序中到底是如何工作起来的? 我从零开始创建了一个 UWP 程序,用于探索这些文件的用途,了解 UWP 程序的启动流程。 本文分为两个部分: 从零开始创建一个 UWP 项目并完成部署 从零开始编写一个 UWP 应用...
.NET/C# 项目如何优雅地设置条件编译符号?
条件编译符号指的是 Conditional Compilation Symbols。你可以在 Visual Studio 的项目属性中设置,也可以直接在项目文件中写入 DefineConstants 属性。 不过对于不同种类的项目,我建议使用不同的设置方法。本文将介绍如何设置条件编译符。 对于新旧格式的差别或者迁移,可以查看我的其他博客: 理解 C# 项目 csproj 文件...
.NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
当你的项目中多个不同的项目以及不同的依赖存在不同的依赖程序集时,可能会因为依赖于不同版本的程序集而产生冲突。而绑定重定向可以帮助解决不同程序集的依赖版本不同的问题,使整个程序使用统一个版本的 dll 来运行整个应用程序。 然而,如果我们就是需要使用一个分离的不同版本,那么我们就需要禁用掉自动生成绑定重定向。本文介绍如何禁用自动生成绑定重定向。 本文的结论只有一句,就是在项目中设置属性...
在 Visual Studio 新旧不同的 csproj 项目格式中启用混合模式调试程序(开启本机代码调试)
因为我使用 Visual Studio 主要用来编写 .NET 托管程序,所以平时调试的时候是仅限托管代码的。不过有时需要在托管代码中混合调试本机代码,那么就需要额外在项目中开启本机代码调试。 本文介绍如何开启本机代码调试。 本文涉及到新旧 csproj 项目格式,不懂这个也不影响你完成开启本机代码调试。不过如果你希望了解,可以阅读:将 WPF、UWP 以及其他各种类型的旧 cs...
Visual Studio 2017 以前的旧格式的 csproj Import 进来的 targets 文件有时不能正确计算属性(PropertyGroup)和集合(ItemGroup)
我在之前的博客中有教大家如何编写 NuGet 工具包,其中就有编写 .targets 文件。 我在实际的使用中,发现 Visual Studio 2017 带来的 Sdk 风格的 csproj 格式基本上没有多少坑;然而旧的 csproj 文件却总是不能完美的运行,总是出错。关键是,不是每台电脑都出错,不是每个时机都出错。 本文将讲一些坑。 本文的前置知识 你可能需要了解 c...
csproj 文件中那个空的 NuGetPackageImportStamp 是干什么的?
当我们在传统格式的 csproj 项目文件中安装 NuGet 包后,有时会在项目文件中发现空的 NuGetPackageImportStamp 节点。这个空的节点让我们这波强迫症患者觉得有点难以接受,关键是手工删除之后也没发现有什么副作用。 那么为什么会出现这个节点?它究竟有什么作用? 空的 NuGetPackageImportStamp 节点 NuGetPackageImpo...
.NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
.NET/C# 程序从 Main 函数开始执行,基本上各种书籍资料都是这么写的。不过,我们可以写多个 Main 函数,然后在项目文件中设置应该选择哪一个 Main 函数。 你可能会觉得这样没有什么用,不过如果你的应用程序在不同的编译条件下有不同的启动代码,或者你需要持续去大范围修改启动代码,那么做一个 Main 函数的选择器是一个不错的选择。 在哪里选择 Main? 在带有 M...
阻止某个 NuGet 包意外升级
出于兼容性考虑,我们可能不再更新某个项目的 NuGet 包。典型的情况是软件版本进行了大规模的不兼容的升级,需要对旧格式的数据进行读取,以便迁移到新格式的数据。 然而,团队开发的软件可能因为某个小伙伴不知道这样的历史问题,从而手抖将某个不应该更新的 NuGet 包更新了,于是迁移就挂了。 本文提供了一种方法来避免某些特定 NuGet 包的升级。 如果你只关心结果,请直接前往最后一节...
Sdk 风格的 csproj 对 WPF/UWP 支持不太好?有第三方 SDK 可以用!MSBuild.Sdk.Extras
自从微软推出 .NET Core 以来,新的项目文件格式以其优秀的可扩展性正吸引着更多项目采用。然而——微软官方的 WPF/UWP 项目模板依然还在采用旧的 csproj 格式! 这只是因为——在 .NET Core 3.0 以前,基于 Microsoft.NET.Sdk 的官方 SDK 依然对 WPF/UWP 支持不够友好。 为什么要使用第三方的 SDK? 关于项目文件格式的...
如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
最近我将项目格式进行了升级,从旧的 csproj 升级成了新的 csproj;NuGet 包管理的方式也从 packages.config 升级成了 PackageReference。然而迁移完才发现,这个项目竟然还依赖了大量的从 NuGet 2.x 时代发布的 NuGet 包,这些包并不能在 PackageReference 下好好工作。 于是,我准备将所有这些包都进行升级。本文将介绍最...
理解 C# 项目 csproj 文件格式的本质和编译流程
写了这么多个 C# 项目,是否对项目文件 csproj 有一些了解呢?Visual Studio 是怎么让 csproj 中的内容正确显示出来的呢?更深入的,我能够自己扩展 csproj 的功能吗? 本文将直接从 csproj 文件格式的本质来看以上这些问题。 阅读本文,你将: 可以通读 csproj 文件,并说出其中每一行的含义 可以手工修改 csproj 文件,以实现...
自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference
在前段时间我写了一篇迁移 csproj 格式的博客 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成 Sdk 风格的 csproj,不过全过程是手工进行的,而且到最后处理 XAML 问题也非常头疼。 现在,我们可以利用工具自动地完成这个过程。当然,工具并不将 csproj 格式进行迁移,而是在不迁移格式的情况下,使用到 PackageReference 方式 NuGet 引用...
语义版本号(Semantic Versioning)
版本号格式不陌生吧,.NET 传统的版本号格式类似这样 1.5.1254.0。本文将推荐一种新的版本号格式——语义版本号,格式类似这样 1.4.6-beta。我推荐语义版本号是因为这样的版本号自包含语义,而且这样的语义能够在版本库中体现出来。 传统的版本号 如果你只是知道传统版本号由四个部分组成,那么建议去官方文档 Assembly Versioning 了解一下这种版本号的定义...
项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦)
知道了 csproj 文件中的一些常用属性,修改文件的时候就不会写很多的垃圾代码。 “项目文件中的已知属性系列”分为两个部分: 本文:项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦) - 吕毅 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦) - 吕毅 什么?你的 csproj 文件太...
让一个 csproj 项目指定多个开发框架
可移植类库、共享项目、.NET Standard 项目都能够帮我们完成跨多个 .NET SDK 的单一项目开发,但它们的跨 SDK 开发都有些限制。现在,我们又有新的方式能够跨多个 .NET SDK 开发了,这就是使用新的 csproj 文件格式。 看看拥有多个开发框架的项目长什么样吧! ▲ 多 SDK 项目 这个是我和 erdao 在 GitHub 上开源项目 dotnet-c...
.NET Core/Framework 创建委托以大幅度提高反射调用的性能
都知道反射伤性能,但不得不反射的时候又怎么办呢?当真的被问题逼迫的时候还是能找到解决办法的。 为反射得到的方法创建一个委托,此后调用此委托将能够提高近乎直接调用方法本身的性能。(当然 Emit 也能够帮助我们显著提升性能,不过直接得到可以调用的委托不是更加方便吗?) 性能对比数据 ▲ 没有什么能够比数据更有说服力(注意后面两行是有秒数的) 可能我还需要解释一下那五行数据的含...
将 C++/WinRT 中的线程切换体验带到 C# 中来(WPF 版本)
如果你要在 WPF 程序中使用线程池完成一个特殊的任务,那么使用 .NET 的 API Task.Run 并传入一个 Lambda 表达式可以完成。不过,使用 Lambda 表达式会带来变量捕获的一些问题,比如说你需要区分一个变量作用于是在 Lambda 表达式中,还是当前上下文全局(被 Lambda 表达式捕获到的变量)。然后,在静态分析的时候,也难以知道此 Lambda 表达式在整个方法...
解决 Git 重命名时遇到的大小写不敏感的问题
Windows/Mac OS 操作系统文件的大小写是不敏感的,不管文件路径是何种奇怪的大小写,我们始终可以以另一种大小写的方式访问到这个路径种的文件或者文件夹。Linux 操作系统文件的大小写却是敏感的,不同大小写意味着不同的路径。于是,Windows 下的 A 文件在 Docs 文件夹下,B 文件在 docs 文件夹下,最终效果是 A B 都在 docs 文件夹下;而同样的情况放到 Lin...
WPF 的命令的自动刷新时机——当你 CanExecute 会返回 true 但命令依旧不可用时可能是这些原因
在 WPF 中,你可以使用 Command="{Binding WalterlvCommand}" 的方式来让 XAML 中的一个按钮或其他控件绑定一个命令。这样,按钮的可用性会自动根据 WalterlvCommand 当前 CanExecute 的状态来改变。这本是一个非常智能的特性,直到你可能发现你按钮的可用性状态不正确…… 本文介绍默认情况下,WPF 在 UI 上的这些命令会在什么时...