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):
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):
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 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 😊