Método System.String.Format

En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.

Importante

En lugar de llamar al método String.Format o usar cadenas de formato compuesto, se pueden usar cadenas interpoladas si el lenguaje las admite. Una cadena interpolada es una cadena que contiene expresiones interpoladas. Cada expresión interpolada se resuelve con el valor de la expresión y se incluye en la cadena de resultado cuando se asigna la cadena. Para obtener más información, vea Interpolación de cadenas (Referencia de C#) y Cadenas interpoladas (referencia de Visual Basic).

Ejemplos

En este artículo se intercalan numerosos ejemplos que llaman al Format método . También puede descargar un conjunto completo de ejemplos, que se incluyen en un proyecto de String.Format .NET Core para C#.

A continuación se muestran algunos de los ejemplos incluidos en el artículo:

Creación de una cadena de formato

Insertar una cadena
Elemento de formato
Dar formato a los elementos que tienen el mismo índice

Control de salida con formato

Formato de control
Espaciado de control
Alineación del control
Controlar el número de dígitos enteros
Controlar el número de dígitos después del separador decimal
Incluir llaves literales en la cadena de resultado

Hacer que las cadenas de formato sean sensibles a la referencia cultural

Hacer que las cadenas de formato sean sensibles a la referencia cultural

Personalización de la operación de formato

Una operación de formato personalizada
Un proveedor de interceptación y formateador de números romanos

Introducción al método String.Format

Use String.Format si necesita insertar el valor de un objeto, una variable o una expresión en otra cadena. Por ejemplo, puede insertar el valor de un Decimal valor en una cadena para mostrarlo al usuario como una sola cadena:

Decimal pricePerOunce = 17.36m;
String s = String.Format("The current price is {0} per ounce.",
                         pricePerOunce);
Console.WriteLine(s);
// Result: The current price is 17.36 per ounce.
let pricePerOunce = 17.36m
String.Format("The current price is {0} per ounce.", pricePerOunce)
|> printfn "%s"
// Result: The current price is 17.36 per ounce.
Dim pricePerOunce As Decimal = 17.36D
Dim s As String = String.Format("The current price is {0} per ounce.",
                              pricePerOunce)
' Result: The current price is 17.36 per ounce.

Y puede controlar el formato de ese valor:

Decimal pricePerOunce = 17.36m;
String s = String.Format("The current price is {0:C2} per ounce.",
                         pricePerOunce);
Console.WriteLine(s);
// Result if current culture is en-US:
//      The current price is $17.36 per ounce.
let pricePerOunce = 17.36m
String.Format("The current price is {0:C2} per ounce.", pricePerOunce)
|> printfn "%s"
// Result if current culture is en-US:
//      The current price is $17.36 per ounce.
Dim pricePerOunce As Decimal = 17.36D
Dim s As String = String.Format("The current price is {0:C2} per ounce.",
                              pricePerOunce)
' Result if current culture is en-US:
'      The current price is $17.36 per ounce.

Además del formato, también puede controlar la alineación y el espaciado.

Insertar una cadena

String.Format comienza con una cadena de formato, seguida de uno o varios objetos o expresiones que se convertirán en cadenas e insertarán en un lugar especificado en la cadena de formato. Por ejemplo:

decimal temp = 20.4m;
string s = String.Format("The temperature is {0}°C.", temp);
Console.WriteLine(s);
// Displays 'The temperature is 20.4°C.'
let temp = 20.4m
String.Format("The temperature is {0}°C.", temp)
|> printfn "%s"
// Displays 'The temperature is 20.4°C.'
Dim temp As Decimal = 20.4D
Dim s As String = String.Format("The temperature is {0}°C.", temp)
Console.WriteLine(s)
' Displays 'The temperature is 20.4°C.'

En {0} la cadena de formato es un elemento de formato. 0 es el índice del objeto cuyo valor de cadena se insertará en esa posición. (Los índices comienzan en 0). Si el objeto que se va a insertar no es una cadena, se llama a su ToString método para convertirlo en uno antes de insertarlo en la cadena de resultado.

Este es otro ejemplo que usa dos elementos de formato y dos objetos en la lista de objetos:

string s = String.Format("At {0}, the temperature is {1}°C.",
                         DateTime.Now, 20.4);
Console.WriteLine(s);
// Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'
String.Format("At {0}, the temperature is {1}°C.", DateTime.Now, 20.4)
|> printfn "%s"
// Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'
Dim s As String = String.Format("At {0}, the temperature is {1}°C.",
                              Date.Now, 20.4)
' Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'

Puede tener tantos elementos de formato y tantos objetos en la lista de objetos como desee, siempre y cuando el índice de cada elemento de formato tenga un objeto coincidente en la lista de objetos. Tampoco tienes que preocuparte por qué sobrecarga llamas; el compilador seleccionará el adecuado para usted.

Formato de control

Puede seguir el índice en un elemento de formato con una cadena de formato para controlar cómo se da formato a un objeto. Por ejemplo, {0:d} aplica la cadena de formato "d" al primer objeto de la lista de objetos. Este es un ejemplo con un solo objeto y dos elementos de formato:

string s = String.Format("It is now {0:d} at {0:t}", DateTime.Now);
Console.WriteLine(s);
// Output similar to: 'It is now 4/10/2015 at 10:04 AM'
String.Format("It is now {0:d} at {0:t}", DateTime.Now)
|> printfn "%s"
// Output similar to: 'It is now 4/10/2015 at 10:04 AM'
Dim s As String = String.Format("It is now {0:d} at {0:t}",
                              Date.Now)
' Output similar to: 'It is now 4/10/2015 at 10:04 AM'

Varios tipos admiten cadenas de formato, incluidos todos los tipos numéricos (cadenas de formato estándar y personalizado), todas las fechas y horas (cadenas de formato estándar y personalizado) e intervalos de tiempo (cadenas de formato estándar y personalizado), todos los tipos de enumeración de tipos de enumeración y GUID. También puede agregar compatibilidad con cadenas de formato a sus propios tipos.

Espaciado de control

Puede definir el ancho de la cadena que se inserta en la cadena de resultado mediante la sintaxis como {0,12}, que inserta una cadena de 12 caracteres. En este caso, la representación de cadena del primer objeto está alineada a la derecha en el campo de 12 caracteres. (Si la representación de cadena del primer objeto tiene más de 12 caracteres de longitud, sin embargo, se omite el ancho de campo preferido y se inserta toda la cadena en la cadena de resultado).

En el ejemplo siguiente se define un campo de 6 caracteres para contener la cadena "Year" y algunas cadenas de año, así como un campo de 15 caracteres para contener la cadena "Population" y algunos datos de rellenado. Tenga en cuenta que los caracteres están alineados a la derecha en el campo.

int[] years = { 2013, 2014, 2015 };
int[] population = { 1025632, 1105967, 1148203 };
var sb = new System.Text.StringBuilder();
sb.Append(String.Format("{0,6} {1,15}\n\n", "Year", "Population"));
for (int index = 0; index < years.Length; index++)
    sb.Append(String.Format("{0,6} {1,15:N0}\n", years[index], population[index]));

Console.WriteLine(sb);

// Result:
//      Year      Population
//
//      2013       1,025,632
//      2014       1,105,967
//      2015       1,148,203
open System
open System.Text
 
let years = [| 2013; 2014; 2015 |]
let population = [| 1025632; 1105967; 1148203 |]
let sb = StringBuilder()
sb.Append(String.Format("{0,6} {1,15}\n\n", "Year", "Population")) |> ignore
for i = 0 to years.Length - 1 do
   sb.Append(String.Format("{0,6} {1,15:N0}\n", years[i], population[i])) |> ignore

printfn $"{sb}"

// Result:
//      Year      Population
//
//      2013       1,025,632
//      2014       1,105,967
//      2015       1,148,203
Dim years() As Integer = {2013, 2014, 2015}
Dim population() As Integer = {1025632, 1105967, 1148203}
Dim sb As New StringBuilder()
sb.Append(String.Format("{0,6} {1,15}{2}{2}",
                       "Year", "Population", vbCrLf))
For index As Integer = 0 To years.Length - 1
    sb.AppendFormat("{0,6} {1,15:N0}{2}",
                  years(index), population(index), vbCrLf)
Next
' Result:
'      Year      Population
'
'      2013       1,025,632
'      2014       1,105,967
'      2015       1,148,203

Alineación del control

De forma predeterminada, las cadenas están alineadas a la derecha dentro de su campo si se especifica un ancho de campo. Para alinear las cadenas a la izquierda en un campo, se antepone el ancho del campo con un signo negativo, como {0,-12} para definir un campo alineado a la izquierda de 12 caracteres.

El ejemplo siguiente es similar al anterior, salvo que alinea las etiquetas y los datos a la izquierda.

int[] years = { 2013, 2014, 2015 };
int[] population = { 1025632, 1105967, 1148203 };
String s = String.Format("{0,-10} {1,-10}\n\n", "Year", "Population");
for (int index = 0; index < years.Length; index++)
    s += String.Format("{0,-10} {1,-10:N0}\n",
                       years[index], population[index]);
Console.WriteLine($"\n{s}");
// Result:
//    Year       Population
//
//    2013       1,025,632
//    2014       1,105,967
//    2015       1,148,203
let years = [| 2013; 2014; 2015 |]
let population = [| 1025632; 1105967; 1148203 |]
let mutable s = String.Format("{0,-10} {1,-10}\n\n", "Year", "Population")
for i = 0 to years.Length - 1 do
    s <- s + String.Format("{0,-10} {1,-10:N0}\n", years[i], population[i])
printfn $"\n{s}"
// Result:
//    Year       Population
//
//    2013       1,025,632
//    2014       1,105,967
//    2015       1,148,203
Dim years() As Integer = {2013, 2014, 2015}
Dim population() As Integer = {1025632, 1105967, 1148203}
Dim s As String = String.Format("{0,-10} {1,-10}{2}{2}",
                               "Year", "Population", vbCrLf)
For index As Integer = 0 To years.Length - 1
    s += String.Format("{0,-10} {1,-10:N0}{2}",
                     years(index), population(index), vbCrLf)
Next
' Result:
'    Year       Population
'
'    2013       1,025,632
'    2014       1,105,967
'    2015       1,148,203

String.Format hace uso de la característica de formato compuesto. Para obtener más información, consulte Formatos compuestos.

¿A qué método se llama?

En Call
Dar formato a uno o varios objetos mediante las convenciones de la referencia cultural actual. Excepto las sobrecargas que incluyen un provider parámetro, las sobrecargas restantes Format incluyen un String parámetro seguido de uno o varios parámetros de objeto. Por este motivo, no tiene que determinar qué Format sobrecarga quiere llamar. El compilador de lenguaje selecciona la sobrecarga adecuada entre las sobrecargas que no tienen un provider parámetro, en función de la lista de argumentos. Por ejemplo, si la lista de argumentos tiene cinco argumentos, el compilador llama al Format(String, Object[]) método .
Dar formato a uno o varios objetos mediante las convenciones de una referencia cultural específica. Cada Format sobrecarga que comienza con un provider parámetro va seguida de un String parámetro y uno o varios parámetros de objeto. Por este motivo, no es necesario determinar a qué sobrecarga específica Format quiere llamar. El compilador de lenguaje selecciona la sobrecarga adecuada entre las sobrecargas que tienen un provider parámetro, en función de la lista de argumentos. Por ejemplo, si la lista de argumentos tiene cinco argumentos, el compilador llama al Format(IFormatProvider, String, Object[]) método .
Realice una operación de formato personalizada con una ICustomFormatter implementación o una IFormattable implementación. Cualquiera de las cuatro sobrecargas con un provider parámetro . El compilador selecciona la sobrecarga adecuada entre las sobrecargas que tienen un provider parámetro, en función de la lista de argumentos.

El método Format en breve

Cada sobrecarga del Format método usa la característica de formato compuesto para incluir marcadores de posición indexados de base cero, denominados elementos de formato, en una cadena de formato compuesto. En tiempo de ejecución, cada elemento de formato se reemplaza por la representación de cadena del argumento correspondiente en una lista de parámetros. Si el valor del argumento es null, el elemento de formato se reemplaza por String.Empty. Por ejemplo, la siguiente llamada al Format(String, Object, Object, Object) método incluye una cadena de formato con tres elementos de formato, {0}, {1}y {2}una lista de argumentos con tres elementos.

DateTime dat = new DateTime(2012, 1, 17, 9, 30, 0); 
string city = "Chicago";
int temp = -16;
string output = String.Format("At {0} in {1}, the temperature was {2} degrees.",
                              dat, city, temp);
Console.WriteLine(output);
// The example displays output like the following:
//    At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.
open System

let dat = DateTime(2012, 1, 17, 9, 30, 0) 
let city = "Chicago"
let temp = -16
String.Format("At {0} in {1}, the temperature was {2} degrees.", dat, city, temp)
|> printfn "%s"
// The example displays output like the following:
//    At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.
Dim dat As Date = #1/17/2012 9:30AM#
Dim city As String = "Chicago"
Dim temp As Integer = -16
Dim output As String = String.Format("At {0} in {1}, the temperature was {2} degrees.",
                                   dat, city, temp)
Console.WriteLine(output)
' The example displays the following output:
'    At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.

Elemento de formato

Un elemento de formato tiene esta sintaxis:

{index[,alignment][:formatString]}

Los corchetes indican elementos opcionales. Se requieren llaves de apertura y cierre. (Para incluir una llave de apertura o cierre literal en la cadena de formato, vea el Sección Escape de llaves en el artículo Formato compuesto).

Por ejemplo, un elemento de formato para dar formato a un valor de moneda podría aparecer de la siguiente manera:

var value = String.Format("{0,-10:C}", 126347.89m);
Console.WriteLine(value);
open System

String.Format("{0,-10:C}", 126347.89m)         
|> printfn "%s"
String.Format("{0,-10:C}", 126347.89D)

Un elemento de formato tiene los siguientes elementos:

índice
Índice de base cero del argumento cuya representación de cadena se va a incluir en esta posición de la cadena. Si este argumento es null, se incluirá una cadena vacía en esta posición de la cadena.

alignment
Opcional. Entero con signo que indica la longitud total del campo en el que se inserta el argumento y si está alineado a la derecha (un entero positivo) o alineado a la izquierda (un entero negativo). Si omite la alineación, la representación de cadena del argumento correspondiente se inserta en un campo sin espacios iniciales o finales.

Si el valor de alineación es menor que la longitud del argumento que se va a insertar, se omite la alineación y la longitud de la representación de cadena del argumento se usa como ancho de campo.

formatString
Opcional. Cadena que especifica el formato de la cadena de resultado del argumento correspondiente. Si omite formatString, se llama al método sin ToString parámetros del argumento correspondiente para generar su representación de cadena. Si especifica formatString, el argumento al que hace referencia el elemento de formato debe implementar la IFormattable interfaz. Los tipos que admiten cadenas de formato incluyen:

Sin embargo, tenga en cuenta que cualquier tipo personalizado puede implementar IFormattable o ampliar la implementación de IFormattable un tipo existente.

En el ejemplo siguiente se usan los alignment argumentos y formatString para generar una salida con formato.

// Create array of 5-tuples with population data for three U.S. cities, 1940-1950.
Tuple<string, DateTime, int, DateTime, int>[] cities = 
    { Tuple.Create("Los Angeles", new DateTime(1940, 1, 1), 1504277, 
                   new DateTime(1950, 1, 1), 1970358),
      Tuple.Create("New York", new DateTime(1940, 1, 1), 7454995, 
                   new DateTime(1950, 1, 1), 7891957),  
      Tuple.Create("Chicago", new DateTime(1940, 1, 1), 3396808, 
                   new DateTime(1950, 1, 1), 3620962),  
      Tuple.Create("Detroit", new DateTime(1940, 1, 1), 1623452, 
                   new DateTime(1950, 1, 1), 1849568) };

// Display header
var header = String.Format("{0,-12}{1,8}{2,12}{1,8}{2,12}{3,14}\n",
                              "City", "Year", "Population", "Change (%)");
Console.WriteLine(header);
foreach (var city in cities) {
   var output = String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}{5,14:P1}",
                          city.Item1, city.Item2, city.Item3, city.Item4, city.Item5,
                          (city.Item5 - city.Item3)/ (double)city.Item3);
   Console.WriteLine(output);
}
// The example displays the following output:
//    City            Year  Population    Year  Population    Change (%)
//  
//    Los Angeles     1940   1,504,277    1950   1,970,358        31.0 %
//    New York        1940   7,454,995    1950   7,891,957         5.9 %
//    Chicago         1940   3,396,808    1950   3,620,962         6.6 %
//    Detroit         1940   1,623,452    1950   1,849,568        13.9 %
// Create a list of 5-tuples with population data for three U.S. cities, 1940-1950.
let cities = 
    [ "Los Angeles", DateTime(1940, 1, 1), 1504277, DateTime(1950, 1, 1), 1970358
      "New York", DateTime(1940, 1, 1), 7454995, DateTime(1950, 1, 1), 7891957
      "Chicago", DateTime(1940, 1, 1), 3396808, DateTime(1950, 1, 1), 3620962
      "Detroit", DateTime(1940, 1, 1), 1623452, DateTime(1950, 1, 1), 1849568 ]

// Display header
String.Format("{0,-12}{1,8}{2,12}{1,8}{2,12}{3,14}\n", "City", "Year", "Population", "Change (%)")
|> printfn "%s"

for name, year1, pop1, year2, pop2 in cities do
    String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}{5,14:P1}",
                  name, year1, pop1, year2, pop2,
                  double (pop2 - pop1) / double pop1)
    |> printfn "%s"
// The example displays the following output:
//    City            Year  Population    Year  Population    Change (%)
//  
//    Los Angeles     1940   1,504,277    1950   1,970,358        31.0 %
//    New York        1940   7,454,995    1950   7,891,957         5.9 %
//    Chicago         1940   3,396,808    1950   3,620,962         6.6 %
//    Detroit         1940   1,623,452    1950   1,849,568        13.9 %
Module Example3
    Public Sub Main()
        ' Create array of 5-tuples with population data for three U.S. cities, 1940-1950.
        Dim cities() =
          {Tuple.Create("Los Angeles", #1/1/1940#, 1504277, #1/1/1950#, 1970358),
            Tuple.Create("New York", #1/1/1940#, 7454995, #1/1/1950#, 7891957),
            Tuple.Create("Chicago", #1/1/1940#, 3396808, #1/1/1950#, 3620962),
            Tuple.Create("Detroit", #1/1/1940#, 1623452, #1/1/1950#, 1849568)}

        ' Display header
        Dim header As String = String.Format("{0,-12}{1,8}{2,12}{1,8}{2,12}{3,14}",
                                           "City", "Year", "Population", "Change (%)")
        Console.WriteLine(header)
        Console.WriteLine()
        For Each city In cities
            Dim output = String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}{5,14:P1}",
                                city.Item1, city.Item2, city.Item3, city.Item4, city.Item5,
                                (city.Item5 - city.Item3) / city.Item3)
            Console.WriteLine(output)
        Next
    End Sub
End Module
' The example displays the following output:
'    City            Year  Population    Year  Population    Change (%)
'    
'    Los Angeles     1940   1,504,277    1950   1,970,358        31.0 %
'    New York        1940   7,454,995    1950   7,891,957         5.9 %
'    Chicago         1940   3,396,808    1950   3,620,962         6.6 %
'    Detroit         1940   1,623,452    1950   1,849,568        13.9 %

Cómo se da formato a los argumentos

Los elementos de formato se procesan secuencialmente desde el principio de la cadena. Cada elemento de formato tiene un índice que corresponde a un objeto de la lista de argumentos del método. El Format método recupera el argumento y deriva su representación de cadena de la siguiente manera:

Para obtener un ejemplo que intercepta las llamadas al ICustomFormatter.Format método y permite ver qué información pasa el Format método a un método de formato para cada elemento de formato en una cadena de formato compuesto, vea Ejemplo: proveedor de interceptación y formateador de números romanos.

Para obtener más información, consulte Procesamiento del pedido.

Dar formato a los elementos que tienen el mismo índice

El Format método produce una FormatException excepción si el índice de un elemento de índice es mayor o igual que el número de argumentos de la lista de argumentos. Sin embargo, format puede incluir más elementos de formato que los argumentos, siempre y cuando varios elementos de formato tengan el mismo índice. En la llamada al método en el Format(String, Object) ejemplo siguiente, la lista de argumentos tiene un único argumento, pero la cadena de formato incluye dos elementos de formato: uno muestra el valor decimal de un número y el otro muestra su valor hexadecimal.

short[] values= { Int16.MinValue, -27, 0, 1042, Int16.MaxValue };
Console.WriteLine("{0,10}  {1,10}\n", "Decimal", "Hex");
foreach (short value in values)
{
   string formatString = String.Format("{0,10:G}: {0,10:X}", value);
   Console.WriteLine(formatString);
}   
// The example displays the following output:
//       Decimal         Hex
//    
//        -32768:       8000
//           -27:       FFE5
//             0:          0
//          1042:        412
//         32767:       7FFF
open System

let values= [| Int16.MinValue; -27s; 0s; 1042s; Int16.MaxValue |]
printfn "%10s  %10s\n" "Decimal" "Hex"
for value in values do
    String.Format("{0,10:G}: {0,10:X}", value)
    |> printfn "%s"
// The example displays the following output:
//       Decimal         Hex
//    
//        -32768:       8000
//           -27:       FFE5
//             0:          0
//          1042:        412
//         32767:       7FFF
Module Example1
    Public Sub Main()
        Dim values() As Short = {Int16.MinValue, -27, 0, 1042, Int16.MaxValue}
        Console.WriteLine("{0,10}  {1,10}", "Decimal", "Hex")
        Console.WriteLine()
        For Each value As Short In values
            Dim formatString As String = String.Format("{0,10:G}: {0,10:X}", value)
            Console.WriteLine(formatString)
        Next
    End Sub
End Module
' The example displays the following output:
'       Decimal         Hex
'    
'        -32768:       8000
'           -27:       FFE5
'             0:          0
'          1042:        412
'         32767:       7FFF

Formato y referencia cultural

Por lo general, los objetos de la lista de argumentos se convierten en sus representaciones de cadena mediante las convenciones de la referencia cultural actual, que devuelve la CultureInfo.CurrentCulture propiedad . Puede controlar este comportamiento llamando a una de las sobrecargas de Format que incluye un provider parámetro . El provider parámetro es una IFormatProvider implementación que proporciona información de formato personalizada y específica de la referencia cultural que se usa para moderar el proceso de formato.

La IFormatProvider interfaz tiene un único miembro, , GetFormatque es responsable de devolver el objeto que proporciona información de formato. .NET tiene tres IFormatProvider implementaciones que proporcionan formato específico de la referencia cultural:

  • CultureInfo. Su GetFormat método devuelve un objeto específico NumberFormatInfo de la referencia cultural para dar formato a valores numéricos y un objeto específico DateTimeFormatInfo de la referencia cultural para dar formato a los valores de fecha y hora.
  • DateTimeFormatInfo, que se usa para el formato específico de la referencia cultural de valores de fecha y hora. Su GetFormat método se devuelve a sí mismo.
  • NumberFormatInfo, que se usa para el formato específico de la referencia cultural de valores numéricos. Su GetFormat(Type) método se devuelve a sí mismo.

Operaciones de formato personalizado

También puede llamar a cualquiera de las sobrecargas del Format método que tienen un provider parámetro de tipo IFormatProvider para realizar operaciones de formato personalizadas. Por ejemplo, podría dar formato a un entero como un número de identificación o como un número de teléfono. Para realizar el formato personalizado, el provider argumento debe implementar las IFormatProvider interfaces y ICustomFormatter . Cuando el Format método se pasa una ICustomFormatter implementación como provider argumento, el Format método llama a su IFormatProvider.GetFormat implementación y solicita un objeto de tipo ICustomFormatter. A continuación, llama al método del Format objeto devuelto ICustomFormatter para dar formato a cada elemento de formato de la cadena compuesta que se le pasa.

Para obtener más información sobre cómo proporcionar soluciones de formato personalizadas, vea Cómo: Definir y usar proveedores de formato numérico personalizado y ICustomFormatter. Para obtener un ejemplo que convierte enteros en números personalizados con formato, vea Ejemplo: una operación de formato personalizado. Para obtener un ejemplo que convierte bytes sin signo en números romanos, vea Ejemplo: proveedor de interceptación y formateador de números romanos.

Ejemplo: una operación de formato personalizada

En este ejemplo se define un proveedor de formato que da formato a un valor entero como un número de cuenta de cliente con el formato x-xxxxx-xx.

using System;

public class TestFormatter
{
   public static void Main()
   {
      int acctNumber = 79203159;
      Console.WriteLine(String.Format(new CustomerFormatter(), "{0}", acctNumber));
      Console.WriteLine(String.Format(new CustomerFormatter(), "{0:G}", acctNumber));
      Console.WriteLine(String.Format(new CustomerFormatter(), "{0:S}", acctNumber));
      Console.WriteLine(String.Format(new CustomerFormatter(), "{0:P}", acctNumber));
      try {
         Console.WriteLine(String.Format(new CustomerFormatter(), "{0:X}", acctNumber));
      }
      catch (FormatException e) {
         Console.WriteLine(e.Message);
      }
   }
}

public class CustomerFormatter : 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) 
   {                       
      if (! this.Equals(formatProvider))
      {
         return null;
      }
      else
      {
         if (String.IsNullOrEmpty(format)) 
            format = "G";
         
         string customerString = arg.ToString();
         if (customerString.Length < 8)
            customerString = customerString.PadLeft(8, '0');
         
         format = format.ToUpper();
         switch (format)
         {
            case "G":
               return customerString.Substring(0, 1) + "-" +
                                     customerString.Substring(1, 5) + "-" +
                                     customerString.Substring(6);
            case "S":                          
               return customerString.Substring(0, 1) + "/" +
                                     customerString.Substring(1, 5) + "/" +
                                     customerString.Substring(6);
            case "P":                          
               return customerString.Substring(0, 1) + "." +
                                     customerString.Substring(1, 5) + "." +
                                     customerString.Substring(6);
            default:
               throw new FormatException( 
                         String.Format("The '{0}' format specifier is not supported.", format));
         }
      }   
   }
}
// The example displays the following output:
//       7-92031-59
//       7-92031-59
//       7/92031/59
//       7.92031.59
//       The 'X' format specifier is not supported.
open System

type CustomerFormatter() = 
    interface IFormatProvider with
        member this.GetFormat(formatType) =
            if formatType = typeof<ICustomFormatter> then
                this
            else
                null

    interface ICustomFormatter with
        member this.Format(format, arg, formatProvider: IFormatProvider) = 
            if this.Equals formatProvider |> not then
                null
            else
                let format = 
                    if String.IsNullOrEmpty format then "G"
                    else format.ToUpper()
                
                let customerString = 
                    let s = string arg
                    if s.Length < 8 then
                        s.PadLeft(8, '0')
                    else s
                
                match format with
                | "G" ->
                    customerString.Substring(0, 1) + "-" +
                        customerString.Substring(1, 5) + "-" +
                        customerString.Substring 6
                | "S" ->                          
                    customerString.Substring(0, 1) + "/" +
                        customerString.Substring(1, 5) + "/" +
                        customerString.Substring 6
                | "P" ->                          
                    customerString.Substring(0, 1) + "." +
                        customerString.Substring(1, 5) + "." +
                        customerString.Substring 6
                | _ ->
                    raise (FormatException $"The '{format}' format specifier is not supported.")

let acctNumber = 79203159
String.Format(CustomerFormatter(), "{0}", acctNumber)
|> printfn "%s"
String.Format(CustomerFormatter(), "{0:G}", acctNumber)
|> printfn "%s"
String.Format(CustomerFormatter(), "{0:S}", acctNumber)
|> printfn "%s"
String.Format(CustomerFormatter(), "{0:P}", acctNumber)
|> printfn "%s"
try
    String.Format(CustomerFormatter(), "{0:X}", acctNumber)
    |> printfn "%s"
with :? FormatException as e ->
    printfn $"{e.Message}"

// The example displays the following output:
//       7-92031-59
//       7-92031-59
//       7/92031/59
//       7.92031.59
//       The 'X' format specifier is not supported.
Module TestFormatter
   Public Sub Main()
      Dim acctNumber As Integer = 79203159
      Console.WriteLine(String.Format(New CustomerFormatter, "{0}", acctNumber))
      Console.WriteLine(String.Format(New CustomerFormatter, "{0:G}", acctNumber))
      Console.WriteLine(String.Format(New CustomerFormatter, "{0:S}", acctNumber))
      Console.WriteLine(String.Format(New CustomerFormatter, "{0:P}", acctNumber))
      Try
         Console.WriteLine(String.Format(New CustomerFormatter, "{0:X}", acctNumber))
      Catch e As FormatException
         Console.WriteLine(e.Message)
      End Try   
   End Sub
End Module

Public Class CustomerFormatter : Implements IFormatProvider, ICustomFormatter
   Public Function GetFormat(type As Type) As Object  _
                   Implements IFormatProvider.GetFormat
      If type 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
      If Not Me.Equals(formatProvider) Then
         Return Nothing
      Else
         If String.IsNullOrEmpty(fmt) Then fmt = "G"
         
         Dim customerString As String = arg.ToString()
         if customerString.Length < 8 Then _
            customerString = customerString.PadLeft(8, "0"c)
         
         Select Case fmt
            Case "G"
               Return customerString.Substring(0, 1) & "-" & _
                                     customerString.Substring(1, 5) & "-" & _
                                     customerString.Substring(6)
            Case "S"                         
               Return customerString.Substring(0, 1) & "/" & _
                                     customerString.Substring(1, 5) & "/" & _
                                     customerString.Substring(6)
            Case "P"
               Return customerString.Substring(0, 1) & "." & _
                                     customerString.Substring(1, 5) & "." & _
                                     customerString.Substring(6)
            Case Else
               Throw New FormatException( _
                         String.Format("The '{0}' format specifier is not supported.", fmt))
         End Select                                                     
      End If   
   End Function
End Class
' The example displays the following output:
'       7-92031-59
'       7-92031-59
'       7/92031/59
'       7.92031.59
'       The 'X' format specifier is not supported.

Ejemplo: proveedor de interceptación y formateador de números romanos

En este ejemplo se define un proveedor de formato personalizado que implementa las ICustomFormatter interfaces y IFormatProvider para hacer dos cosas:

  • Muestra los parámetros pasados a su ICustomFormatter.Format implementación. Esto nos permite ver qué parámetros está pasando el Format(IFormatProvider, String, Object[]) método a la implementación de formato personalizado para cada objeto que intenta dar formato. Esto puede ser útil al depurar la aplicación.

  • Si el objeto al que se va a dar formato es un valor de byte sin signo al que se va a dar formato mediante la cadena de formato estándar "R", el formateador personalizado da formato al valor numérico como un número romano.

using System;
using System.Globalization;

public class InterceptProvider : IFormatProvider, ICustomFormatter
{
   public object GetFormat(Type formatType)
   {
      if (formatType == typeof(ICustomFormatter))
         return this;
      else
         return null;
   }
   
   public string Format(String format, Object obj, IFormatProvider provider) 
   {
      // Display information about method call.
      string formatString = format ?? "<null>";
      Console.WriteLine("Provider: {0}, Object: {1}, Format String: {2}",
                        provider.GetType().Name, obj ?? "<null>", formatString);
                        
      if (obj == null) return String.Empty;
            
      // If this is a byte and the "R" format string, format it with Roman numerals.
      if (obj is Byte && formatString.ToUpper().Equals("R")) {
         Byte value = (Byte) obj;
         int remainder;
         int result;
         String returnString = String.Empty;

         // Get the hundreds digit(s)
         result = Math.DivRem(value, 100, out remainder);
         if (result > 0)  
            returnString = new String('C', result);
         value = (Byte) remainder;
         // Get the 50s digit
         result = Math.DivRem(value, 50, out remainder);
         if (result == 1)
            returnString += "L";
         value = (Byte) remainder;
         // Get the tens digit.
         result = Math.DivRem(value, 10, out remainder);
         if (result > 0)
            returnString += new String('X', result);
         value = (Byte) remainder; 
         // Get the fives digit.
         result = Math.DivRem(value, 5, out remainder);
         if (result > 0)
            returnString += "V";
         value = (Byte) remainder;
         // Add the ones digit.
         if (remainder > 0) 
            returnString += new String('I', remainder);
         
         // Check whether we have too many X characters.
         int pos = returnString.IndexOf("XXXX");
         if (pos >= 0) {
            int xPos = returnString.IndexOf("L"); 
            if (xPos >= 0 & xPos == pos - 1)
               returnString = returnString.Replace("LXXXX", "XC");
            else
               returnString = returnString.Replace("XXXX", "XL");   
         }
         // Check whether we have too many I characters
         pos = returnString.IndexOf("IIII");
         if (pos >= 0)
            if (returnString.IndexOf("V") >= 0)
               returnString = returnString.Replace("VIIII", "IX");
            else
               returnString = returnString.Replace("IIII", "IV");    

         return returnString; 
      }   

      // Use default for all other formatting.
      if (obj is IFormattable)
         return ((IFormattable) obj).ToString(format, CultureInfo.CurrentCulture);
      else
         return obj.ToString();
   }
}

public class Example
{
   public static void Main()
   {
      int n = 10;
      double value = 16.935;
      DateTime day = DateTime.Now;
      InterceptProvider provider = new InterceptProvider();
      Console.WriteLine(String.Format(provider, "{0:N0}: {1:C2} on {2:d}\n", n, value, day));
      Console.WriteLine(String.Format(provider, "{0}: {1:F}\n", "Today: ", 
                                      (DayOfWeek) DateTime.Now.DayOfWeek));
      Console.WriteLine(String.Format(provider, "{0:X}, {1}, {2}\n", 
                                      (Byte) 2, (Byte) 12, (Byte) 199));
      Console.WriteLine(String.Format(provider, "{0:R}, {1:R}, {2:R}\n", 
                                      (Byte) 2, (Byte) 12, (Byte) 199));
   }
}
// The example displays the following output:
//    Provider: InterceptProvider, Object: 10, Format String: N0
//    Provider: InterceptProvider, Object: 16.935, Format String: C2
//    Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format String: d
//    10: $16.94 on 1/31/2013
//    
//    Provider: InterceptProvider, Object: Today: , Format String: <null>
//    Provider: InterceptProvider, Object: Thursday, Format String: F
//    Today: : Thursday
//    
//    Provider: InterceptProvider, Object: 2, Format String: X
//    Provider: InterceptProvider, Object: 12, Format String: <null>
//    Provider: InterceptProvider, Object: 199, Format String: <null>
//    2, 12, 199
//    
//    Provider: InterceptProvider, Object: 2, Format String: R
//    Provider: InterceptProvider, Object: 12, Format String: R
//    Provider: InterceptProvider, Object: 199, Format String: R
//    II, XII, CXCIX
open System
open System.Globalization

type InterceptProvider() =
    interface IFormatProvider with
        member this.GetFormat(formatType) =
            if formatType = typeof<ICustomFormatter> then
                this
            else
                null
    interface ICustomFormatter with
        member _.Format(format, obj, provider: IFormatProvider) = 
            // Display information about method call.
            let formatString =
                if format = null then "<null>" else format
            printfn $"Provider: {provider.GetType().Name}, Object: %A{obj}, Format String: %s{formatString}"
                                
            if obj = null then
                String.Empty
            else
                // If this is a byte and the "R" format string, format it with Roman numerals.
                match obj with
                | :? byte as value when formatString.ToUpper().Equals "R" -> 
                    let mutable returnString = String.Empty

                    // Get the hundreds digit(s)
                    let struct (result, remainder) = Math.DivRem(value, 100uy)
                    if result > 0uy then
                        returnString <- String('C', int result)
                    let value = byte remainder
                    // Get the 50s digit
                    let struct (result, remainder) = Math.DivRem(value, 50uy)
                    if result = 1uy then
                        returnString <- returnString + "L"
                    let value = byte remainder
                    // Get the tens digit.
                    let struct (result, remainder) = Math.DivRem(value, 10uy)
                    if result > 0uy then
                        returnString <- returnString + String('X', int result)
                    let value = byte remainder 
                    // Get the fives digit.
                    let struct (result, remainder) = Math.DivRem(value, 5uy)
                    if result > 0uy then
                        returnString <- returnString + "V"
                    let value = byte remainder
                    // Add the ones digit.
                    if remainder > 0uy then 
                        returnString <- returnString + String('I', int remainder)
                    
                    // Check whether we have too many X characters.
                    let pos = returnString.IndexOf "XXXX"
                    if pos >= 0 then
                        let xPos = returnString.IndexOf "L" 
                        returnString <-
                            if xPos >= 0 && xPos = pos - 1 then
                                returnString.Replace("LXXXX", "XC")
                            else
                                returnString.Replace("XXXX", "XL")   
                    // Check whether we have too many I characters
                    let pos = returnString.IndexOf "IIII"
                    if pos >= 0 then
                        returnString <-
                            if returnString.IndexOf "V" >= 0 then
                                returnString.Replace("VIIII", "IX")
                            else
                                returnString.Replace("IIII", "IV")    
                    returnString 

                // Use default for all other formatting.
                | :? IFormattable as x ->
                    x.ToString(format, CultureInfo.CurrentCulture)
                | _ ->
                    string obj

let n = 10
let value = 16.935
let day = DateTime.Now
let provider = InterceptProvider()
String.Format(provider, "{0:N0}: {1:C2} on {2:d}\n", n, value, day)
|> printfn "%s"
String.Format(provider, "{0}: {1:F}\n", "Today: ", DateTime.Now.DayOfWeek)
|> printfn "%s"
String.Format(provider, "{0:X}, {1}, {2}\n", 2uy, 12uy, 199uy)
|> printfn "%s"
String.Format(provider, "{0:R}, {1:R}, {2:R}\n", 2uy, 12uy, 199uy)
|> printfn "%s"
// The example displays the following output:
//    Provider: InterceptProvider, Object: 10, Format String: N0
//    Provider: InterceptProvider, Object: 16.935, Format String: C2
//    Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format String: d
//    10: $16.94 on 1/31/2013
//    
//    Provider: InterceptProvider, Object: Today: , Format String: <null>
//    Provider: InterceptProvider, Object: Thursday, Format String: F
//    Today: : Thursday
//    
//    Provider: InterceptProvider, Object: 2, Format String: X
//    Provider: InterceptProvider, Object: 12, Format String: <null>
//    Provider: InterceptProvider, Object: 199, Format String: <null>
//    2, 12, 199
//    
//    Provider: InterceptProvider, Object: 2, Format String: R
//    Provider: InterceptProvider, Object: 12, Format String: R
//    Provider: InterceptProvider, Object: 199, Format String: R
//    II, XII, CXCIX
Imports System.Globalization

Public Class InterceptProvider : 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, obj As Object, provider As IFormatProvider) As String _
         Implements ICustomFormatter.Format

      Dim formatString As String = If(fmt IsNot Nothing, fmt, "<null>")
      Console.WriteLine("Provider: {0}, Object: {1}, Format String: {2}",
                        provider, If(obj IsNot Nothing, obj, "<null>"), formatString)

      If obj Is Nothing Then Return String.Empty
            
      ' If this is a byte and the "R" format string, format it with Roman numerals.
      If TypeOf(obj) Is Byte AndAlso formatString.ToUpper.Equals("R") Then
         Dim value As Byte = CByte(obj)
         Dim remainder As Integer
         Dim result As Integer
         Dim returnString As String = String.Empty

         ' Get the hundreds digit(s)
         result = Math.DivRem(value, 100, remainder)
         If result > 0 Then returnString = New String("C"c, result)
         value = CByte(remainder)
         ' Get the 50s digit
         result = Math.DivRem(value, 50, remainder)
         If result = 1 Then returnString += "L"
         value = CByte(remainder)
         ' Get the tens digit.
         result = Math.DivRem(value, 10, remainder)
         If result > 0 Then returnString += New String("X"c, result)
         value = CByte(remainder) 
         ' Get the fives digit.
         result = Math.DivRem(value, 5, remainder)
         If result > 0 Then returnString += "V"
         value = CByte(remainder)
         ' Add the ones digit.
         If remainder > 0 Then returnString += New String("I"c, remainder)
         
         ' Check whether we have too many X characters.
         Dim pos As Integer = returnString.IndexOf("XXXX")
         If pos >= 0 Then
            Dim xPos As Integer = returnString.IndexOf("L") 
            If xPos >= 0 And xPos = pos - 1 Then
               returnString = returnString.Replace("LXXXX", "XC")
            Else
               returnString = returnString.Replace("XXXX", "XL")   
            End If         
         End If
         ' Check whether we have too many I characters
         pos = returnString.IndexOf("IIII")
         If pos >= 0 Then
            If returnString.IndexOf("V") >= 0 Then
               returnString = returnString.Replace("VIIII", "IX")
            Else
               returnString = returnString.Replace("IIII", "IV")    
            End If
         End If
         Return returnString 
      End If   

      ' Use default for all other formatting.
      If obj Is GetType(IFormattable)
         Return CType(obj, IFormattable).ToString(fmt, CultureInfo.CurrentCulture)
      Else
         Return obj.ToString()
      End If
   End Function
End Class

Module Example
   Public Sub Main()
      Dim n As Integer = 10
      Dim value As Double = 16.935
      Dim day As DateTime = Date.Now
      Dim provider As New InterceptProvider()
      Console.WriteLine(String.Format(provider, "{0:N0}: {1:C2} on {2:d}", n, value, day))
      Console.WriteLine()
      Console.WriteLine(String.Format(provider, "{0}: {1:F}", "Today", 
                                      CType(Date.Now.DayOfWeek, DayOfWeek)))
      Console.WriteLine()
      Console.WriteLine(String.Format(provider, "{0:X}, {1}, {2}\n", 
                                      CByte(2), CByte(12), CByte(199)))
      Console.WriteLine()
      Console.WriteLine(String.Format(provider, "{0:R}, {1:R}, {2:R}", 
                                      CByte(2), CByte(12), CByte(199)))
   End Sub
End Module
' The example displays the following output:
'    Provider: InterceptProvider, Object: 10, Format String: N0
'    Provider: InterceptProvider, Object: 16.935, Format String: C2
'    Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format String: d
'    10: $16.94 on 1/31/2013
'    
'    Provider: InterceptProvider, Object: Today: , Format String: <null>
'    Provider: InterceptProvider, Object: Thursday, Format String: F
'    Today: : Thursday
'    
'    Provider: InterceptProvider, Object: 2, Format String: X
'    Provider: InterceptProvider, Object: 12, Format String: <null>
'    Provider: InterceptProvider, Object: 199, Format String: <null>
'    2, 12, 199
'    
'    Provider: InterceptProvider, Object: 2, Format String: R
'    Provider: InterceptProvider, Object: 12, Format String: R
'    Provider: InterceptProvider, Object: 199, Format String: R
'    II, XII, CXCIX

Preguntas más frecuentes

¿Por qué se recomienda la interpolación de cadenas a través de llamadas al String.Format método?

La interpolación de cadenas es:

  • Más flexible. Se puede usar en cualquier cadena sin necesidad de llamar a un método que admita el formato compuesto. De lo contrario, debe llamar al Format método u otro método que admita el formato compuesto, como Console.WriteLine o StringBuilder.AppendFormat.

  • Más legible. Dado que la expresión que se va a insertar en una cadena aparece en la expresión interpolada en lugar de en una lista de argumentos, las cadenas interpoladas son mucho más fáciles de codificar y leer. Debido a su mayor legibilidad, las cadenas interpoladas pueden reemplazar no solo las llamadas a métodos de formato compuesto, sino que también se pueden usar en operaciones de concatenación de cadenas para generar código más conciso y más claro.

Una comparación de los dos ejemplos de código siguientes muestra la superioridad de las cadenas interpoladas sobre la concatenación de cadenas y las llamadas a métodos de formato compuestos. El uso de varias operaciones de concatenación de cadenas en el ejemplo siguiente genera código detallado y de lectura difícil.

string[] names = { "Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma" };
string output = names[0] + ", " + names[1] + ", " + names[2] + ", " +
                names[3] + ", " + names[4] + ", " + names[5] + ", " +
                names[6];

output += "\n";
var date = DateTime.Now;
output += String.Format("It is {0:t} on {0:d}. The day of the week is {1}.",
                        date, date.DayOfWeek);
Console.WriteLine(output);
// The example displays the following output:
//     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
//     It is 10:29 AM on 1/8/2018. The day of the week is Monday.
open System

let names = [| "Balto"; "Vanya"; "Dakota"; "Samuel"; "Koani"; "Yiska"; "Yuma" |]
let output = 
    names[0] + ", " + names[1] + ", " + names[2] + ", " + 
    names[3] + ", " + names[4] + ", " + names[5] + ", " + 
    names[6] + "\n"

let date = DateTime.Now
output + String.Format("It is {0:t} on {0:d}. The day of the week is {1}.", date, date.DayOfWeek)
|> printfn "%s"
// The example displays the following output:
//     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
//     It is 10:29 AM on 1/8/2018. The day of the week is Monday.

Module Example12
    Public Sub Main()
        Dim names = {"Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma"}
        Dim output = names(0) + ", " + names(1) + ", " + names(2) + ", " +
                   names(3) + ", " + names(4) + ", " + names(5) + ", " +
                   names(6)

        output += vbCrLf
        Dim dat = DateTime.Now
        output += String.Format("It is {0:t} on {0:d}. The day of the week is {1}.",
                              dat, dat.DayOfWeek)
        Console.WriteLine(output)
    End Sub
End Module
' The example displays the following output:
'     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
'     It is 10:29 AM on 1/8/2018. The day of the week is Monday.

En cambio, el uso de cadenas interpoladas en el ejemplo siguiente genera código mucho más claro y conciso que la instrucción de concatenación de cadenas y la llamada al Format método en el ejemplo anterior.

string[] names = { "Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma" };
string output = $"{names[0]}, {names[1]}, {names[2]}, {names[3]}, {names[4]}, " +
                $"{names[5]}, {names[6]}";

var date = DateTime.Now;
output += $"\nIt is {date:t} on {date:d}. The day of the week is {date.DayOfWeek}.";
Console.WriteLine(output);
// The example displays the following output:
//     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
//     It is 10:29 AM on 1/8/2018. The day of the week is Monday.
open System

let names = [| "Balto"; "Vanya"; "Dakota"; "Samuel"; "Koani"; "Yiska"; "Yuma" |]
let output = $"{names[0]}, {names[1]}, {names[2]}, {names[3]}, {names[4]}, {names[5]}, {names[6]}"  

let date = DateTime.Now
output + $"\nIt is {date:t} on {date:d}. The day of the week is {date.DayOfWeek}."
|> printfn "%s" 
// The example displays the following output:
//     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
//     It is 10:29 AM on 1/8/2018. The day of the week is Monday.

Module Example13
    Public Sub Main()
        Dim names = {"Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma"}
        Dim output = $"{names(0)}, {names(1)}, {names(2)}, {names(3)}, {names(4)}, " +
                   $"{names(5)}, {names(6)}"

        Dim dat = DateTime.Now
        output += $"{vbCrLf}It is {dat:t} on {dat:d}. The day of the week is {dat.DayOfWeek}."
        Console.WriteLine(output)
    End Sub
End Module
' The example displays the following output:
'     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
'     It is 10:29 AM on 1/8/2018. The day of the week is Monday.

¿Dónde puedo encontrar las cadenas de formato predefinidas?

Cómo controlar la alineación de las cadenas de resultado que reemplazan los elementos de formato?

La sintaxis general de un elemento de formato es:

{index[,alignment][: formatString]}

donde la alineación es un entero con signo que define el ancho del campo. Si este valor es negativo, el texto del campo está alineado a la izquierda. Si es positivo, el texto está alineado a la derecha.

Cómo controlar el número de dígitos después del separador decimal?

Todas las cadenas de formato numérico estándar excepto "D" (que solo se usa con enteros), "G", "R" y "X" permiten un especificador de precisión que define el número de dígitos decimales en la cadena de resultado. En el ejemplo siguiente se usan cadenas de formato numérico estándar para controlar el número de dígitos decimales en la cadena de resultado.

object[] values = { 1603, 1794.68235, 15436.14 };
string result;
foreach (var value in values)
{
    result = String.Format("{0,12:C2}   {0,12:E3}   {0,12:F4}   {0,12:N3}  {1,12:P2}\n",
                           Convert.ToDouble(value), Convert.ToDouble(value) / 10000);
    Console.WriteLine(result);
}
// The example displays output like the following:
//       $1,603.00     1.603E+003      1603.0000      1,603.000       16.03 %
//    
//       $1,794.68     1.795E+003      1794.6824      1,794.682       17.95 %
//    
//      $15,436.14     1.544E+004     15436.1400     15,436.140      154.36 %
open System

let values: obj list = [ 1603, 1794.68235, 15436.14 ]
for value in values do
   String.Format("{0,12:C2}   {0,12:E3}   {0,12:F4}   {0,12:N3}  {1,12:P2}\n", Convert.ToDouble(value), Convert.ToDouble(value) / 10000.)
   |> printfn "%s"
// The example displays output like the following:
//       $1,603.00     1.603E+003      1603.0000      1,603.000       16.03 %
//    
//       $1,794.68     1.795E+003      1794.6824      1,794.682       17.95 %
//    
//      $15,436.14     1.544E+004     15436.1400     15,436.140      154.36 %
Module Example7
    Public Sub Main()
        Dim values() As Object = {1603, 1794.68235, 15436.14}
        Dim result As String
        For Each value In values
            result = String.Format("{0,12:C2}   {0,12:E3}   {0,12:F4}   {0,12:N3}  {1,12:P2}",
                                value, CDbl(value) / 10000)
            Console.WriteLine(result)
            Console.WriteLine()
        Next
    End Sub
End Module
' The example displays the following output:
'       $1,603.00     1.603E+003      1603.0000      1,603.000       16.03 %
'    
'       $1,794.68     1.795E+003      1794.6824      1,794.682       17.95 %
'    
'      $15,436.14     1.544E+004     15436.1400     15,436.140      154.36 %

Si usa una cadena de formato numérico personalizado, use el especificador de formato "0" para controlar el número de dígitos decimales en la cadena de resultado, como se muestra en el ejemplo siguiente.

decimal value = 16309.5436m;
string result = String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}",
                              value);
Console.WriteLine(result);
// The example displays the following output:
//        16309.54360    16,309.54    16309.544
let value = 16309.5436m
String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}", value)
|> printfn "%s"
// The example displays the following output:
//        16309.54360    16,309.54    16309.544
Module Example8
    Public Sub Main()
        Dim value As Decimal = 16309.5436D
        Dim result As String = String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}",
                                           value)
        Console.WriteLine(result)
    End Sub
End Module
' The example displays the following output:
'    16309.54360    16,309.54    16309.544

Cómo controlar el número de dígitos enteros?

De forma predeterminada, las operaciones de formato solo muestran dígitos enteros distintos de cero. Si da formato a enteros, puede usar un especificador de precisión con las cadenas de formato estándar "D" y "X" para controlar el número de dígitos.

int value = 1326;
string result = String.Format("{0,10:D6} {0,10:X8}", value);
Console.WriteLine(result);
// The example displays the following output:
//     001326   0000052E
open System

let value = 1326
String.Format("{0,10:D6} {0,10:X8}", value)
|> printfn "%s"
// The example displays the following output:
//     001326   0000052E
Module Example10
    Public Sub Main()
        Dim value As Integer = 1326
        Dim result As String = String.Format("{0,10:D6} {0,10:X8}", value)
        Console.WriteLine(result)
    End Sub
End Module
' The example displays the following output:
'       001326   0000052E

Puede rellenar un número entero o de punto flotante con ceros iniciales para generar una cadena de resultado con un número especificado de dígitos enteros mediante el especificador de formato numérico personalizado "0", como se muestra en el ejemplo siguiente.

int value = 16342;
string result = String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}",
                              value);
Console.WriteLine(result);
// The example displays the following output:
//           00016342       00016342.000    0,000,016,342.0
open System

let value = 16342
String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}", value)
|> printfn "%s"
// The example displays the following output:
//           00016342       00016342.000    0,000,016,342.0
Module Example9
    Public Sub Main()
        Dim value As Integer = 16342
        Dim result As String = String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}",
                                           value)
        Console.WriteLine(result)
    End Sub
End Module
' The example displays the following output:
'           00016342       00016342.000    0,000,016,342.0

¿Cuántos elementos puedo incluir en la lista de formato?

No hay ningún límite práctico. El segundo parámetro del Format(IFormatProvider, String, Object[]) método se etiqueta con el ParamArrayAttribute atributo , que permite incluir una lista delimitada o una matriz de objetos como lista de formato.

Cómo incluir llaves literales ("{" y "}") en la cadena de resultado?

Por ejemplo, ¿cómo impide que la siguiente llamada de método inicie una FormatException excepción?

result = String.Format("The text has {0} '{' characters and {1} '}' characters.",
                       nOpen, nClose);
let result = 
    String.Format("The text has {0} '{' characters and {1} '}' characters.", nOpen, nClose)
result = String.Format("The text has {0} '{' characters and {1} '}' characters.",
                 nOpen, nClose)

Una sola llave de apertura o cierre siempre se interpreta como el principio o el final de un elemento de formato. Para interpretarse literalmente, debe ser de escape. Para escapar una llave, agregue otra llave ("{{" y "}}" en lugar de "{" y "}"), como en la siguiente llamada al método:

string result;
int nOpen = 1;
int nClose = 2;
result = String.Format("The text has {0} '{{' characters and {1} '}}' characters.",
                       nOpen, nClose);
Console.WriteLine(result);
let result =
    String.Format("The text has {0} '{{' characters and {1} '}}' characters.", nOpen, nClose)
result = String.Format("The text has {0} '{{' characters and {1} '}}' characters.",
                 nOpen, nClose)

Sin embargo, incluso las llaves de escape se malinterpretan fácilmente. Se recomienda incluir llaves en la lista de formato y usar elementos de formato para insertarlos en la cadena de resultado, como se muestra en el ejemplo siguiente.

string result;
int nOpen = 1;
int nClose = 2;
result = String.Format("The text has {0} '{1}' characters and {2} '{3}' characters.",
                       nOpen, "{", nClose, "}");
Console.WriteLine(result);
let result =
    String.Format("The text has {0} '{1}' characters and {2} '{3}' characters.", nOpen, "{", nClose, "}")
result = String.Format("The text has {0} '{1}' characters and {2} '{3}' characters.",
                 nOpen, "{", nClose, "}")

¿Por qué mi llamada al método String.Format produce una excepción FormatException?

La causa más común de la excepción es que el índice de un elemento de formato no corresponde a un objeto de la lista de formato. Normalmente esto indica que ha numerado incorrectamente los índices de elementos de formato o que ha olvidado incluir un objeto en la lista de formato. Si se intenta incluir un carácter de llave izquierda o derecha nocapada, también se produce una FormatExceptionexcepción . En ocasiones, la excepción es el resultado de un error tipográfico; por ejemplo, un error típico es escribir incorrectamente "[" (corchete izquierdo) en lugar de "{" (llave izquierda).

Si el método Format(System.IFormatProvider,System.String,System.Object[]) admite matrices de parámetros, ¿por qué mi código produce una excepción cuando uso una matriz?

Por ejemplo, el código siguiente produce una FormatException excepción:

Random rnd = new Random();
int[] numbers = new int[4];
int total = 0;
for (int ctr = 0; ctr <= 2; ctr++)
{
    int number = rnd.Next(1001);
    numbers[ctr] = number;
    total += number;
}
numbers[3] = total;
Console.WriteLine("{0} + {1} + {2} = {3}", numbers);
open System

let rnd = Random()
let mutable total = 0
let numbers = Array.zeroCreate<int> 4
for i = 0 to 2 do
   let number = rnd.Next 1001
   numbers[i] <- number
   total <- total + number
numbers[3] <- total
Console.WriteLine("{0} + {1} + {2} = {3}", numbers)
Imports System.Collections.Generic

Module Example5
    Public Sub Main()
        Dim rnd As New Random()
        Dim numbers(3) As Integer
        Dim total As Integer = 0
        For ctr = 0 To 2
            Dim number As Integer = rnd.Next(1001)
            numbers(ctr) = number
            total += number
        Next
        numbers(3) = total
        Console.WriteLine("{0} + {1} + {2} = {3}", numbers)
    End Sub
End Module

Se trata de un problema de resolución de sobrecarga del compilador. Dado que el compilador no puede convertir una matriz de enteros en una matriz de objetos, trata la matriz de enteros como un único argumento, por lo que llama al Format(String, Object) método . Se produce la excepción porque hay cuatro elementos de formato, pero solo un solo elemento en la lista de formato.

Dado que ni Visual Basic ni C# pueden convertir una matriz de enteros en una matriz de objetos, debe realizar la conversión usted mismo antes de llamar al Format(String, Object[]) método . En el siguiente ejemplo se proporciona una implementación.

Random rnd = new Random();
int[] numbers = new int[4];
int total = 0;
for (int ctr = 0; ctr <= 2; ctr++)
{
    int number = rnd.Next(1001);
    numbers[ctr] = number;
    total += number;
}
numbers[3] = total;
object[] values = new object[numbers.Length];
numbers.CopyTo(values, 0);
Console.WriteLine("{0} + {1} + {2} = {3}", values);
open System

let rnd = Random()
let numbers = Array.zeroCreate<int> 4
let mutable total = 0
for i = 0 to 2 do
   let number = rnd.Next 1001
   numbers[i] <- number
   total <- total + number
numbers[3] <- total
let values = Array.zeroCreate<obj> numbers.Length
numbers.CopyTo(values, 0)
Console.WriteLine("{0} + {1} + {2} = {3}", values)
Imports System.Collections.Generic

Module Example6
    Public Sub Main()
        Dim rnd As New Random()
        Dim numbers(3) As Integer
        Dim total As Integer = 0
        For ctr = 0 To 2
            Dim number As Integer = rnd.Next(1001)
            numbers(ctr) = number
            total += number
        Next
        numbers(3) = total
        Dim values(numbers.Length - 1) As Object
        numbers.CopyTo(values, 0)
        Console.WriteLine("{0} + {1} + {2} = {3}", values)
    End Sub
End Module