Porady: definiowanie i użycie niestandardowych dostawców formatu liczbowego

Platforma .NET zapewnia obszerną kontrolę nad reprezentacją ciągów wartości liczbowych. Obsługuje ona następujące funkcje dostosowywania formatu wartości liczbowych:

  • Standardowe ciągi formatu liczbowego, które zapewniają wstępnie zdefiniowany zestaw formatów do konwertowania liczb na ich reprezentację ciągu. Można ich używać z dowolną metodą formatowania liczbowego, taką jak Decimal.ToString(String), z parametrem format . Aby uzyskać szczegółowe informacje, zobacz Standardowe ciągi formatu liczbowego.

  • Niestandardowe ciągi formatu liczbowego, które zapewniają zestaw symboli, które można połączyć w celu zdefiniowania niestandardowych specyfikatorów formatu liczbowego. Można ich również używać z dowolną metodą formatowania liczbowego, taką jak Decimal.ToString(String), z parametrem format . Aby uzyskać szczegółowe informacje, zobacz Niestandardowe ciągi formatu liczbowego.

  • Obiekty niestandardowe CultureInfo lub NumberFormatInfo definiujące symbole i wzorce formatu używane w wyświetlaniu reprezentacji ciągów wartości liczbowych. Można ich używać z dowolną metodą formatowania liczbowego, taką jak ToString, z parametrem provider . provider Zazwyczaj parametr służy do określania formatowania specyficznego dla kultury.

W niektórych przypadkach (na przykład gdy aplikacja musi wyświetlić sformatowany numer konta, numer identyfikacyjny lub kod pocztowy) te trzy techniki są niewłaściwe. Platforma .NET umożliwia również zdefiniowanie obiektu formatowania, który nie jest ani obiektem CultureInfo , NumberFormatInfo aby określić sposób formatowania wartości liczbowej. Ten temat zawiera instrukcje krok po kroku dotyczące implementowania takiego obiektu i zawiera przykład formatujący numery telefonów.

Definiowanie niestandardowego dostawcy formatu

  1. Zdefiniuj klasę, która implementuje IFormatProvider interfejsy i ICustomFormatter .

  2. Zaimplementuj metodę IFormatProvider.GetFormat . GetFormat to metoda wywołania zwrotnego, którą metoda formatowania (na String.Format(IFormatProvider, String, Object[]) przykład metoda) wywołuje w celu pobrania obiektu, który jest faktycznie odpowiedzialny za wykonywanie niestandardowego formatowania. Typowa implementacja programu GetFormat obejmuje następujące czynności:

    1. Określa, czy Type obiekt przekazany jako parametr metody reprezentuje ICustomFormatter interfejs.

    2. Jeśli parametr reprezentuje ICustomFormatter interfejs, GetFormat zwraca obiekt, który implementuje ICustomFormatter interfejs, który jest odpowiedzialny za zapewnienie niestandardowego formatowania. Zazwyczaj obiekt formatowania niestandardowego zwraca sam siebie.

    3. Jeśli parametr nie reprezentuje interfejsu ICustomFormatter , GetFormat zwraca wartość null.

  3. Zaimplementuj metodę Format . Ta metoda jest wywoływana przez metodę String.Format(IFormatProvider, String, Object[]) i jest odpowiedzialna za zwracanie reprezentacji ciągu liczby. Implementacja metody zwykle obejmuje następujące elementy:

    1. Opcjonalnie upewnij się, że metoda jest legalnie przeznaczona do świadczenia usług formatowania, sprawdzając provider parametr . W przypadku obiektów formatowania, które implementują metody IFormatProvider i ICustomFormatter, obejmuje to przetestowanie parametru provider pod kątem równości z bieżącym obiektem formatowania.

    2. Ustal, czy obiekt formatowania powinien obsługiwać specyfikatory formatu niestandardowego. (Na przykład specyfikator formatu "N" może wskazywać, że numer telefonu USA powinien być wyjściowy w formacie NANP, a "I" może wskazywać dane wyjściowe w formacie ZALECENIA ITU-T E.123). Jeśli są używane specyfikatory formatu, metoda powinna obsługiwać specyfikator określonego formatu. Jest przekazywany do metody w parametrze format . Jeśli nie ma specyfikatora, wartość parametru format to String.Empty.

    3. Pobierz wartość liczbową przekazaną do metody jako parametr.arg Wykonaj wszelkie wymagane manipulacje, aby przekonwertować je na reprezentację ciągu.

    4. Zwraca reprezentację ciągu parametru arg .

Używanie niestandardowego obiektu formatowania liczbowego

  1. Utwórz nowe wystąpienie niestandardowej klasy formatowania.

  2. Wywołaj metodę String.Format(IFormatProvider, String, Object[]) formatowania, przekazując ją do niestandardowego obiektu formatowania, specyfikator formatowania (lub String.Empty, jeśli nie jest używany), oraz wartość liczbową, która ma być sformatowana.

Przykład

W poniższym przykładzie zdefiniowano niestandardowego dostawcę formatu liczbowego o nazwie TelephoneFormatter , który konwertuje liczbę reprezentującą numer telefonu USA na format NANP lub E.123. Metoda obsługuje dwa specyfikatory formatu: "N" (który generuje format NANP) i "I" (który zwraca międzynarodowy format E.123).

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

Niestandardowy dostawca formatu liczbowego może być używany tylko z String.Format(IFormatProvider, String, Object[]) metodą . Inne przeciążenia metod formatowania liczbowego (takich jak ToString) z parametrem typu IFormatProvider wszystkie przekazują implementację IFormatProvider.GetFormatType obiektu reprezentującego NumberFormatInfo typ. W zamian oczekują, że metoda zwróci NumberFormatInfo obiekt. Jeśli tak nie jest, niestandardowy dostawca formatu liczbowego jest ignorowany, a NumberFormatInfo obiekt bieżącej kultury jest używany w jego miejscu. W tym przykładzie TelephoneFormatter.GetFormat metoda obsługuje możliwość, że może zostać niewłaściwie przekazana do metody formatowania liczbowego, sprawdzając parametr metody i zwracając null , jeśli reprezentuje typ inny niż ICustomFormatter.

Jeśli niestandardowy dostawca formatu liczbowego obsługuje zestaw specyfikatorów formatu, upewnij się, że określono domyślne zachowanie, jeśli w elemencie formatu używanym w wywołaniu String.Format(IFormatProvider, String, Object[]) metody nie podano żadnego specyfikatora formatu. W przykładzie "N" jest domyślnym specyfikatorem formatu. Dzięki temu można przekonwertować liczbę na sformatowany numer telefonu, podając jawny specyfikator formatu. Poniższy przykład ilustruje takie wywołanie metody.

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

Umożliwia również konwersję, jeśli nie ma specyfikatora formatu. Poniższy przykład ilustruje takie wywołanie metody.

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

Jeśli nie zdefiniowano domyślnego ICustomFormatter.Format specyfikatora formatu, implementacja metody powinna zawierać kod, taki jak poniższy, aby platforma .NET mogła zapewnić formatowanie, które nie obsługuje kod.

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

W przypadku tego przykładu metoda, która implementuje ICustomFormatter.Format , ma służyć jako metoda wywołania zwrotnego dla String.Format(IFormatProvider, String, Object[]) metody . W związku z tym sprawdza parametr, formatProvider aby określić, czy zawiera odwołanie do bieżącego TelephoneFormatter obiektu. Można jednak wywołać metodę bezpośrednio z kodu. W takim przypadku można użyć parametru formatProvider , aby podać CultureInfo obiekt lub NumberFormatInfo , który dostarcza informacje o formatowaniu specyficznym dla kultury.