Database
 sql >> Base de Dados >  >> RDS >> Database

O operador de string “+” é tão simples?

Introdução

Um tipo de dados string é um dos tipos de dados fundamentais, junto com os numéricos (int, long, double) e lógicos (booleanos). Você dificilmente pode imaginar pelo menos um programa útil que não utilize esse tipo.

Na plataforma .NET, o tipo string é apresentado como uma classe String imutável. Além disso, ele é fortemente integrado ao ambiente CLR e também é suportado pelo compilador C#.

Este artigo é dedicado à concatenação – uma operação executada em strings com a mesma frequência que a operação de adição em numerais. Você pode pensar:“O que há para dizer?”, afinal, todos nós sabemos sobre o operador de string “+”, mas como se viu, ele tem suas próprias peculiaridades.


Especificação de idioma para o operador de string “+”

A especificação da linguagem C# fornece três sobrecargas para o operador de string “+”:

string operator + (string x, string y)

string operator + (string x, object y)

string operator + (object x, string y)

Se um dos operandos da concatenação de strings for NULL, a string vazia será inserida. Caso contrário, qualquer argumento, que não seja uma string, é representado como uma string chamando o método virtual ToString. Se o método ToString retornar NULL, uma string vazia será inserida. Deve-se notar que de acordo com a especificação, esta operação nunca deve retornar NULL.

A descrição do operador é bastante clara, porém, se observarmos a implementação da classe String, encontramos uma definição clara de apenas dois operadores “==” e “!=”. Surge uma pergunta razoável:o que acontece nos bastidores da concatenação de strings? Como o compilador lida com o operador de string “+”?

A resposta a esta pergunta acabou por não ser tão difícil. Vamos dar uma olhada no método estático String.Concat. O método String.Concat une uma ou mais instâncias da classe String ou exibe como valores String de uma ou mais instâncias de Object. Existem as seguintes sobrecargas deste 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)

Detalhes

Suponha que temos a seguinte expressão s =a + b, onde aeb são strings. O compilador o converte em uma chamada de um método estático Concat, ou seja,

s = string.Concat (a, b)

A operação de concatenação de strings, como qualquer outra operação de adição na linguagem C#, é associativa à esquerda.

Tudo fica claro com duas linhas, mas e se houver mais linhas? A expressão s =a + b + c, dada a associatividade à esquerda da operação, poderia ser substituída por:

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

No entanto, dada a sobrecarga que leva três argumentos, ela será convertida em:
s = string.Concat (a, b, c)

A situação semelhante é com a concatenação de quatro strings. Para concatenar 5 ou mais strings, temos a sobrecarga string.Concat (params string[]), portanto é necessário levar em consideração o overhead associado à alocação de memória para um array.

Deve-se notar também que o operador de concatenação de strings é totalmente associativo :não importa em qual ordem concatenamos as strings, então a expressão s =a + (b + c), apesar da prioridade de execução da concatenação explicitamente indicada, deve ser processada da seguinte forma

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

em vez do esperado:
s = string.Concat (a, string.Concat (b, c))

Assim, resumindo o anterior:a operação de concatenação de strings é sempre representada da esquerda para a direita e chama o método estático String.Concat.

Otimizando o compilador para strings literais

O compilador C# tem otimizações relacionadas a strings literais. Por exemplo, a expressão s =“a” + “b” + c, dada a associatividade à esquerda do operador “+”, é equivalente a s =(“a” + “b”) + c é convertida para

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

A expressão s =c + “a” + “b”, apesar da associatividade à esquerda da operação de concatenação (s =(c + “a”) + “b”) é convertida para
s = string.Concat (c, "ab")

Em geral, a posição dos literais não importa, o compilador concatena tudo o que pode, e só então tenta selecionar uma sobrecarga apropriada do método Concat. A expressão s =a + “b” + “c” + d é convertida para
s = string.Concat (a, "bc", d)

As otimizações associadas a strings vazias e NULL também devem ser mencionadas. O compilador sabe que adicionar um sting vazio não afeta o resultado da concatenação, então a expressão s =a + “” + b é convertida em
s = string.Concat (a, b),

em vez do esperado
s = string.Concat (a, "", b)

Da mesma forma, com a string const, cujo valor é NULL, temos:
const string nullStr = null;

s = a + nullStr + b;

é convertido para
s = string.Concat (a, b)

A expressão s =a + nullStr é convertida em s =a ?? “”, se a for uma string, e a chamada do método string.Concat(a), se a não for uma string, por exemplo, s =17 + nullStr, ele é convertido em s =string.Concat (17) .

Um recurso interessante associado à otimização do processamento literal e à associatividade à esquerda do operador string “+”.

Vamos considerar a expressão:

var s1 = 17 + 17 + "abc";

levada em consideração a associatividade de esquerda, é equivalente a
var s1 = (17 + 17) + "abc"; // сalling the string.Concat method (34, "abc")

Como resultado, em tempo de compilação, os numerais são adicionados, de modo que o resultado será 34abc.

Por outro lado, a expressão

var s2 = "abc" + 17 + 17;

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

o resultado será abc1717.

Então, pronto, o mesmo operador de concatenação leva a resultados diferentes.

String.Concat VS StringBuilder.Append

É necessário dizer algumas palavras sobre esta comparação. Vamos considerar o seguinte código:

string name = "Timur";

string surname = "Guev";

string patronymic = "Ahsarbecovich";

string fio = surname + name + patronymic;

Ele pode ser substituído pelo código usando StringBuilder:
var sb = new StringBuilder ();

sb.Append (surname);

sb.Append (name);

sb.Append (patronymic);

string fio = sb.ToString ();

Porém, neste caso, dificilmente obteremos benefícios com o uso do StringBuilder. Além do fato de o código ter se tornado menos legível, ele se tornou mais ou menos eficaz, pois a implementação do método Concat calcula o comprimento da string resultante e aloca memória apenas uma vez, ao contrário do StringBuilder que não sabe nada sobre o comprimento da cadeia resultante.

Implementação do método Concat para 3 strings:

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 “+” em Java

Algumas palavras sobre o operador string “+” em Java. Embora eu não programe em Java, ainda estou interessado em como funciona lá. O compilador Java otimiza o operador “+” para que ele use a classe StringBuilder e chame o método append.

O código anterior é convertido para

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

Vale a pena notar que eles recusaram intencionalmente tal otimização em C#, Eric Lippert tem um post sobre este tópico. O ponto é que essa otimização não é a otimização como tal, é a reescrita de código. Além disso, os criadores da linguagem C# acreditam que os desenvolvedores devem estar familiarizados com os aspectos de trabalhar com a classe String e, se necessário, mudar para StringBuilder.

A propósito, Eric Lippert foi quem trabalhou na otimização do compilador C# associado à concatenação de strings.

Conclusão

Talvez, à primeira vista, possa parecer estranho que a classe String não defina o operador “+” até pensarmos na capacidade de otimização do compilador relacionada à visibilidade de um fragmento de código maior. Por exemplo, se o operador “+” foi definido na classe String, a expressão s =a + b + c + d levaria à criação de duas strings intermediárias, uma única chamada da string.Concat (a, b, c, d) permite realizar a concatenação de forma mais eficaz.