2017-09-30 15:45
还记得微软在 Mitigation: Pointer-based Touch and Stylus Support 中告诉大家如何在 .NET Framework 4.7 中迁移 WPF 的触控到基于 Pointer 消息?记得关键的 <AppContextSwitchOverrides value="Switch.System.Windows.Input.Stylus.EnablePointerSupport=true"/>
这一句吗?
有没有好奇为何这一句话能用来控制微软基础类库中某一块功能的行为呢?阅读本文将了解微软为开发者提供的一套类库更新的兼容性解决方案——AppContext
。
这是微软自 .NET Framework 4.6 开始为开发者们提供的方案。
比如你打算为你的类库增加了一个功能——指定一个文件夹名称用于存放文件。你写出了这样的代码:
// 1.0 版本的类库
public static class StorageSomeInfo
{
public static void SetDirectoryName(string directoryName)
{
_directory = directoryName ?? throw new ArgumentNullException(nameof(directoryName));
// 其他逻辑。
}
}
你将类库发布到 NuGet 上,一切运行安好。
直到有一天,某人给 directoryName
传入了空字符串。结果你的文件全部都不再存到指定的文件夹下,而是存到了根目录……这跟你的预期不符啊!
然而,类库发布了这么久,这么多人都下载安装使用了,要是随随便便把代码改成这样,搞不好一大堆小伙伴将面临着崩溃……(谁知道他们有没有依赖于你的 BUG 编程呢?搞不好他们绞尽脑汁发现这样还可以存到根目录呢于是就开开心心地用了呢!)
// 2.0 版本的类库
public static class StorageSomeInfo
{
public static void SetDirectoryName(string directoryName)
{
if (string.IsNullOrWhitespace(directoryName))
throw new ArgumentException(nameof(directoryName));
// 其他逻辑。
}
}
[Obsolete]
是一个好方案,他能够指导开发者一步步迁移他们对 API 的使用。不过:
这时候祭出——AppContext
!
将你的 2.0 代码改成这样:
// 2.0 版本的类库
public static class StorageSomeInfo
{
public static void SetDirectoryName(string directoryName)
{
if (AppContext.TryGetSwitch("Switch.StorageSomeInfo.UseLegacyDirectoryName", out var flag)
&& flag == true)
// 跑以前的代码
else
// 跑新的代码
// 其他逻辑。
}
}
那么开发者们更新你的类库时,就有可以挽回的方案了:
你可以在更新日志中写下说明:
app.config
文件中添加以下代码以使用“遗弃的”逻辑。<configuration> <runtime> <AppContextSwitchOverrides value="Switch.StorageSomeInfo.UseLegacyDirectoryName=true" /> </runtime> </configuration>
开发者们如果有多个开关需要开启或关闭,则使用分号分隔多个开关:
<AppContextSwitchOverrides value="switchName1=value1;switchName2=value2" />
开发者们如果不想写配置文件,也可以直接在程序中调用:
AppContext.SetSwitch(string, bool);
当然,甚至可以直接动用注册表:HKLM\SOFTWARE\Microsoft\.NETFramework\AppContext
作为 Key,字符串作为 Value。依然是分号分割的键值对作为注册表项的值来存。如果采用注册表方案,将影响这台计算机上运行的所有程序。
这三种方式的优先级是:
app.config
中指定的优先级其次;在从 .NET Framework 4.6 升级到 4.7 后,注册表的方式貌似失效了。参考:FIX: AppContext switch overrides are not applied to applications that run on the .NET Framework 4.7
本文会经常更新,请阅读原文: https://walterlv.com/post/dotnet/2017/09/30/app-context.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com) 。