Esta es una pregunta típica de principiante que suele tener una respuesta muy clara y concisa: sí, es indiferente usar una cosa o la otra. Pero, sin embargo, las cosas nunca suelen ser tan fáciles en los casos extremos, como veremos a continuación.

En primer lugar vamos a responder a la pregunta desde un punto de vista general...

Definir cadenas con string o String: ¿hay alguna diferencia?

Consideremos este híper-sencillo programa:

string s1 = "Hola mundo!";
String s2 = "Adios mundo";
Console.WriteLine(s1);
Console.WriteLine(s2);

Lo único que hace es definir dos cadenas de texto y mostrarlas por consola. Nada del otro mundo. La diferencia estriba en que la primera está definida con la palabra string y la segunda con String. La única diferencia es que la primera "s" es minúscula en el primer caso y mayúscula en el segundo.

Nota: en realidad hay otra pequeña diferencia. Si queremos usar String debemos asegurarnos de que hemos incluido el espacio de nombres System en la cabecera del archivo de código, o sea, que hemos puesto using System; arriba del todo. En el caso de string, al ser una palabra clave no es necesario hacer nada para usarla. Dado que Visual Studio siempre incluye eso automáticamente, si no lo hemos quitado ni nos daremos cuenta, pero es necesario saberlo.

La diferencia es que string es una palabra clave del lenguaje C#, mientras que la segunda es el tipo System.String de la plataforma .NET. En C# la palabra clave string es un alias para el tipo System.String del runtime de .NET, por lo que en la práctica, usar una cosa u otra es indiferente.

Si compilamos el programa anterior y lo abrimos con un decompilador como ILSpy o el propio descompilador ildasm.exe que viene con el SDK de .NET, obtenemos lo siguiente en lenguaje intermedio (lo que se genera al compilar cualquier programa .NET en cualquier lenguaje de programación):

El código en lenguaje intermedio

En ambos casos el resultado es que se reserva espacio para una cadena con ldstr. O sea, que es exactamente lo mismo usar uno u otro.

Generalmente los usos habituales recomiendan usar string para las declaraciones y String cuando se hace uso de algún método estático como por ejemplo String.Format o String.Concat:

string nombre = String.Concat("José Manuel", "Alarcón");

Incluso Microsoft en sus ejemplos lo suele hacer así.

Y sin embargo hay una diferencia...

Lo anterior es una realidad tangible y en el 99% de los casos es como funcionan las cosas, por lo que más allá del estilo no nos debería preocupar.

Sin embargo sí que existe una diferencia importante entre usar string y String: el primero es una palabra clave que no está sujeto a la interpretación, mientras que el segundo es un tipo y por lo tanto está sujeto al proceso de búsqueda de tipos del compilador.

¿Qué quiere decir esto? Pues quiere decir que en condiciones normales no va a haber diferencia, pero podría haberla y grande. Por ejemplo, mira este código:

void Main()
{
string s1 = "Hola mundo!";
String s2 = "Adios mundo";
Console.WriteLine(s1);
Console.WriteLine(s2);
}

// Redefino la clase String
public class String
{
}

Lo único que he hecho es crear una clase String propia (en este caso no hace nada de nada). Lo que obtengo en este caso es un error de compilación (lo he ejecutado en LinqPad, excelente herramienta para pruebas rápidas):

Error de conversión

Lo que está ocurriendo es que, con este código tan simple, el compilador interpreta que tenemos un literal de cadena y que queremos asignarlo a una clase propia llamada String y entonces todo rompe porque no se puede convertir de una a otra. Es decir, hemos transformado una declaración + asignación en un declaración + conversión + asignación, y hemos hecho que el programa no compile. Sin embargo fíjate como la primera declaración + asignación no cambia en absoluto ya que string siempre tiene un significado claro y meridiano.

De hecho cabría preguntarse si sería posible definir una clase llamada string. La respuesta te la da el propio compilador ya que esa cadena siempre se interpreta como un tipo:

Además, por supuesto, no funcionará ninguno de los métodos estáticos de la clase System.String, salvo que estén redefinidos en nuestra clase.

Incluso aunque hagamos que el código compile, simplemente metiéndole una conversión implícita de cadena de nuestra clase propia String, algo así:

El programa compila pero no funciona como esperábamos

el resultado no es el esperado, claro. En este caso lo que he hecho es definir una conversión implícita entre cadenas de texto y mi clase String para que el compilador no se queje y he sobrescrito el método ToString() para que devuelva por pantalla lo que a mi me dé la gana.

Vale, ¿y qué? Esto no va a pasar nunca...

¿Seguro?

Está claro que es una casuística extraña, pero burradas mayores he visto que definir una clase propia con el mismo nombre que una del sistema. Además, existen ciertos casos en los que es lícito y está justificado redefinir clases de esta manera, como por ejemplo un serializador o la implementación de un protocolo de transferencia de datos. Puede que tuviésemos problemas

Y un último caso algo conspiranoico, pero factible: imagina que estamos utilizando una biblioteca externa Open Source en forma de DLL que nos proporciona cierta funcionalidad y tenemos incluido, por tanto su(s) espacio(s) de nombres en nuestro programa, de modo que se resuelvan las clases que tiene definidas. El creador de la biblioteca externa (o, con mayor probabilidad un cracker que le haya pirateado la cuenta, cosa no tan rara) redefine la clase String en uno de los espacios de nombre más comunes, que seguro que tienes añadido y saca una versión nueva que tú actualizas. Esa nueva clase String malévola imita en todo a la clase del sistema, por lo que ni te enterarás del cambio porque nada falla, pero "loguea" y transmite todas las cadenas de texto que maneja tu programa y que has definido usando String (o que pasan por los métodos estáticos de la clase). Si el programa trabaja con alguna información importante y no usa cadenas seguras, puede que tengas un problema grave.

La cuestión fundamental es que no es lo mismo realmente usar string que String, aunque en el 99.9% de los casos sí que lo sea. Y conviene tenerlo claro.

La mejor política en C# es utilizar siempre string, la palabra clave alias del tipo que realmente estamos queriendo utilizar, y no mezclar ambas maneras.

¡Espero que te resulte interesante y útil!

Nota: La verdad es que yo siempre me había quedado en lo de todo el mundo, la primera parte: que era equivalente. Y de hecho usaba el estilo Microsoft, de declarar con minúscula y usar métodos estáticos con mayúscula. Y el año pasado alguien hizo un pull request de mi proyecto MIIS (un gestor de contenidos basado en archivos, sin base de datos) en el que lo único que me cambiaba era eso, aunque lo aducía a una cuestión de estilo, sin ninguna otra consideración cuando le pregunté por qué lo había hecho. Así que investigué un poco más y prácticamente nadie le veía diferencia. Sin embargo, hace un par de meses, Jared Parsons un programador del compilador de C# en Microsoft sacó este estupendo artículo en su blog hablando precisamente de este problema, y me di cuenta de mi error (y del de muchos programadores). Así que gracias a los dos por darme pie para escribir el artículo de hoy 😊

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor

Escrito por un humano, no por una IA