Para poner en mayúscula la primera letra de una cadena en C#, pasa el primer carácter a mayúscula y copia el resto sin cambios. string.Create() lo hace con una sola asignación de memoria: string.Create(input.Length, input, static (chars, str) => { chars[0] = char.ToUpperInvariant(str[0]); str.AsSpan(1).CopyTo(chars[1..]); }).
Todo lo demás en esta página es una variación de esa idea con un costo distinto. Las subcadenas, los arrays de caracteres, LINQ y una expresión regular funcionan, pero todos asignan más memoria de la necesaria.
Hay un enfoque que no tiene cabida en código de producción. Fijar la cadena en memoria y sobrescribir su primer carácter mediante un puntero es la fila más rápida del benchmark, pero corrompe todas las demás referencias a la misma cadena. Lo mantenemos aquí, bien señalado, porque circula mucho.
Técnica con Substring
Para convertir correctamente la primera letra de una cadena en mayúscula, tenemos que extraerla y usar el método integrado ToUpper() para pasar ese primer carácter a mayúscula.
Vamos a implementar un método para ilustrar este concepto:
public string FirstCharSubstring(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{input[0].ToString().ToUpper()}{input.Substring(1)}";
}
Primero, comprobamos si la cadena es null o está vacía antes de extraer su primer carácter e invocar el método ToUpper() para convertir la primera letra en mayúscula. Después, concatenamos el primer carácter con el resto de la subcadena y devolvemos el resultado al usuario.
Para todas las pruebas unitarias que vamos a implementar hoy, usaremos el mismo campo:
private readonly FirstLetterToUpperMethods _upperCase = new();
A continuación, podemos comprobar que el método cumple su propósito:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharSubstring(testString);
Assert.AreEqual("This is a test string", returnedString);
Usar el método AsSpan()
Podemos usar el método integrado AsSpan() para convertir la primera letra de una cadena en mayúscula. La llamada a AsSpan(1) crea un nuevo span de solo lectura sobre una cadena, desde el segundo carácter hasta el último, lo que evita asignaciones de memoria adicionales y mejora el rendimiento:
public string FirstCharToUpperAsSpan(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
Span<char> destination = stackalloc char[1];
input.AsSpan(0, 1).ToUpperInvariant(destination);
return $"{destination}{input.AsSpan(1)}";
}
El método FirstCharToUpperAsSpan() recibe una cadena, comprueba si es null o está vacía y, si el resultado es true, devuelve una cadena vacía.
En caso contrario, usamos stackalloc para crear en la pila un bloque de memoria para un carácter. A continuación, pasamos a mayúscula la primera letra de la cadena invocando el método AsSpan() junto con el método ToUpperInvariant().
Por último, creamos un span de solo lectura sobre el resto de la subcadena, lo concatenamos con la primera letra de la cadena invocando el método AsSpan() y devolvemos el resultado al usuario.
Comprobemos con la prueba que el método cumple su propósito:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpperAsSpan(testString);
Assert.AreEqual("This is a test string", returnedString);
Usar string.Create con AsSpan
Si quieres mejorar la implementación con Span y ganar todavía más rendimiento, puedes usar el método string.Create:
public string FirstCharToUpperStringCreate(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return string.Create(input.Length, input, static (Span<char> chars, string str) =>
{
chars[0] = char.ToUpperInvariant(str[0]);
str.AsSpan(1).CopyTo(chars[1..]);
});
}
Es el método seguro más rápido de la página, y el benchmark que aparece más abajo lo compara con los otros siete. Queremos dar las gracias a nuestro lector Joel por aportar esta sugerencia en la sección de comentarios.
Usar el método Char.ToUpper()
Esta técnica hace lo mismo que la técnica con Substring, con una diferencia: pasa el primer carácter a mayúscula como char en lugar de convertirlo antes en una cadena de un solo carácter:
public string FirstCharToUpper(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{char.ToUpper(input[0])}{input[1..]}";
}
El método FirstCharToUpper() toma el primer carácter input[0] e invoca el método char.ToUpper() para convertirlo en mayúscula. Para completar el proceso, lo concatenamos con el resto de la cadena y lo devolvemos como cadena.
Ya podemos comprobar que la implementación convierte el primer carácter en mayúscula:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpper(testString);
Assert.AreEqual("This is a test string", returnedString);
Usar arrays de caracteres
Como una cadena puede verse como un array de caracteres, podemos convertirla en un array e invocar el método char.ToUpper() para pasar a mayúscula la primera letra del array antes de devolverlo como cadena:
public string FirstCharToCharArray(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
var stringArray = input.ToCharArray();
if (char.IsLower(stringArray[0]))
{
stringArray[0] = char.ToUpper(stringArray[0]);
}
return new string(stringArray);
}
Después de comprobar si la cadena input es null o está vacía, la convertimos en un array de caracteres invocando el método string.ToCharArray(). A continuación, pasamos a mayúscula el primer elemento del array invocando el método char.ToUpper(), tras comprobar con el método char.IsLower() si está en minúscula. Una vez convertida la primera letra de la cadena en mayúscula, devolvemos un nuevo objeto string.
Podemos comprobar que el método convierte en mayúscula la primera letra de una cadena:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToCharArray(testString);
Assert.AreEqual("This is a test string", returnedString);
Expresiones regulares
C# admite expresiones regulares, que encontramos en el espacio de nombres System.Text.RegularExpressions. Podemos usar el método Regex.Replace() para convertir la primera letra de una cadena en mayúscula, y dejamos que el generador de código fuente de Regex construya el motor en tiempo de compilación:
[GeneratedRegex("^\\p{Ll}")]
private static partial Regex FirstLowerLetter();
public string FirstCharToUpperRegex(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return FirstLowerLetter().Replace(input, c => c.Value.ToUpper());
}
Aquí, input es la cadena en la que buscamos caracteres en minúscula. "^\p{Ll}" es el patrón de expresión regular que usamos para que coincida con el primer carácter de la cadena input. Cuando el método Replace() encuentra, según nuestro patrón, un carácter en minúscula al principio de una cadena, lo reemplaza por su equivalente en mayúscula invocando el método ToUpper() en la última parte, c => c.Value.ToUpper().
El patrón importa más de lo que parece. La forma obvia de escribirlo, "^[a-z]", solo coincide con las veintiséis letras minúsculas ASCII, así que una cadena que empieza por é o ñ se devuelve sin cambios, mientras que todas las demás técnicas de esta página la ponen en mayúscula. La categoría Unicode \p{Ll} significa “cualquier letra minúscula” y cierra ese hueco sin añadir una dependencia de la cultura.

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
Podemos comprobar con una prueba que la solución convierte en mayúscula la primera letra de una cadena:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpperRegex(testString);
Assert.AreEqual("This is a test string", returnedString);
Convertir la primera letra en mayúscula con LINQ
Otra forma de convertir en mayúscula la primera letra de una cadena es usar LINQ. Para que el ejemplo sea sencillo, vamos a usar el método FirstOrDefault() para extraer el primer carácter de la cadena de entrada e invocar el método ToUpper() para convertirlo en mayúscula:
public string FirstCharToUpperLinq(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{input.FirstOrDefault().ToString().ToUpper()}{input.Substring(1)}";
}
Extraemos la primera letra de la cadena, la convertimos en mayúscula y la concatenamos con el resto de la subcadena antes de devolver el resultado al usuario.
Ahora, probemos esta lógica:
var testString = "this is a test string";
var returnedString = _upperCase.FirstCharToUpperLinq(testString);
Assert.AreEqual("This is a test string", returnedString);
¿Por qué no conviene poner en mayúscula la primera letra de una cadena con código no seguro?
La versión con código no seguro (unsafe) nunca construye una cadena nueva. Fija en memoria la cadena que recibe, sobrescribe el primer carácter en su sitio y devuelve la misma instancia.
Las cadenas en .NET son inmutables y el runtime depende de ello. Los literales idénticos de un ensamblado normalmente comparten una única instancia internada, así que sobrescribir el primer carácter de una variable cambia todas las demás variables, campos y claves de diccionario que apuntan al mismo literal.
Lo ejecutamos en .NET 10. Dos variables a las que se asigna el mismo literal son la misma instancia, y tras una sola llamada a través de cualquiera de ellas, ambas contienen This is a test string.
Un Dictionary<string, int> construido con esa cadena antes de la llamada deja de encontrar después una clave igual, porque el cubo (bucket) se eligió a partir del contenido anterior.
Por el mismo motivo, el benchmark deja en muy buen lugar a este método. No asigna nada ni copia nada, así que no hace el trabajo que hacen los otros siete métodos.
La imagen muestra todo el problema: a y b nunca fueron cadenas distintas.

No uses esto en código de producción. Está aquí porque circula como consejo de rendimiento, y los párrafos anteriores explican lo que hace en realidad. Podemos ver cómo funciona con código no seguro:
public string FirstCharToUpperUnsafeCode(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
unsafe
{
fixed (char* p = input)
{
*p = char.ToUpper(*p);
}
}
return input;
}
¿Poner una letra en mayúscula depende de la cultura?
Sí, y las dos llamadas que parecen intercambiables no lo son.
char.ToUpper(c) aplica las reglas de mayúsculas y minúsculas de la cultura actual. La referencia de Microsoft del método lo resume en una línea: “Las reglas de mayúsculas y minúsculas se obtienen de la cultura actual”. char.ToUpperInvariant(c), en cambio, aplica la cultura invariante, sea cual sea la configuración de la máquina.
Para la mayoría de las letras, las dos coinciden. Es en el turco donde divergen. La misma referencia muestra que la i minúscula (U+0069) se convierte en I (U+0049) con en-US y con la cultura invariante, y en İ (U+0130) con tr-TR.

Ebook gratis
¿Tu Web API está lista para producción?
33 puntos que comprobar antes de desplegarla, con la solución de cada uno. Un PDF gratuito de 76 páginas para .NET 10.
El ebook está en inglés.
Descarga la checklist gratisPDF gratuito. Un solo correo para enviártelo. Puedes darte de baja cuando quieras.
Lo reprodujimos. Con la cultura actual establecida en tr-TR, char.ToUpper('i') devolvió U+0130 y char.ToUpperInvariant('i') devolvió U+0049.
Así que la pregunta es para qué sirve la cadena. El texto que lee una persona debe seguir su cultura. Un identificador, un nombre de archivo, un token de protocolo o una clave de base de datos deben usar ToUpperInvariant(), porque el resultado no debe cambiar cuando cambia la configuración regional de la máquina.
Ambas reglas de conversión a mayúsculas proceden de la referencia del método Char.ToUpper, cuya tabla de ejemplo recoge, uno junto a otro, los resultados de i con en-US, con la cultura invariante y con tr-TR.
¿Cómo pasar toda una cadena a mayúsculas en C#?
ToUpper() sobre una cadena convierte cada carácter y devuelve una cadena nueva cuando algún carácter cambia, sin tocar la original. ToUpperInvariant() hace lo mismo con la cultura invariante, así que elegir entre ellos es exactamente la elección de cultura del apartado anterior.
Ninguno de los dos pone en mayúscula solo la primera letra, que es la razón de ser del resto de este artículo. "code maze".ToUpper() devuelve CODE MAZE, no Code maze.
Poner en mayúscula la primera letra de cada palabra es una tercera tarea, y TextInfo.ToTitleCase() es el método pensado para ella. Además, pasa a minúsculas el resto de cada palabra, salvo que la palabra entera esté en mayúsculas.
Ninguno de estos métodos modifica nada. En .NET, una cadena es inmutable, así que, cuando algún carácter tiene que cambiar, cada uno de ellos asigna una cadena nueva de la misma longitud que la original. En un bucle que se ejecuta a menudo, esa asignación es el costo que vale la pena medir, y es la razón por la que existen los enfoques basados en span que aparecen más arriba en esta página.
Pruebas de rendimiento
Evaluemos el rendimiento de estas técnicas; puedes ejecutar estos benchmarks por tu cuenta. Primero, vamos a implementar un método que genera una cadena aleatoria, que pasaremos a los ejemplos:
private string GenerateRandomString(int size)
{
var random = new Random();
var charOptions = "abcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Repeat(charOptions, size).Select(s => s[random.Next(s.Length)]).ToArray()).ToLower();
}
Primero, definimos una cadena charOptions que contiene todas las letras del alfabeto y que usaremos para generar cadenas aleatorias. Después, usamos la clase Random integrada, LINQ y la longitud de la cadena (Length) para devolver una nueva cadena aleatoria.
A continuación, vamos a crear un nuevo objeto que pasa a los ejemplos cadenas aleatorias de 2000 caracteres cada una:
public IEnumerable<object[]> SampleStrings()
{
yield return new object[] { GenerateRandomString(2000)};
}
Veamos cómo rinden las distintas formas de convertir en mayúscula la primera letra de una cadena:
BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6466/22H2/2022Update) AMD Ryzen 5 3600 3.60GHz, 1 CPU, 12 logical and 6 physical cores .NET SDK 10.0.302 [Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 DefaultJob : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 | Method | input | Mean | Error | StdDev | Median | Rank | Gen0 | Gen1 | Allocated | |----------------------------- |--------------------- |-----------:|-----------:|-----------:|-----------:|-----:|-------:|-------:|----------:| | FirstCharToUpperUnsafeCode | fowj(...)jwgs [2000] | 3.912 ns | 0.1390 ns | 0.3277 ns | 3.825 ns | 1 | - | - | - | | FirstCharToUpperStringCreate | kbbm(...)nhyr [2000] | 181.430 ns | 8.7778 ns | 24.0291 ns | 173.651 ns | 2 | 0.4809 | - | 4024 B | | FirstCharToUpperAsSpan | zhay(...)mbzp [2000] | 344.204 ns | 20.6009 ns | 59.7669 ns | 323.639 ns | 3 | 0.4807 | - | 4024 B | | FirstCharToCharArray | glhj(...)kvvv [2000] | 365.260 ns | 12.7927 ns | 36.0820 ns | 352.843 ns | 3 | 0.9618 | 0.0072 | 8048 B | | FirstCharToUpperLinq | gkkq(...)aiqu [2000] | 390.932 ns | 22.4573 ns | 64.0720 ns | 362.186 ns | 3 | 0.9675 | 0.0072 | 8096 B | | FirstCharSubstring | vmla(...)mybs [2000] | 407.755 ns | 11.0983 ns | 31.6641 ns | 401.588 ns | 4 | 0.9675 | 0.0072 | 8096 B | | FirstCharToUpper | ovap(...)dkyy [2000] | 476.392 ns | 13.8828 ns | 39.3832 ns | 469.819 ns | 5 | 0.9618 | - | 8048 B | | FirstCharToUpperRegex | tpvo(...)tijn [2000] | 561.932 ns | 17.1487 ns | 50.2942 ns | 549.487 ns | 6 | 0.5112 | - | 4280 B |
La técnica de código no seguro es la fila más rápida, con 3.912 ns, pero es la fila que hay que ignorar: no asigna nada ni copia nada porque no construye ninguna cadena nueva. De los siete métodos que sí hacen el trabajo, string.Create() gana con 181.430 ns y la expresión regular es la más lenta, con 561.932 ns. La columna “Allocated” los separa aún más: string.Create() y AsSpan() asignan una cadena cada uno, mientras que las técnicas con Substring, con LINQ y con char.ToUpper() asignan también las cadenas intermedias, y la técnica con array de caracteres asigna un array intermedio.
| Enfoque | Llamada | Asignaciones por llamada | Cultura | Úsalo cuando |
|---|---|---|---|---|
string.Create con un span | string.Create(len, input, …) + char.ToUpperInvariant() | Una cadena | Invariante, explícita | Opción por defecto; rutas críticas |
AsSpan() con stackalloc | input.AsSpan(0, 1).ToUpperInvariant(dest) | Una cadena, mediante interpolación | Invariante, explícita | Ya tienes un span |
| Array de caracteres | input.ToCharArray() y luego char.ToUpper() | Array más cadena | Cultura actual, implícita | El array se necesita de todos modos |
| Substring | input[0].ToString().ToUpper() + input.Substring(1) | Cuatro cadenas | Cultura actual, implícita | Legibilidad antes que costo |
char.ToUpper con un rango | char.ToUpper(input[0]) + input[1..] | Dos cadenas | Cultura actual, implícita | Legibilidad antes que costo |
| LINQ | FirstOrDefault().ToString() + .ToUpper() | Cuatro cadenas | Cultura actual, implícita | Nunca; es la versión con Substring disfrazada |
| Expresión regular | [GeneratedRegex("^\\p{Ll}")] y luego Replace(input, …) | Tres cadenas más la maquinaria de coincidencias | Cultura actual, implícita | Hay un patrón real de por medio, no un solo carácter |
| Escritura no segura con puntero | fixed (char* p = input) *p = … | Ninguna, porque modifica la cadena en su sitio | Cultura actual, implícita | Nunca. Consulta la sección sobre código no seguro |
Conclusión
En este artículo, hemos visto las distintas técnicas que podemos usar para convertir en mayúscula la primera letra de una cadena.
string.Create() es la que conviene usar: produce el resultado con una sola asignación y es la más rápida de las técnicas seguras, con 181.430 ns. La técnica del puntero es aún más rápida sobre el papel, con 3.912 ns, pero es la que hay que evitar, porque modifica una cadena a la que otras partes del programa también hacen referencia.
Poner en mayúscula cada palabra en lugar de solo la primera letra es una tarea distinta, y nuestro artículo sobre cómo poner una cadena en formato de título lo explica.
Probado con .NET 10.0.10 y BenchmarkDotNet 0.15.8.