sql >> Base de Datos >  >> RDS >> Database

¿Es el operador de cadena "+" tan simple?

Introducción

Un tipo de datos de cadena es uno de los tipos de datos fundamentales, junto con los numéricos (int, long, double) y lógicos (booleanos). Difícilmente puede imaginar al menos un programa útil que no utilice este tipo.

En la plataforma .NET, el tipo de cadena se presenta como una clase de cadena inmutable. Además, está fuertemente integrado en el entorno CLR y también es compatible con el compilador C#.

Este artículo está dedicado a la concatenación, una operación que se realiza en cadenas con tanta frecuencia como la operación de suma en números. Puede pensar:"¿Qué hay para decir?", Después de todo, todos conocemos el operador de cadena "+", pero resultó que tiene sus propias peculiaridades.

Especificación de idioma para el operador de cadena “+”

La especificación del lenguaje C# proporciona tres sobrecargas para el operador de cadena “+”:

string operator + (string x, string y)

string operator + (string x, object y)

string operator + (object x, string y)

Si uno de los operandos de la concatenación de cadenas es NULL, se inserta la cadena vacía. De lo contrario, cualquier argumento, que no sea una cadena, se representa como una cadena llamando al método virtual ToString. Si el método ToString devuelve NULL, se inserta una cadena vacía. Cabe señalar que, de acuerdo con la especificación, esta operación nunca debe devolver NULL.

La descripción del operador es bastante clara, sin embargo, si observamos la implementación de la clase String, encontramos una definición clara de solo dos operadores “==” y “!=”. Surge una pregunta razonable:¿qué sucede detrás de escena de la concatenación de cadenas? ¿Cómo maneja el compilador el operador de cadena “+”?

La respuesta a esta pregunta resultó no ser tan difícil. Echemos un vistazo más de cerca al método estático String.Concat. El método String.Concat une una o más instancias de la clase String o visualiza como valores String de una o más instancias de Object. Existen las siguientes sobrecargas de este método:

public static String Concat (String str0, String str1)

public static String Concat (String str0, String str1, String str2)

public static String Concat (String str0, String str1, String str2, String str3)

public static String Concat (params String[] values)

public static String Concat (IEnumerable <String> values)



public static String Concat (Object arg0)

public static String Concat (Object arg0, Object arg1)

public static String Concat (Object arg0, Object arg1, Object arg2)

public static String Concat (Object arg0, Object arg1, Object arg2, Object arg3, __arglist)



public static String Concat <T> (IEnumerable <T> values)

Detalles

Supongamos que tenemos la siguiente expresión s =a + b, donde a y b son cadenas. El compilador lo convierte en una llamada de un método estático Concat, es decir,

s = string.Concat (a, b)

La operación de concatenación de cadenas, como cualquier otra operación de adición en el lenguaje C#, es asociativa por la izquierda.

Todo está claro con dos filas, pero ¿y si hay más filas? La expresión s =a + b + c, dada la asociatividad por la izquierda de la operación, podría ser reemplazada por:

s = string.Concat(string.Concat (a, b), c)

Sin embargo, dada la sobrecarga que requiere tres argumentos, se convertirá en:

s = string.Concat (a, b, c)

La situación similar es con la concatenación de cuatro cadenas. Para concatenar 5 o más cadenas, tenemos la sobrecarga string.Concat (params string[]), por lo que es necesario tener en cuenta la sobrecarga asociada con la asignación de memoria para una matriz.

También se debe tener en cuenta que el operador de concatenación de cadenas es totalmente asociativo :no importa en qué orden concatenemos las cadenas, por lo que la expresión s =a + (b + c), a pesar de la prioridad de ejecución de la concatenación explícitamente indicada, se procesará de la siguiente manera

s = (a + b) + c = string.Concat (a, b, c)

en lugar de lo esperado:

s = string.Concat (a, string.Concat (b, c))

Así, resumiendo lo anterior:la operación de concatenación de cadenas siempre se representa de izquierda a derecha y llama al método estático String.Concat.

Optimización del compilador para cadenas literales

El compilador de C# tiene optimizaciones relacionadas con cadenas literales. Por ejemplo, la expresión s =“a” + “b” + c, dada la asociatividad por la izquierda del operador “+”, es equivalente a s =(“a” + “b”) + c se convierte en

s = string.Concat ("ab", c)

La expresión s =c + “a” + “b”, a pesar de la asociatividad por la izquierda de la operación de concatenación (s =(c + “a”) + “b”) se convierte en

s = string.Concat (c, "ab")

En general, la posición de los literales no importa, el compilador concatena todo lo que puede y solo entonces intenta seleccionar una sobrecarga apropiada del método Concat. La expresión s =a + “b” + “c” + d se convierte en

s = string.Concat (a, "bc", d)

También se deben mencionar las optimizaciones asociadas con cadenas vacías y NULL. El compilador sabe que agregar una cadena vacía no afecta el resultado de la concatenación, por lo que la expresión s =a + “” + b se convierte en

s = string.Concat (a, b),

en lugar de lo esperado

s = string.Concat (a, "", b)

De manera similar, con la cadena const, cuyo valor es NULL, tenemos:

const string nullStr = null;

s = a + nullStr + b;

se convierte en

s = string.Concat (a, b)

La expresión s =a + nullStr se convierte en s =a ?? “”, si a es una cadena, y la llamada del método string.Concat(a), si a no es una cadena, por ejemplo, s =17 + nullStr, se convierte en s =string.Concat (17) .

Una característica interesante asociada con la optimización del procesamiento literal y la asociatividad por la izquierda del operador de cadena "+".

Consideremos la expresión:

var s1 = 17 + 17 + "abc";

teniendo en cuenta la asociatividad por la izquierda, es equivalente a

var s1 = (17 + 17) + "abc"; // сalling the string.Concat method (34, "abc")

Como resultado, en el momento de la compilación, se suman los números, por lo que el resultado será 34abc.

Por otro lado, la expresión

var s2 = "abc" + 17 + 17;

es equivalente a

var s2 = ( "abc" + 17) + 17; // calling the string.Concat method ("abc", 17, 17)

el resultado será abc1717.

Entonces, ahí lo tienes, el mismo operador de concatenación conduce a diferentes resultados.

String.Concat VS StringBuilder.Append

Es necesario decir algunas palabras sobre esta comparación. Consideremos el siguiente código:

string name = "Timur";

string surname = "Guev";

string patronymic = "Ahsarbecovich";

string fio = surname + name + patronymic;

Se puede reemplazar con el código usando StringBuilder:

var sb = new StringBuilder ();

sb.Append (surname);

sb.Append (name);

sb.Append (patronymic);

string fio = sb.ToString ();

Sin embargo, en este caso, difícilmente obtendremos beneficios al usar StringBuilder. Aparte del hecho de que el código se ha vuelto menos legible, se ha vuelto más o menos efectivo, ya que la implementación del método Concat calcula la longitud de la cadena resultante y asigna memoria solo una vez, en contraste con StringBuilder que no sabe nada sobre la longitud. de la cadena resultante.

Implementación del método Concat para 3 cadenas:

public static string Concat (string str0, string str1, string str2)

{

if (str0 == null && str1 == null && str2 == null)

return string.Empty;

if (str0 == null)

str0 = string.Empty;

if (str1 == null)

str1 = string.Empty;

if (str2 == null)

str2 = string.Empty;

string dest = string.FastAllocateString (str0.Length + str1.Length + str2.Length); // Allocate memory for strings

string.FillStringChecked (dest, 0, str0); /

string.FillStringChecked (dest, str0.Length, str1);

string.FillStringChecked (dest, str0.Length + str1.Length, str2);

return dest;

}

Operador “+” en Java

Algunas palabras sobre el operador de cadena “+” en Java. Aunque no programo en Java, estoy interesado en cómo funciona allí. El compilador de Java optimiza el operador "+" para que use la clase StringBuilder y llame al método de agregar.

El código anterior se convierte en

String fio = new StringBuilder(String.valueOf(surname)).append(name).append (patronymic).ToString()

Vale la pena señalar que rechazaron intencionalmente dicha optimización en C #, Eric Lippert tiene una publicación sobre este tema. El punto es que dicha optimización no es la optimización como tal, es la reescritura de código. Además, los creadores del lenguaje C# creen que los desarrolladores deben estar familiarizados con los aspectos de trabajar con la clase String y, si es necesario, cambiar a StringBuilder.

Por cierto, Eric Lippert fue quien trabajó en la optimización del compilador de C# asociado con la concatenación de cadenas.

Conclusión

Quizás, a primera vista, pueda parecer extraño que la clase String no defina el operador “+” hasta que pensamos en la capacidad de optimización del compilador relacionada con la visibilidad de un fragmento de código más grande. Por ejemplo, si el operador “+” estuviera definido en la clase String, la expresión s =a + b + c + d daría lugar a la creación de dos cadenas intermedias, una sola llamada de la cadena. Concat (a, b, c, d) método permite realizar la concatenación de manera más eficaz.