Procedimiento para definir y usar proveedores de formato numérico personalizado
.NET ofrece un amplio control sobre la representación de cadena de valores numéricos. Admite las siguientes características para personalizar el formato de los valores numéricos:
Cadenas con formato numérico estándar, que proporcionan un conjunto predefinido de formatos para convertir números en su representación de cadena. Se pueden usar con cualquier método de formato numérico, como Decimal.ToString(String), que tiene un parámetro
format
. Para obtener detalles, vea Cadenas con formato numérico estándar.Cadenas con formato numérico personalizado, que proporcionan un conjunto de símbolos que pueden combinarse para definir especificadores de formato numérico personalizado. Se pueden usar también con cualquier método de formato numérico, como Decimal.ToString(String), que tiene un parámetro
format
. Para obtener detalles, consulte Cadenas con formato numérico personalizado.Objetos personalizados CultureInfo o NumberFormatInfo, que definen los símbolos y los modelos de formato que se usan para mostrar las representaciones de cadena de valores numéricos. Se pueden usar con cualquier método de formato numérico, como ToString, que tiene un parámetro
provider
. Normalmente, el parámetroprovider
se usa para especificar el formato específico de la referencia cultural.
En algunos casos (por ejemplo, cuando una aplicación debe mostrar un número de cuenta con formato, un número de identificación o un código postal) estas tres técnicas no resultan apropiadas. .NET también permite definir un objeto de formato que no es ni CultureInfo ni NumberFormatInfo para determinar cómo se aplica formato a un valor numérico. En este tema se proporcionan instrucciones paso a paso para implementar este tipo de objeto y se ofrece un ejemplo que da formato a números de teléfono.
Definición de un proveedor de formato personalizado
Defina una clase que implementa las interfaces IFormatProvider y ICustomFormatter.
Implemente el método IFormatProvider.GetFormat. GetFormat es un método de devolución de llamada que el método de formato (como el método String.Format(IFormatProvider, String, Object[])) invoca para recuperar el objeto realmente responsable del formato personalizado. Una implementación típica de GetFormat hace lo siguiente:
Determina si el objeto Type pasado como un parámetro de método representa a una interfaz ICustomFormatter.
Si el parámetro representa a la interfaz ICustomFormatter, GetFormat devuelve un objeto que implementa la interfaz ICustomFormatter, que es responsable de proporcionar el formato personalizado. Normalmente, el objeto de formato personalizado se devuelve a sí mismo.
Si el parámetro no representa la interfaz ICustomFormatter, GetFormat devuelve
null
.
Implemente el método Format. Este método es invocado por el método String.Format(IFormatProvider, String, Object[]) y es responsable de devolver la representación de cadena de un número. La implementación del método normalmente implica lo siguiente:
Opcionalmente, asegúrese de que el método se haya diseñado para proporcionar servicios de formato al examinar el parámetro
provider
. En el caso de los objetos de formato que implementan IFormatProvider y ICustomFormatter, esto implica probar la igualdad del parámetroprovider
y el objeto de formato actual.Determine si el objeto de formato debe admitir especificadores de formato personalizado. (Por ejemplo, un especificador de formato "N" podría indicar que debe generarse un número de teléfono de los Estados Unidos en formato NANP, mientras que una "I" podría indicar la salida en el formato de la recomendación E.123 de ITU-T). Si se usan especificadores de formato, el método debe controlar el especificador de formato específico. Se pasa al método en el parámetro
format
. Si no hay ningún especificador, el valor del parámetroformat
es String.Empty.Recupere el valor numérico pasado al método como parámetro
arg
. Realice todas las manipulaciones necesarias para convertirlo en su representación de cadena.Devuelva la representación de cadena del parámetro
arg
.
Uso de un objeto de formato numérico personalizado
Cree una nueva instancia de la clase de formato personalizado.
Llame al método de formato String.Format(IFormatProvider, String, Object[]) y pásele el objeto de formato personalizado, el especificador de formato (o String.Empty si no se usa ninguno) y el valor numérico al que se va a dar formato.
Ejemplo
En el ejemplo siguiente se define un proveedor de formato numérico personalizado denominado TelephoneFormatter
que convierte un número que representa un número de teléfono de los Estados Unidos en su formato NANP o E.123. El método controla dos especificadores de formato, "N" (que genera el formato NANP) e "I" (que genera el formato E.123 internacional).
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
El proveedor de formato numérico personalizado puede utilizarse solo con el método String.Format(IFormatProvider, String, Object[]). Las demás sobrecargas de métodos de formato numérico (como ToString
) que tienen un parámetro de tipo IFormatProvider pasan a la implementación de IFormatProvider.GetFormat un objeto Type que representa al tipo NumberFormatInfo. A cambio, esperan que el método devuelva un objeto NumberFormatInfo. Si no es así, se omite el proveedor de formato numérico personalizado y se usa el objeto NumberFormatInfo de la referencia cultural actual en su lugar. En el ejemplo, el método TelephoneFormatter.GetFormat
controla la posibilidad de que se pueda pasar incorrectamente a un método de formato numérico al examinar el parámetro de método y devolver null
si representa a un tipo distinto de ICustomFormatter.
Si un proveedor de formato numérico personalizado admite un conjunto de especificadores de formato, asegúrese de proporcionar un comportamiento predeterminado si no se proporciona ningún especificador de formato en el elemento de formato usado en la llamada al método String.Format(IFormatProvider, String, Object[]). En el ejemplo, "N" es el especificador de formato predeterminado. Esto permite convertir un número en un número de teléfono con formato al proporcionar un especificador de formato explícito. En el ejemplo siguiente se muestra una llamada al método así.
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 4257884748));
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 4257884748))
Pero también permite que se produzca la conversión si no hay ningún especificador de formato. En el ejemplo siguiente se muestra una llamada al método así.
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 4257884748));
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 4257884748))
Si no se ha definido ningún especificador de formato predeterminado, la implementación del método ICustomFormatter.Format debe incluir código como el siguiente para que .NET pueda proporcionar formato no admitido por el código.
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
En el caso de este ejemplo, el método que implementa ICustomFormatter.Format está diseñado para que actúe como un método de devolución de llamada para el método String.Format(IFormatProvider, String, Object[]). Por lo tanto, examina el parámetro formatProvider
para determinar si contiene una referencia al objeto TelephoneFormatter
actual. Pero también se puede llamar al método directamente desde el código. En ese caso, puede usar el parámetro formatProvider
para proporcionar un objeto CultureInfo o NumberFormatInfo que aporte información de formato específica de la referencia cultural.