Vorgehensweise: Definieren und Verwenden von benutzerdefinierten numerischen Formatanbietern

.NET ermöglicht eine umfangreiche Steuerung der Zeichenfolgendarstellung numerischer Werte. Die folgenden Funktionen für die Anpassung des Formats numerischer Werte werden unterstützt:

  • Standardmäßige Zahlenformatzeichenfolgen, die einen vordefinierten Satz an Formaten für die Konvertierung von Zahlen in ihre Zeichenfolgendarstellung bereitstellen. Sie können diese mit jeder Zahlenformatierungsmethode verwenden, die über einen format-Parameter verfügt, z.B. Decimal.ToString(String). Weitere Informationen finden Sie unter Standardmäßige Zahlenformatzeichenfolgen.

  • Benutzerdefinierte Zahlenformatzeichenfolgen, die einen Satz von Symbolen bereitstellen, die kombiniert werden können, um benutzerdefinierte Zahlenformatbezeichner zu definieren. Sie können auch mit jeder Zahlenformatierungsmethode verwendet werden, die über einen format-Parameter verfügt, z.B. Decimal.ToString(String). Weitere Informationen finden Sie unter Benutzerdefinierte Zahlenformatzeichenfolgen.

  • Benutzerdefinierte CultureInfo- oder NumberFormatInfo-Objekte, die die beim Anzeigen der Zeichenfolgendarstellungen numerischer Werte verwendeten Symbole und Formatmuster definieren. Sie können diese mit jeder Zahlenformatierungsmethode verwenden, die über einen provider-Parameter verfügt, z.B. ToString. Üblicherweise wird der provider-Parameter verwendet, um eine kulturspezifische Formatierung anzugeben.

In einigen Fällen (z.B. wenn eine Anwendung eine formatierte Kontonummer, eine ID oder eine Postleitzahl anzeigen muss) sind diese drei Techniken nicht geeignet. .NET ermöglicht Ihnen auch die Definition eines Formatierungsobjekts, bei dem es sich weder um ein CultureInfo-Objekt noch um ein NumberFormatInfo-Objekt handelt, um zu bestimmen, wie ein numerischer Wert formatiert wird. Dieses Thema stellt eine Schrittanleitung für die Implementierung eines solchen Objekts bereit und bietet ein Beispiel, das Telefonnummern formatiert.

Definieren eines benutzerdefinierten Formatanbieters

  1. Definieren Sie eine Klasse, die die IFormatProvider- und ICustomFormatter-Schnittstelle implementiert.

  2. Implementieren Sie die IFormatProvider.GetFormat-Methode. GetFormat ist eine Rückrufmethode, die von der Formatierungsmethode (z.B. der String.Format(IFormatProvider, String, Object[])-Methode) aufgerufen wird, um das Objekt abzurufen, das tatsächlich für die benutzerdefinierte Formatierung zuständig ist. Eine typische Implementierung von GetFormat führt Folgendes aus:

    1. Bestimmt, ob das als Methodenparameter übergebene Type-Objekt eine ICustomFormatter-Schnittstelle darstellt.

    2. Wenn der Parameter die ICustomFormatter-Schnittstelle tatsächlich darstellt, gibt GetFormat ein Objekt zurück, das die für die benutzerdefinierte Formatierung zuständige ICustomFormatter-Schnittstelle implementiert. Üblicherweise gibt das benutzerdefinierte Formatierungsobjekt sich selbst zurück.

    3. Wenn der Parameter die ICustomFormatter-Schnittstelle nicht darstellt, gibt GetFormatnull zurück.

  3. Implementieren Sie die Format-Methode. Diese Methode wird von der String.Format(IFormatProvider, String, Object[])-Methode aufgerufen und ist für die Rückgabe der Zeichenfolgendarstellung einer Zahl zuständig. Die Implementierung der Methode umfasst üblicherweise Folgendes:

    1. Stellen Sie optional sicher, dass die Methode dazu in der Lage sein soll, Formatierungsdienste bereitzustellen, indem Sie den provider-Parameter untersuchen. Bei Formatierungsobjekten, die sowohl IFormatProvider als auch ICustomFormatter implementieren, umfasst dies das Testen des provider-Parameters auf Gleichheit mit dem aktuellen Formatierungsobjekt.

    2. Bestimmen Sie, ob das Formatierungsobjekt benutzerdefinierte Formatbezeichner unterstützen soll. (Beispielsweise könnte ein Formatbezeichner „N“ angeben, dass eine US-amerikanische Telefonnummer im NANP-Format (Nordamerikanischer Nummerierungsplan) ausgegeben werden soll, und ein Bezeichner „I“ könnte eine Ausgabe im Format der ITU-T-Empfehlung E.123 angeben.) Wenn Formatbezeichner verwendet werden, muss die Methode den angegebenen Formatbezeichner verarbeiten. Der Bezeichner wird im format-Parameter der Methode übergeben. Wenn kein Bezeichner vorhanden ist, lautet der Wert des format-Parameters String.Empty.

    3. Rufen Sie den numerischen Wert ab, der als arg-Parameter der Methode übergeben wurde. Führen Sie alle Änderungen durch, die notwendig sind, um den Wert in seine Zeichenfolgendarstellung zu konvertieren.

    4. Geben Sie die Zeichenfolgendarstellung des arg-Parameters zurück.

Verwenden eines benutzerdefinierten Zahlenformatierungsobjekts

  1. Erstellen Sie eine neue Instanz der benutzerdefinierten Formatierungsklasse.

  2. Rufen Sie die String.Format(IFormatProvider, String, Object[])-Formatierungsmethode auf, und übergeben Sie das benutzerdefinierte Formatierungsobjekt, den Formatierungsbezeichner (oder String.Empty, falls kein Bezeichner verwendet wird) und den numerischen Wert, der formatiert werden soll.

Beispiel

Das folgende Beispiel definiert einen benutzerdefinierten Zahlenformatanbieter namens TelephoneFormatter, der eine Zahl, die eine US-amerikanische Telefonnummer darstellt, in das entsprechende NANP- oder E.123-Format konvertiert. Die Methode verarbeitet zwei Formatbezeichner: „N“ (zur Ausgabe des NANP-Formats) und „I“ (zur Ausgabe des internationalen E.123-Formats).

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));
   }
}
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

Der benutzerdefinierte Zahlenformatanbieter kann nur mit der String.Format(IFormatProvider, String, Object[])-Methode verwendet werden. Alle anderen Überladungen der Zahlenformatierungsmethoden (z.B. ToString), die einen Parameter des Typs IFormatProvider aufweisen, übergeben der IFormatProvider.GetFormat-Implementierung ein Type-Objekt, das den NumberFormatInfo-Typ darstellt. Im Gegenzug erwarten sie, dass die Methode ein NumberFormatInfo-Objekt zurückgibt. Wenn dies nicht der Fall ist, wird der benutzerdefinierte Zahlenformatanbieter ignoriert und stattdessen das NumberFormatInfo-Objekt für die aktuelle Kultur verwendet. Im Beispiel verarbeitet die TelephoneFormatter.GetFormat-Methode die Möglichkeit, dass sie fälschlicherweise an eine Zahlenformatierungsmethode übergeben wurde, indem sie den Methodenparameter untersucht und null zurückgibt, wenn dieser einen anderen Typ als ICustomFormatter darstellt.

Wenn ein benutzerdefinierter Zahlenformatanbieter einen Satz von Formatbezeichnern unterstützt, stellen Sie sicher, dass Sie ein Standardverhalten einrichten, falls in dem Formatelement, das im String.Format(IFormatProvider, String, Object[])-Methodenaufruf verwendet wird, kein Formatbezeichner bereitgestellt wird. Im Beispiel ist „N“ der Standardformatbezeichner. Dadurch kann eine Zahl in eine formatierte Telefonnummer konvertiert werden, indem ein expliziter Formatbezeichner bereitgestellt wird. Das folgende Beispiel veranschaulicht einen solchen Methodenaufruf.

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

Es ermöglicht die Konvertierung aber auch, falls kein Formatbezeichner vorhanden ist. Das folgende Beispiel veranschaulicht einen solchen Methodenaufruf.

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

Wenn kein Standardformatbezeichner definiert ist, muss Ihre Implementierung der ICustomFormatter.Format-Methode einen Code wie den folgenden beinhalten, damit .NET eine Formatierung bereitstellen kann, die von Ihrem Code nicht unterstützt wird.

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

In diesem Beispiel dient die Methode, die ICustomFormatter.Format implementiert, als eine Rückrufmethode der String.Format(IFormatProvider, String, Object[])-Methode. Daher untersucht diese Methode den formatProvider-Parameter, um zu ermitteln, ob dieser einen Verweis auf das aktuelle TelephoneFormatter-Objekt enthält. Die Methode kann jedoch auch direkt aus dem Code aufgerufen werden. In diesem Fall können Sie den formatProvider-Parameter verwenden, um ein CultureInfo- oder NumberFormatInfo-Objekt bereitzustellen, das kulturspezifische Formatierungsinformationen bereitstellt.