System.AppContext 类

本文提供了此 API 参考文档的补充说明。

AppContext 类使库编写者能够为其用户提供新的功能的统一选择退出机制。 它建立组件之间的松散耦合协定,以便传达选择退出请求。 当对现有功能进行更改时,此功能通常很重要。 相反,已有新功能隐式选择加入。

库开发人员的 AppContext

库使用 AppContext 类来定义和公开兼容性开关,而库用户可以设置这些开关以影响库行为。 默认情况下,库提供新功能,并且仅在设置开关时对其进行更改(即提供以前的功能)。 这样,库就可以为现有 API 提供新行为,同时继续支持依赖先前行为的调用方。

定义开关名称

允许库使用者选择退出行为更改的最常见方法是定义命名开关。 它的 value 元素是一个名称/值对,由开关的名称及其 Boolean 值组成。 默认情况下,开关总是隐式地设置为false,以提供新的行为(并默认启用新的行为)。 设置开关为true后可启用它,从而恢复旧有行为。 将此开关显式设置为 false 还将提供新行为。

使用一致的开关名称格式是有益的,因为它们是由库公开的正式协定。 以下是两种明显的格式:

  • 切换命名空间switchname
  • 切换switchname

定义并记录开关后,调用方可以通过以编程方式调用 AppContext.SetSwitch(String, Boolean) 该方法来使用它。 .NET Framework 应用程序也可以通过在应用程序配置文件中添加 <AppContextSwitchOverrides> 元素,或通过注册表来使用该开关。 有关调用方如何使用和设置 AppContext 配置开关的值的详细信息,请参阅库使用者的 AppContext 部分。

在 .NET Framework 中,公共语言运行时运行应用程序时,它会自动读取注册表的兼容性设置并加载应用程序配置文件以填充应用程序的 AppContext 实例。 由于AppContext实例是由调用者或运行时以编程方式填充的,因此 .NET Framework 应用无需执行任何操作(例如调用SetSwitch方法)来配置AppContext实例。

检查设置

您可以检查用户是否已声明开关的值,并通过调用 AppContext.TryGetSwitch 方法来采取适当的操作。 如果true找到参数,该方法将返回switchName,并且其isEnabled参数指示开关的值。 否则,该方法返回 false

示例:

下面的示例演示了 AppContext 类的使用,以允许客户选择库方法的原始行为。 下面是名为 StringLibrary 的库的版本 1.0。 它定义一个 SubstringStartsAt 方法,该方法执行序号比较,以确定较大字符串中子字符串的起始索引。

using System;
using System.Reflection;

[assembly: AssemblyVersion("1.0.0.0")]

public static class StringLibrary1
{
    public static int SubstringStartsAt(string fullString, string substr)
    {
        return fullString.IndexOf(substr, StringComparison.Ordinal);
    }
}
open System
open System.Reflection

[<assembly: AssemblyVersion("1.0.0.0")>]
do ()

module StringLibrary =
    let substringStartsAt (fullString: string) (substr: string) =
        fullString.IndexOf(substr, StringComparison.Ordinal)
Imports System.Reflection

<Assembly: AssemblyVersion("1.0.0.0")>

Public Class StringLibrary
   Public Shared Function SubstringStartsAt(fullString As String, substr As String) As Integer
      Return fullString.IndexOf(substr, StringComparison.Ordinal)
   End Function
End Class

以下示例随后使用库在“考古学家”中查找子字符串“archæ”的起始索引。 由于该方法执行序号比较,因此找不到子字符串。

using System;

public class Example1
{
    public static void Main()
    {
        string value = "The archaeologist";
        string substring = "archæ";
        int position = StringLibrary1.SubstringStartsAt(value, substring);
        if (position >= 0)
            Console.WriteLine($"'{substring}' found in '{value}' starting at position {position}");
        else
            Console.WriteLine($"'{substring}' not found in '{value}'");
    }
}
// The example displays the following output:
//       'archæ' not found in 'The archaeologist'
let value = "The archaeologist"
let substring = "archæ"

let position =
    StringLibrary.substringStartsAt value substring

if position >= 0 then
    printfn $"'{substring}' found in '{value}' starting at position {position}"
else
    printfn $"'{substring}' not found in '{value}'"

// The example displays the following output:
//       'archæ' not found in 'The archaeologist'
Public Module Example4
    Public Sub Main()
        Dim value As String = "The archaeologist"
        Dim substring As String = "archæ"
        Dim position As Integer = StringLibrary.SubstringStartsAt(value, substring)
        If position >= 0 Then
            Console.WriteLine("'{0}' found in '{1}' starting at position {2}",
                        substring, value, position)
        Else
            Console.WriteLine("'{0}' not found in '{1}'", substring, value)
        End If
    End Sub
End Module
' The example displays the following output:
'       'archæ' not found in 'The archaeologist'

然而,2.0 版本的库会将 SubstringStartsAt 方法更改为使用区分区域性的比较。

using System;
using System.Reflection;

[assembly: AssemblyVersion("2.0.0.0")]

public static class StringLibrary2
{
    public static int SubstringStartsAt(string fullString, string substr)
    {
        return fullString.IndexOf(substr, StringComparison.CurrentCulture);
    }
}
open System
open System.Reflection

[<assembly: AssemblyVersion("2.0.0.0")>]
do ()

module StringLibrary =
    let substringStartsAt (fullString: string) (substr: string) =
        fullString.IndexOf(substr, StringComparison.CurrentCulture)
Imports System.Reflection

<Assembly: AssemblyVersion("2.0.0.0")>

Public Class StringLibrary
   Public Shared Function SubstringStartsAt(fullString As String, substr As String) As Integer
      Return fullString.IndexOf(substr, StringComparison.CurrentCulture)
   End Function
End Class

当重新编译应用以针对新版本的库运行时,它现在报告称在“The archaeologist”中的索引 4 处找到了子字符串“archæ”。

using System;

public class Example2
{
    public static void Main()
    {
        string value = "The archaeologist";
        string substring = "archæ";
        int position = StringLibrary2.SubstringStartsAt(value, substring);
        if (position >= 0)
            Console.WriteLine($"'{substring}' found in '{value}' starting at position {position}");
        else
            Console.WriteLine($"'{substring}' not found in '{value}'");
    }
}
// The example displays the following output:
//       'archæ' found in 'The archaeologist' starting at position 4
let value = "The archaeologist"
let substring = "archæ"

let position =
    StringLibrary.substringStartsAt value substring

if position >= 0 then
    printfn $"'{substring}' found in '{value}' starting at position {position}"
else
    printfn $"'{substring}' not found in '{value}'"

// The example displays the following output:
//       'archæ' found in 'The archaeologist' starting at position 4
Public Module Example6
    Public Sub Main()
        Dim value As String = "The archaeologist"
        Dim substring As String = "archæ"
        Dim position As Integer = StringLibrary.SubstringStartsAt(value, substring)
        If position >= 0 Then
            Console.WriteLine("'{0}' found in '{1}' starting at position {2}",
                        substring, value, position)
        Else
            Console.WriteLine("'{0}' not found in '{1}'", substring, value)
        End If
    End Sub
End Module
' The example displays the following output:
'       'archæ' found in 'The archaeologist' starting at position 4

可以通过定义开关来防止依赖于原始行为的应用程序因更改而中断。 在本例中,开关命名为StringLibrary.DoNotUseCultureSensitiveComparison。 其默认值 false 表示库将执行其 2.0 版本的文化敏感比较。 true 指示库应执行其版本 1.0 序号比较。 对前面的代码进行轻微修改后,库使用者可以设置开关以确定该方法执行的比较类型。

using System;
using System.Reflection;

[assembly: AssemblyVersion("2.0.0.0")]

public static class StringLibrary
{
   public static int SubstringStartsAt(string fullString, string substr)
   {
      bool flag;
      if (AppContext.TryGetSwitch("StringLibrary.DoNotUseCultureSensitiveComparison", out flag) && flag == true)
         return fullString.IndexOf(substr, StringComparison.Ordinal);
      else
         return fullString.IndexOf(substr, StringComparison.CurrentCulture);
   }
}
open System
open System.Reflection

[<assembly: AssemblyVersion("2.0.0.0")>]
do ()

AppContext.SetSwitch("StringLibrary.DoNotUseCultureSensitiveComparison",true)

module StringLibrary =
    let substringStartsAt (fullString: string) (substr: string) =
        match AppContext.TryGetSwitch "StringLibrary.DoNotUseCultureSensitiveComparison" with 
        | true, true -> fullString.IndexOf(substr, StringComparison.Ordinal)
        | _ -> fullString.IndexOf(substr, StringComparison.CurrentCulture)
Imports System.Reflection

<Assembly: AssemblyVersion("2.0.0.0")>

Public Class StringLibrary
   Public Shared Function SubstringStartsAt(fullString As String, substr As String) As Integer
      Dim flag As Boolean
      If AppContext.TryGetSwitch("StringLibrary.DoNotUseCultureSensitiveComparison", flag) AndAlso flag = True Then
         Return fullString.IndexOf(substr, StringComparison.Ordinal)
      Else
         Return fullString.IndexOf(substr, StringComparison.CurrentCulture)
      End If   
   End Function
End Class

然后,.NET Framework 应用程序可以使用以下配置文件还原版本 1.0 行为。

<configuration>
   <runtime>
      <AppContextSwitchOverrides value="StringLibrary.DoNotUseCultureSensitiveComparison=true" />
   </runtime>
</configuration>

当应用程序与配置文件一起运行时,它将生成以下输出:

'archæ' not found in 'The archaeologist'

库使用者的 AppContext

如果你是库的使用者,则 AppContext 类允许你利用库或库方法的选择退出机制来实现新功能。 要调用的类库的各个方法定义启用或禁用新行为的特定开关。 开关的值是布尔值。 false如果是,这通常是默认值,则启用新行为;如果是true,则禁用新行为,并且成员的行为与以前一样。

可以通过在代码中调用 AppContext.SetSwitch(String, Boolean) 方法来设置开关的值。 该 switchName 参数定义开关名称,属性 isEnabled 定义开关的值。 由于 AppContext 是静态类,因此它基于每个应用程序域可用。 AppContext.SetSwitch(String, Boolean)调用具有应用程序范围;也就是说,它仅影响应用程序。

.NET Framework 应用具有设置开关值的其他方法:

  • 通过将 <AppContextSwitchOverrides> 元素添加到 app.config 文件的 <运行时> 部分。 该开关具有单个属性, value其值为一个字符串,表示包含开关名称和其值的键/值对。

    若要定义多个开关,请在>元素的属性中,使用分号分隔每个开关的键/值对。 在这种情况下,该 <AppContextSwitchOverrides> 元素具有以下格式:

    <AppContextSwitchOverrides value="switchName1=value1;switchName2=value2" />
    

    <AppContextSwitchOverrides>使用元素定义配置设置具有应用程序范围;也就是说,它只影响应用程序。

    注释

    有关 .NET Framework 定义的开关的信息,请参阅 <AppContextSwitchOverrides> 元素

  • 通过将条目添加到注册表中。 将新的字符串值添加到 HKLM\SOFTWARE\Microsoft\.NETFramework\AppContext 子项。 将条目的名称设置为开关的名称。 将其值设置为以下选项之一:True、、trueFalsefalse。 如果运行时遇到任何其他值,它将忽略开关。

    在 64 位操作系统上,你还必须将相同的条目添加到 HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AppContext 子项中。

    使用注册表定义 AppContext 交换机具有计算机范围;也就是说,它会影响计算机上运行的每个应用程序。

对于 ASP.NET 和 ASP.NET Core 应用程序,可以通过将 <Add> 元素添加到 web.config 文件的 <appSettings> 部分来设置开关。 例如:

<appSettings>
   <add key="AppContext.SetSwitch:switchName1" value="switchValue1" />
   <add key="AppContext.SetSwitch:switchName2" value="switchValue2" />
</appSettings>

如果以多种方式设置相同的开关,则确定哪些设置替代其他设置的优先级顺序为:

  1. 编程设置。
  2. app.config 文件中的设置(适用于 .NET Framework 应用)或 web.config 文件(适用于 ASP.NET Core 应用)。
  3. 注册表设置(仅适用于 .NET Framework 应用)。

另请参阅