次の方法で共有


方法 : カスタム数値書式プロバイダーを定義して使用する

.NET Framework では、数値の文字列表現を広範に制御することができます。 数値の書式をカスタマイズするための、次のような機能がサポートされています。

  • 標準の数値書式指定文字列。これは、数値を文字列形式に変換するための定義済み形式のセットです。 format パラメーターを持つ、Decimal.ToString(String) などの任意の数値書式指定メソッドでこれらを使用できます。 詳細については、「標準の数値書式指定文字列」を参照してください。

  • カスタム数値書式指定文字列。これはシンボルのセットで、これらのシンボルを組み合わせてカスタム数値書式指定子を定義できます。 これらもまた、Decimal.ToString(String) などの、format パラメーターを持つ任意の数値書式指定メソッドで使用できます。 詳細については、「カスタム数値書式指定文字列」を参照してください。

  • カスタム CultureInfo または NumberFormatInfo オブジェクト。これらは、数値の文字列表現の表示で使用されるシンボルと書式パターンを定義します。 provider パラメーターを持つ、ToString などの任意の数値書式指定メソッドでこれらを使用できます。 通常は、カルチャ固有の書式設定を指定するために provider パラメーターを使用します。

これら 3 つの技法が不適切な場合もあります。書式設定されたアカウント番号、識別番号、または郵便番号をアプリケーションで表示する必要がある場合などです。 .NET Framework では、数値の書式を決定するために、CultureInfo オブジェクトでも NumberFormatInfo オブジェクトでもない書式設定オブジェクトを定義することもできます。 ここでは、そのようなオブジェクトの実装方法を手順を追って説明し、電話番号の書式を設定する例を示します。

カスタム書式プロバイダーを定義するには

  1. IFormatProvider インターフェイスと ICustomFormatter インターフェイスを実装するクラスを定義します。

  2. IFormatProvider.GetFormat メソッドを実装します。 GetFormat はコールバック メソッドで、カスタム書式指定を実際に行うオブジェクトを取得するために (String.Format(IFormatProvider, String, Object[]) メソッドなどの) 書式指定メソッドによって呼び出されます。 通常、GetFormat の実装は次のような操作を実行します。

    1. メソッド パラメーターとして渡される Type オブジェクトが ICustomFormatter インターフェイスを表すかどうかを判断します。

    2. パラメーターが ICustomFormatter インターフェイスを表す場合、GetFormat は、カスタム書式指定を行う ICustomFormatter インターフェイスを実装するオブジェクトを返します。 通常は、カスタム書式指定オブジェクト自身が返されます。

    3. パラメーターが ICustomFormatter インターフェイスを表さない場合、GetFormat は null を返します。

  3. Format メソッドを実装します。 このメソッドは String.Format(IFormatProvider, String, Object[]) メソッドによって呼び出され、数値の文字列表現を返します。 このメソッドを実装する場合には、通常、次のような操作を行う必要があります。

    1. 必要に応じて、provider パラメーターを検査することにより、このメソッドが適切な書式設定サービスを提供することを確認します。 IFormatProviderICustomFormatter の両方を実装する書式設定オブジェクトの場合、現在の書式設定オブジェクトと等しいかどうか provider パラメーターを検査する必要があります。

    2. 書式設定オブジェクトがカスタム書式指定子をサポートするかどうかを判断します。 (たとえば、書式指定子 "N" は米国の電話番号が NANP 形式で出力され、 "I" は ITU-T 推奨 E.123 形式で出力されることを示す可能性があります)。 書式指定子が使用される場合、メソッドで特定の書式指定子を処理する必要があります。 これは format パラメーターでメソッドに渡されます。 指定子が存在しない場合、format パラメーターの値は String.Empty です。

    3. arg パラメーターとしてメソッドに渡される数値を取得します。 それを文字列形式に変換するために必要な操作をすべて実行します。

    4. arg パラメーターの文字列表現を返します。

カスタム数値書式設定オブジェクトを使用するには

  1. カスタム書式設定クラスの新しいインスタンスを作成します。

  2. 書式指定メソッド String.Format(IFormatProvider, String, Object[]) を呼び出します。その際、カスタム書式設定オブジェクト、書式指定子 (使用しない場合は String.Empty)、および書式設定の対象となる数値を渡します。

使用例

次の例では、米国の電話番号を表す数値を NANP 形式または E.123 形式に変換する TelephoneFormatter という名前のカスタム数値書式プロバイダーを 定義します。 このメソッドは、"N" (NANP 形式を出力) および "I" (国際 E.123 形式を出力) の 2 つの書式指定子を処理します。

Public Class TelephoneFormatter : Implements IFormatProvider, ICustomFormatter
   Public Function GetFormat(formatType As Type) As Object _
                   Implements IFormatProvider.GetFormat
      If formatType Is GetType(ICustomFormatter) Then
         Return Me
      Else
         Return Nothing
      End If               
   End Function               

   Public Function Format(fmt As String, arg As Object, _
                          formatProvider As IFormatProvider) As String _
                   Implements ICustomFormatter.Format
      ' Check whether this is an appropriate callback             
      If Not Me.Equals(formatProvider) Then Return Nothing 

      ' Set default format specifier             
      If String.IsNullOrEmpty(fmt) Then fmt = "N"

      Dim numericString As String = arg.ToString

      If fmt = "N" Then
         Select Case numericString.Length
            Case <= 4 
               Return numericString
            Case 7
               Return Left(numericString, 3) & "-" & Mid(numericString, 4) 
            Case 10
               Return "(" & Left(numericString, 3) & ") " & _
                      Mid(numericString, 4, 3) & "-" & Mid(numericString, 7)   
            Case Else
               Throw New FormatException( _
                         String.Format("'{0}' cannot be used to format {1}.", _
                                       fmt, arg.ToString()))
         End Select
      ElseIf fmt = "I" Then
         If numericString.Length < 10 Then
            Throw New FormatException(String.Format("{0} does not have 10 digits.", arg.ToString()))
         Else
            numericString = "+1 " & Left(numericString, 3) & " " & Mid(numericString, 4, 3) & " " & Mid(numericString, 7)
         End If      
      Else
         Throw New FormatException(String.Format("The {0} format specifier is invalid.", fmt))
      End If 
      Return numericString  
   End Function
End Class

Public Module TestTelephoneFormatter
   Public Sub Main
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 0))
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 911))
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 8490216))
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 4257884748))

      Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 0))
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 911))
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 8490216))
      Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 4257884748))

      Console.WriteLine(String.Format(New TelephoneFormatter, "{0:I}", 4257884748))
   End Sub
End Module
using System;
using System.Globalization;

public class TelephoneFormatter : IFormatProvider, ICustomFormatter
{
   public object GetFormat(Type formatType)
   {
      if (formatType == typeof(ICustomFormatter))
         return this;
      else
         return null;
   }               

   public string Format(string format, object arg, IFormatProvider formatProvider)
   {
      // Check whether this is an appropriate callback             
      if (! this.Equals(formatProvider))
         return null; 

      // Set default format specifier             
      if (string.IsNullOrEmpty(format)) 
         format = "N";

      string numericString = arg.ToString();

      if (format == "N")
      {
         if (numericString.Length <= 4)
            return numericString;
         else if (numericString.Length == 7)
            return numericString.Substring(0, 3) + "-" + numericString.Substring(3, 4); 
         else if (numericString.Length == 10)
               return "(" + numericString.Substring(0, 3) + ") " +
                      numericString.Substring(3, 3) + "-" + numericString.Substring(6);   
         else
            throw new FormatException( 
                      string.Format("'{0}' cannot be used to format {1}.", 
                                    format, arg.ToString()));
      }
      else if (format == "I")
      {
         if (numericString.Length < 10)
            throw new FormatException(string.Format("{0} does not have 10 digits.", arg.ToString()));
         else
            numericString = "+1 " + numericString.Substring(0, 3) + " " + numericString.Substring(3, 3) + " " + numericString.Substring(6);
      }
      else
      {
         throw new FormatException(string.Format("The {0} format specifier is invalid.", format));
      } 
      return numericString;  
   }
}

public class TestTelephoneFormatter
{
   public static void Main()
   {
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 0));
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 911));
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 8490216));
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 4257884748));

      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 0));
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 911));
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 8490216));
      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 4257884748));

      Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:I}", 4257884748));
   }
}

このカスタム数値書式プロバイダーは、String.Format(IFormatProvider, String, Object[]) メソッドでのみ使用可能です。 IFormatProvider 型のパラメーターを持つ数値書式指定メソッドの他のすべてのオーバーロード (たとえば ToString) は、NumberFormatInfo 型を表す Type オブジェクトを IFormatProvider.GetFormat 実装に渡します。 これらは、メソッドから NumberFormatInfo オブジェクトが返されることを期待します。 これが返されない場合、カスタム数値書式プロバイダーは無視され、現在のカルチャの NumberFormatInfo オブジェクトが代わりに使用されます。 この例の TelephoneFormatter.GetFormat メソッドは、数値書式指定メソッドに渡されるものが適切でない場合に備えて、メソッド パラメーターを検査し、それが ICustomFormatter 以外の型を表す場合には null を返します。

カスタム数値書式プロバイダーで書式指定子のセットをサポートする場合には、String.Format(IFormatProvider, String, Object[]) メソッド呼び出しの書式指定項目に書式指定子が含まれない場合の既定の動作を必ず実装してください。 この例では、"N" が既定の書式指定子です。 こうして書式指定子を明示的に指定することで、数値を書式設定された電話番号に変換できます。 次の例は、このようなメソッド呼び出しを示しています。

Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 4257884748))
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 4257884748));

加えて、書式指定子が存在しない場合にも変換が可能です。 次の例は、このようなメソッド呼び出しを示しています。

Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 4257884748))
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 4257884748));

既定の書式指定子が定義されない場合、コードがサポートしない書式設定を .NET Framework で提供できるように、ICustomFormatter.Format メソッドの実装に次のようなコードを含める必要があります。

If TypeOf(arg) Is IFormattable Then 
   s = DirectCast(arg, IFormattable).ToString(fmt, formatProvider)
ElseIf arg IsNot Nothing Then    
   s = arg.ToString()
End If
if (arg is IFormattable) 
   s = ((IFormattable)arg).ToString(format, formatProvider);
else if (arg != null)    
   s = arg.ToString();

この例では、ICustomFormatter.Format を実装するメソッドが String.Format(IFormatProvider, String, Object[]) メソッドのコールバック メソッドとして機能するようになっています。 このため、このメソッドは formatProvider パラメーターを検査して、現在の TelephoneFormatter オブジェクトへの参照が含まれるかどうかを判別します。 また、コードからこのメソッドを直接呼び出すこともできます。 その場合には、カルチャ固有の書式設定情報を指定する CultureInfo オブジェクトまたは NumberFormatInfo オブジェクトを提供するために formatProvider パラメーターを使用できます。

コードのコンパイル

コマンド ラインで csc.exe または vb.exe を使用してコードをコンパイルします。 Visual Studio でコードをコンパイルするには、コンソール アプリケーション プロジェクト テンプレートの中にコードを配置します。

参照

概念

書式設定操作の実行