Formatos compuestos

La característica de formato compuesto de .NET toma una lista de objetos y una cadena de formato compuesto como entrada. Una cadena de formato compuesto está formada por texto fijo combinado con marcadores de posición indizados, que reciben el nombre de elementos de formato, y que se corresponden con los objetos de la lista. La operación de formato genera una cadena de resultado compuesta por el texto fijo original combinado con la representación de cadena de los objetos de la lista.

Importante

En lugar de usar cadenas de formato compuesto, puede usar cadenas interpoladas si el idioma y la versión de idioma que está usando son compatibles con ellos. 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).

La característica de formato compuesto se admite mediante métodos como los siguientes:

Cadena de formato compuesto

Los métodos compatibles con la característica de formato compuesto utilizan como argumentos una cadena de formato compuesto y una lista de objetos. Una cadena de formato compuesto consta de cero o más ejecuciones de texto fijo combinadas con uno o varios elementos de formato. El texto fijo es cualquier cadena que elija y cada elemento de formato se corresponde con un objeto o estructura de conversión boxing de la lista. La característica de formato compuesto devuelve una nueva cadena de resultado donde cada elemento de formato se reemplaza por la representación de cadena del objeto correspondiente de la lista.

Observe el siguiente fragmento de código Format.

string name = "Fred";
String.Format("Name = {0}, hours = {1:hh}", name, DateTime.Now);
Dim name As String = "Fred"
String.Format("Name = {0}, hours = {1:hh}", name, DateTime.Now)

El texto fijo es "Name =" y ", hours =". Los elementos de formato son "{0}", cuyo índice es 0, que corresponde al objeto name, y "{1:hh}", cuyo índice es 1, que corresponde al objeto DateTime.Now.

Sintaxis de elemento de formato

Cada elemento de formato presenta la siguiente sintaxis, formada por los siguientes componentes:

{ index[,alignment][:formatString]}

Las llaves ("{" y "}") son necesarias.

Index (Componente)

El componente index obligatorio, denominado también especificador de parámetros, es un número que empieza por 0 que identifica un elemento correspondiente de la lista de objetos. O sea, el elemento de formato cuyo especificador de parámetro es 0 da formato al primer objeto de la lista, el elemento de formato cuyo especificador de parámetro es 1 da formato al segundo objeto de la lista, etc. En el ejemplo siguiente se incluyen cuatro especificadores de parámetros, numerados del cero al tres, para representar números primos menores que diez:

string primes;
primes = String.Format("Prime numbers less than 10: {0}, {1}, {2}, {3}",
                       2, 3, 5, 7 );
Console.WriteLine(primes);
// The example displays the following output:
//      Prime numbers less than 10: 2, 3, 5, 7
Dim primes As String
primes = String.Format("Prime numbers less than 10: {0}, {1}, {2}, {3}",
                       2, 3, 5, 7)
Console.WriteLine(primes)
' The example displays the following output:
'      Prime numbers less than 10: 2, 3, 5, 7

Los elementos de formato múltiple se pueden referir al mismo elemento de la lista de objetos mediante la especificación del mismo especificador de parámetro. Por ejemplo, se puede dar formato al mismo valor numérico en formato hexadecimal, científico y de número mediante la especificación de una cadena de formato compuesto como esta: "0x{0:X} {0:E} {0:N}", como se muestra en el ejemplo siguiente.

string multiple = String.Format("0x{0:X} {0:E} {0:N}",
                                Int64.MaxValue);
Console.WriteLine(multiple);
// The example displays the following output:
//      0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00
Dim multiple As String = String.Format("0x{0:X} {0:E} {0:N}",
                                       Int64.MaxValue)
Console.WriteLine(multiple)
' The example displays the following output:
'      0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00

Cada elemento de formato puede hacer referencia a cualquier objeto de la lista. Por ejemplo, si existen tres objetos, se puede dar formato al segundo, primero y tercer objeto mediante la especificación de una cadena de formato compuesto como esta: "{1} {0} {2}". Un objeto al que no hace referencia ningún elemento de formato se omite. Se produce una excepción de tipo FormatException en tiempo de ejecución si un especificador de parámetro designa un elemento fuera de los límites de la lista de objetos.

Alignment (Componente)

El componente opcional alignment es un entero con signo que indica el ancho de campo con formato preferido. Si el valor de alignment es menor que la longitud de la cadena con formato, se omite alignment y se usa la longitud de la cadena con formato como el ancho de campo. Los datos con formato del campo están alineados a la derecha si alignment es positivo y a la izquierda si alignment es negativo. Si hace falta relleno, se utiliza un espacio en blanco. Si se especifica alignment, es necesaria la coma.

El siguiente ejemplo define dos matrices, que contiene los nombres de empleados y otra contiene las horas que han trabajado en un período de dos semanas. La cadena de formato compuesto alinea a la izquierda los nombres en un campo de 20 caracteres y alinea a la derecha las horas en un campo de 5 caracteres. Tenga en cuenta que la cadena de formato estándar "N1" también se usa para dar formato a las horas con un dígito fraccionario.

using System;

public class Example
{
   public static void Main()
   {
      string[] names = { "Adam", "Bridgette", "Carla", "Daniel",
                         "Ebenezer", "Francine", "George" };
      decimal[] hours = { 40, 6.667m, 40.39m, 82, 40.333m, 80,
                                 16.75m };

      Console.WriteLine("{0,-20} {1,5}\n", "Name", "Hours");
      for (int ctr = 0; ctr < names.Length; ctr++)
         Console.WriteLine("{0,-20} {1,5:N1}", names[ctr], hours[ctr]);
   }
}
// The example displays the following output:
//       Name                 Hours
//
//       Adam                  40.0
//       Bridgette              6.7
//       Carla                 40.4
//       Daniel                82.0
//       Ebenezer              40.3
//       Francine              80.0
//       George                16.8
Module Example
    Public Sub Main()
        Dim names() As String = {"Adam", "Bridgette", "Carla", "Daniel",
                                  "Ebenezer", "Francine", "George"}
        Dim hours() As Decimal = {40, 6.667d, 40.39d, 82, 40.333d, 80,
                                   16.75d}

        Console.WriteLine("{0,-20} {1,5}", "Name", "Hours")
        Console.WriteLine()
        For ctr As Integer = 0 To names.Length - 1
            Console.WriteLine("{0,-20} {1,5:N1}", names(ctr), hours(ctr))
        Next
    End Sub
End Module
' The example displays the following output:
'       Name                 Hours
'
'       Adam                  40.0
'       Bridgette              6.7
'       Carla                 40.4
'       Daniel                82.0
'       Ebenezer              40.3
'       Francine              80.0
'       George                16.8

Format String (Componente)

El componente formatString opcional es una cadena de formato adecuada para el tipo de objeto al que se da formato. Especifique una cadena de formato numérico estándar o personalizado si el objeto correspondiente es un valor numérico, una cadena de formato de fecha y hora estándar o personalizado si el objeto correspondiente es un objeto DateTime, o una cadena de formato de enumeración si el objeto correspondiente es un valor de enumeración. Si no se especifica formatString, se usa el especificador de formato general ("G") para un tipo numérico, de fecha y hora o de enumeración. Si se especifica formatString, son necesarios los dos puntos.

En la tabla siguiente se enumeran los tipos o las categorías de tipos de la biblioteca de clases de .NET que admiten un conjunto predefinido de cadenas de formato, y se proporcionan vínculos a temas en los que se muestran las cadenas de formato admitidas. Observe que la asignación de formato a cadenas es un mecanismo extensible que permite definir cadenas de formato nuevas para todos los tipos existentes, así como definir un conjunto de cadenas de formato admitidas por un tipo definido por la aplicación. Para obtener más información, consulte los temas sobre las interfaces IFormattable y ICustomFormatter.

Tipo o categoría de tipo Vea
Tipos de fecha y hora (DateTime, DateTimeOffset) Cadenas con formato de fecha y hora estándar

Cadenas con formato de fecha y hora personalizado
Tipos de enumeración (todos los tipos derivados de System.Enum) Cadenas de formato de enumeración
Tipos numéricos (BigInteger, Byte, Decimal, Double, Int16, Int32, Int64, SByte, Single, UInt16, UInt32, UInt64) Cadenas con formato numérico estándar

Cadenas con formato numérico personalizado
Guid Guid.ToString(String)
TimeSpan Cadenas de formato TimeSpan estándar

Cadenas de formato TimeSpan personalizado

Llaves de escape

Las llaves de apertura y de cierre se interpretan como el inicio y el final de un elemento de formato. Por lo tanto, debe utilizar una secuencia de escape para que se muestre una llave de apertura o de cierre literal. Especifique dos llaves de apertura ("{{") en el texto fijo para que se muestre una llave de apertura ("{"), o dos llaves de cierre ("}}") para que se muestre una llave de cierre ("}"). Las llaves de un elemento de formato se interpretan secuencialmente, en el orden en que se encuentran. No se admite la interpretación de llaves anidadas.

El modo de interpretar las llaves de escape puede dar lugar a resultados inesperados. Tomemos como ejemplo el elemento de formato "{{{0:D}}}", cuyo propósito es mostrar una llave de apertura, un valor numérico con formato de número decimal y una llave de cierre; pero que, en la práctica, se interpreta de la siguiente forma:

  1. Las dos primeras llaves de apertura ("{{") son llaves de escape y dan lugar a en una llave de apertura.

  2. Los tres caracteres siguientes ("{0:") se interpretan como el inicio de un elemento de formato.

  3. El siguiente carácter ("D") se interpretaría como el especificador de formato numérico estándar decimal, pero las dos llaves de escape siguientes ("}}") dan lugar a una única llave. Como la cadena resultante ("D}") no es un especificador de formato numérico estándar, se interpreta como una cadena de formato personalizado que significa que debe mostrarse la cadena literal "D}".

  4. La última llave ("}") se interpreta como el final del elemento de formato.

  5. Como resultado final, se muestra la cadena literal "{D}". No se muestra el valor numérico al que se debía dar formato.

Una forma de escribir código e impedir que las llaves de escape y los elementos de formato se malinterpreten consiste en dar formato a las llaves y elementos de formato por separado. Es decir, en la primera operación de formato mostrar una llave de apertura literal, en la siguiente operación mostrar el resultado del elemento de formato y, por último, en la operación final mostrar una llave de cierre literal. En el ejemplo siguiente se muestra este enfoque.

int value = 6324;
string output = string.Format("{0}{1:D}{2}",
                             "{", value, "}");
Console.WriteLine(output);
// The example displays the following output:
//       {6324}
Dim value As Integer = 6324
Dim output As String = String.Format("{0}{1:D}{2}", _
                                     "{", value, "}")
Console.WriteLine(output)
' The example displays the following output:
'       {6324}

Orden de procesamiento

Si la llamada al método de formato compuesto incluye un argumento IFormatProvider cuyo valor no es null, el runtime llama al método IFormatProvider.GetFormat para solicitar una implementación de ICustomFormatter. Si el método es capaz de devolver una implementación de ICustomFormatter, se almacena en caché el tiempo que dure la llamada de método de formato compuesto.

Cada valor de la lista de parámetros que se corresponda con un elemento de formato se convierte en una cadena del siguiente modo:

  1. Si el valor al que se va a dar formato es null, se devuelve una cadena vacía String.Empty.

  2. Si hay disponible una implementación de ICustomFormatter, el runtime llama al método Format. Pasa al método el valor formatString del elemento de formato, si hay alguno, o null si no lo hay, junto con la implementación de IFormatProvider. Si la llamada al método ICustomFormatter.Format devuelve null, la ejecución avanza al siguiente paso; en caso contrario, se devuelve el resultado de la llamada a ICustomFormatter.Format.

  3. Si el valor implementa la interfaz IFormattable, se llama al método ToString(String, IFormatProvider) de esta. Se pasa al método el valor formatString, si hubiera uno presente en el elemento de formato, o null si no lo hubiera. El argumento IFormatProvider se determina de la siguiente forma:

  4. Se llama al método sin parámetros ToString del tipo, que reemplaza a Object.ToString() o hereda el comportamiento de su clase base. En este caso, se omite la cadena de formato especificada por el componente formatString en el elemento de formato, si estuviera presente.

La alineación se aplica después de que se hayan realizado los pasos anteriores.

Ejemplos de código

En el ejemplo siguiente se muestra una cadena creada mediante formato compuesto y otra creada mediante el método ToString de un objeto. Los dos tipos de formato producen resultados equivalentes.

string FormatString1 = String.Format("{0:dddd MMMM}", DateTime.Now);
string FormatString2 = DateTime.Now.ToString("dddd MMMM");
Dim FormatString1 As String = String.Format("{0:dddd MMMM}", DateTime.Now)
Dim FormatString2 As String = DateTime.Now.ToString("dddd MMMM")

Si tomamos como día actual un jueves del mes de mayo, el valor de ambas cadenas del ejemplo anterior será Thursday May para la referencia cultural Inglés (Estados Unidos).

Console.WriteLine expone la misma funcionalidad que String.Format. La única diferencia que existe entre estos dos métodos es que String.Format devuelve el resultado como una cadena, mientras que Console.WriteLine escribe el resultado en el flujo de salida asociado al objeto Console. En el ejemplo siguiente se usa el método Console.WriteLine para dar formato al valor de MyInt como un valor de divisa.

int MyInt = 100;
Console.WriteLine("{0:C}", MyInt);
// The example displays the following output
// if en-US is the current culture:
//        $100.00
Dim MyInt As Integer = 100
Console.WriteLine("{0:C}", MyInt)
' The example displays the following output
' if en-US is the current culture:
'        $100.00

En el siguiente ejemplo se muestra la aplicación de formato a objetos múltiples, incluida la aplicación de formato a un objeto de dos formas diferentes.

string myName = "Fred";
Console.WriteLine(String.Format("Name = {0}, hours = {1:hh}, minutes = {1:mm}",
      myName, DateTime.Now));
// Depending on the current time, the example displays output like the following:
//    Name = Fred, hours = 11, minutes = 30
Dim myName As String = "Fred"
Console.WriteLine(String.Format("Name = {0}, hours = {1:hh}, minutes = {1:mm}", _
                  myName, DateTime.Now))
' Depending on the current time, the example displays output like the following:
'    Name = Fred, hours = 11, minutes = 30                 

En el ejemplo siguiente se muestra el uso de la alineación en la aplicación de formato. Los argumentos a los que se da formato se colocan entre caracteres verticales (|) para resaltar la alineación resultante.

string myFName = "Fred";
string myLName = "Opals";
int myInt = 100;
string FormatFName = String.Format("First Name = |{0,10}|", myFName);
string FormatLName = String.Format("Last Name = |{0,10}|", myLName);
string FormatPrice = String.Format("Price = |{0,10:C}|", myInt);
Console.WriteLine(FormatFName);
Console.WriteLine(FormatLName);
Console.WriteLine(FormatPrice);
Console.WriteLine();

FormatFName = String.Format("First Name = |{0,-10}|", myFName);
FormatLName = String.Format("Last Name = |{0,-10}|", myLName);
FormatPrice = String.Format("Price = |{0,-10:C}|", myInt);
Console.WriteLine(FormatFName);
Console.WriteLine(FormatLName);
Console.WriteLine(FormatPrice);
// The example displays the following output on a system whose current
// culture is en-US:
//          First Name = |      Fred|
//          Last Name = |     Opals|
//          Price = |   $100.00|
//
//          First Name = |Fred      |
//          Last Name = |Opals     |
//          Price = |$100.00   |
Dim myFName As String = "Fred"
Dim myLName As String = "Opals"

Dim myInt As Integer = 100
Dim FormatFName As String = String.Format("First Name = |{0,10}|", myFName)
Dim FormatLName As String = String.Format("Last Name = |{0,10}|", myLName)
Dim FormatPrice As String = String.Format("Price = |{0,10:C}|", myInt)
Console.WriteLine(FormatFName)
Console.WriteLine(FormatLName)
Console.WriteLine(FormatPrice)
Console.WriteLine()

FormatFName = String.Format("First Name = |{0,-10}|", myFName)
FormatLName = String.Format("Last Name = |{0,-10}|", myLName)
FormatPrice = String.Format("Price = |{0,-10:C}|", myInt)
Console.WriteLine(FormatFName)
Console.WriteLine(FormatLName)
Console.WriteLine(FormatPrice)
' The example displays the following output on a system whose current
' culture is en-US:
'          First Name = |      Fred|
'          Last Name = |     Opals|
'          Price = |   $100.00|
'
'          First Name = |Fred      |
'          Last Name = |Opals     |
'          Price = |$100.00   |

Vea también