次の方法で共有


System.AppContext クラス

この記事では、この API のリファレンス ドキュメントに補足的な解説を提供します。

AppContext クラスを使用すると、ライブラリ ライターは、ユーザーの新機能に対して統一されたオプトアウト メカニズムを提供できます。 オプトアウト要求を通信するために、コンポーネント間で疎結合コントラクトを確立します。 この機能は、通常、既存の機能に変更が加えられた場合に重要です。 逆に、新しい機能の暗黙的なオプトインが既に存在します。

ライブラリ開発者向けの AppContext

ライブラリでは、 AppContext クラスを使用して互換性スイッチを定義および公開しますが、ライブラリ ユーザーはこれらのスイッチを設定してライブラリの動作に影響を与えることができます。 既定では、ライブラリは新しい機能を提供し、スイッチが設定されている場合にのみ変更します (つまり、以前の機能を提供します)。 これにより、ライブラリは、以前の動作に依存する呼び出し元を引き続きサポートしながら、既存の API に新しい動作を提供できます。

スイッチ名を定義する

ライブラリのコンシューマーが動作の変更をオプトアウトできるようにする最も一般的な方法は、名前付きスイッチを定義することです。 その value 要素は、スイッチの名前とその Boolean 値で構成される名前と値のペアです。 既定では、スイッチは常に暗黙的に falseされ、新しい動作が提供されます (既定では新しい動作がオプトインされます)。 スイッチを true に設定すると有効になり、従来の動作が提供されます。 スイッチを明示的に false に設定すると、新しい動作も提供されます。

スイッチ名はライブラリによって公開される正式なコントラクトであるため、一貫性のある形式を使用すると便利です。 2 つの明白な形式を次に示します。

  • スイッチ名前空間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

新しいバージョンのライブラリに対して実行するようにアプリを再コンパイルすると、サブ文字列 "archæ" が "考古学者" のインデックス 4 にあることが報告されるようになりました。

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 アプリには、スイッチの値を設定する追加の方法があります。

  • app.config ファイルの <AppContextSwitchOverrides> セクションに<要素を追加します。 スイッチには 1 つの属性 valueがあり、その値はスイッチ名とその値の両方を含むキーと値のペアを表す文字列です。

    複数のスイッチを定義するには、 <AppContextSwitchOverrides> 要素の value 属性で各スイッチのキーと値のペアをセミコロンで区切ります。 その場合、 <AppContextSwitchOverrides> 要素の形式は次のとおりです。

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

    <AppContextSwitchOverrides>要素を使用して構成設定を定義すると、アプリケーション スコープが与えられます。つまり、アプリケーションにのみ影響します。

    .NET Framework で定義されているスイッチの詳細については、「 <AppContextSwitchOverrides> 要素」を参照してください。

  • レジストリにエントリを追加する。 HKLM\SOFTWARE\Microsoft\.NETFramework\AppContext サブキーに新しい文字列値を追加します。 エントリの名前をスイッチの名前に設定します。 その値を、 TruetrueFalse、または falseのいずれかのオプションに設定します。 ランタイムが他の値を検出すると、スイッチは無視されます。

    64 ビット オペレーティング システムでは、HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AppContext サブキーにも同じエントリを追加する必要があります。

    レジストリを使用して AppContext スイッチを定義すると、マシン スコープが設定されます。つまり、コンピューター上で実行されているすべてのアプリケーションに影響します。

ASP.NET および ASP.NET Core アプリケーションの場合は、<> セクションに <Add> 要素を追加してスイッチを設定します。 例えば次が挙げられます。

<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 アプリの場合のみ)。

こちらもご覧ください