System.AppContext 类

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

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

面向库开发人员的 AppContext

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

定义开关名称

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

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

  • Switch.namespace.switchname
  • Switch.library.switchname

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

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

检查设置

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

示例

下面的示例演示了 AppContext 类的使用,以允许客户选择库方法的原始行为。 下面是名为 StringLibrary..0 的库的版本 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("'{0}' found in '{1}' starting at position {2}",
                           substring, value, position);
        else
            Console.WriteLine("'{0}' not found in '{1}'", substring, 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

当重新编译应用以针对新版本的库运行时,它现在报告在“考古学家”索引 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("'{0}' found in '{1}' starting at position {2}",
                           substring, value, position);
        else
            Console.WriteLine("'{0}' not found in '{1}'", substring, 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 元素的属性中使用<分号分隔每个开关的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 应用程序,可以通过向 web.config 文件的 app 添加<元素<>设置>部分来设置开关。 例如:

<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 应用)。

另请参阅