Поделиться через


Класс System.AppContext

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Класс AppContext позволяет разработчикам библиотек предоставлять унифицированный механизм отключения новой функциональности для своих пользователей. Он устанавливает слабо связанный контракт между компонентами, чтобы сообщить запрос на отказ. Эта возможность обычно важна при изменении существующих функциональных возможностей. И наоборот, уже существует неявное согласие на новые функциональные возможности.

AppContext для разработчиков библиотек

Библиотеки используют класс AppContext для определения и предоставления коммутаторов совместимости, в то время как пользователи библиотеки могут задать эти параметры, чтобы повлиять на поведение библиотеки. По умолчанию библиотеки предоставляют новые функциональные возможности, и изменяют их на прежние, только если установлен соответствующий параметр. Это позволяет библиотекам предоставлять новое поведение для существующего API, продолжая поддерживать тех, кто зависит от предыдущего поведения.

Определение имени переключателя

Наиболее распространенный способ разрешить потребителям библиотеки отказаться от изменения поведения — определить именованный коммутатор. Его элемент value — это пара name/value, состоящая из имени коммутатора и его Boolean значения. По умолчанию параметр всегда неявно false, который обеспечивает и активирует новое поведение. Установка переключателя на true активирует его, что возвращает устаревшее поведение. Явная установка переключателя на false также обеспечивает новое поведение.

Полезно использовать единообразный формат имен для переключателей, поскольку они являются частью формального контракта, представленного библиотекой. Ниже приведены два очевидных формата:

  • Switch.пространства имен.switchname
  • коммутатор.библиотека.имя коммутатора

После определения и документирования коммутатора пользователи могут использовать его, программно вызывая метод AppContext.SetSwitch(String, Boolean). Приложения .NET Framework также могут использовать этот параметр, добавив элемент <AppContextSwitchOverrides> в файл конфигурации приложения или с помощью реестра. Дополнительные сведения о том, как вызывающие пользователи используют и задают значение коммутаторов конфигурации AppContext, см. в разделе AppContext для потребителей библиотеки.

В .NET Framework, когда среда CLR запускает приложение, он автоматически считывает параметры совместимости реестра и загружает файл конфигурации приложения для заполнения AppContext экземпляра приложения. Так как экземпляр AppContext заполняется либо вызывающим оператором, либо средой выполнения, приложения .NET Framework не должны выполнять никаких действий, например вызов метода SetSwitch, чтобы настроить экземпляр AppContext.

Проверка параметра

Можно определить, сообщил ли пользователь значение переключателя, и при необходимости действовать, вызвав метод AppContext.TryGetSwitch. Метод возвращает true, если найден аргумент switchName, а его аргумент isEnabled указывает значение коммутатора. В противном случае метод возвращает false.

Пример

В следующем примере показано использование класса AppContext, позволяющего клиенту выбрать исходное поведение метода библиотеки. Ниже приведена версия 1.0 библиотеки с именем StringLibrary. Он определяет метод 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

В следующем примере библиотека используется для поиска начального индекса подстроки "архе" в "Археолог". Так как метод выполняет порядковое сравнение, подстрока не найдена.

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

Когда приложение перекомпилируется для запуска с новой версией библиотеки, оно теперь сообщает, что подстрока "archæ" найдена на индексе 4 в "The archaeologist".

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>value точкой с запятой. В этом случае элемент <AppContextSwitchOverrides> имеет следующий формат:

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

    Использование элемента <AppContextSwitchOverrides> для определения параметра конфигурации имеет область приложения; то есть это влияет только на приложение.

    Примечание.

    Сведения о параметрах, определенных платформой .NET Framework, см. в <элементе AppContextSwitchOverrides>.

  • Добавив запись в реестр. Добавьте новое строковое значение в HKLM\SOFTWARE\Microsoft\.NETFramework\AppContext. Установите название записи в название переключателя. Задайте для его значения одно из следующих параметров: True, true, Falseили false. Если среда выполнения обнаруживает любое другое значение, он игнорирует переключатель.

    В 64-разрядной операционной системе необходимо также добавить ту же запись в HKLM\SOFTWARE\Wow6432Node\Microsoft\. Вложенный ключ NETFramework\AppContext.

    Использование реестра для определения переключателя AppContext имеет системный уровень; то есть это влияет на каждое приложение, работающее на компьютере.

Для приложений ASP.NET и ASP.NET Core необходимо установить переключатель, добавив элемент <Add> в раздел <appSettings> в файле web.config. Рассмотрим пример.

<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).

См. также