sql >> Databáze >  >> RDS >> Database

Je řetězcový operátor „+“ tak jednoduchý?

Úvod

Řetězcový datový typ je jedním ze základních datových typů spolu s numerickými (int, long, double) a logickými (booleovský) datovými typy. Stěží si dokážete představit alespoň jeden užitečný program, který tento typ nevyužívá.

Na platformě .NET je typ řetězce prezentován jako neměnná třída String. Navíc je silně integrován do prostředí CLR a je podporován i kompilátorem C#.

Tento článek je věnován zřetězení – operaci prováděné s řetězci stejně často jako operace sčítání s číslicemi. Možná vás napadne:„Co se dá říct?“, koneckonců všichni známe řetězcový operátor „+“, ale jak se ukázalo, má své vlastní zvláštnosti.

Specifikace jazyka pro operátor řetězce „+“

Specifikace jazyka C# poskytuje tři přetížení operátoru řetězce „+“:

string operator + (string x, string y)

string operator + (string x, object y)

string operator + (object x, string y)

Pokud je jeden z operandů zřetězení řetězců NULL, vloží se prázdný řetězec. Jinak je jakýkoli argument, který není řetězcem, reprezentován jako řetězec voláním virtuální metody ToString. Pokud metoda ToString vrátí hodnotu NULL, vloží se prázdný řetězec. Je třeba poznamenat, že podle specifikace by tato operace nikdy neměla vrátit hodnotu NULL.

Popis operátoru je dostatečně jasný, nicméně pokud se podíváme na implementaci třídy String, najdeme jasnou definici pouze dvou operátorů „==“ a „!=“. Nabízí se rozumná otázka:co se děje v zákulisí zřetězení strun? Jak kompilátor zachází s operátorem řetězce „+“?

Ukázalo se, že odpověď na tuto otázku není tak obtížná. Podívejme se blíže na statickou metodu String.Concat. Metoda String.Concat spojuje jednu nebo více instancí třídy String nebo zobrazení jako hodnoty String jedné nebo více instancí objektu. Tato metoda má následující přetížení:

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)

Podrobnosti

Předpokládejme, že máme následující výraz s =a + b, kde a a b jsou řetězce. Kompilátor jej převede na volání statické metody Concat, tj.

s = string.Concat (a, b)

Operace zřetězení řetězců, stejně jako jakákoli jiná operace sčítání v jazyce C#, je asociativní vlevo.

Se dvěma řádky je vše jasné, ale co když je řádků více? Výraz s =a + b + c by vzhledem k levé asociativitě operace mohl být nahrazen takto:

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

Avšak vzhledem k přetížení, které vyžaduje tři argumenty, bude převedeno na:

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

Podobná situace je u zřetězení čtyř strun. Pro zřetězení 5 nebo více řetězců máme string.Concat přetížení (params string[]), takže je nutné vzít v úvahu režii spojenou s alokací paměti pro pole.

Je třeba také poznamenat, že operátor zřetězení řetězců je plně asociativní :nezáleží na tom, v jakém pořadí řetězce zřetězujeme, takže výraz s =a + (b + c), i přes výslovně uvedenou prioritu provádění zřetězení, bude zpracován následovně

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

místo očekávaného:

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

Shrnutí výše uvedeného:operace zřetězení řetězců je vždy reprezentována zleva doprava a volá statickou metodu String.Concat.

Optimalizace kompilátoru pro doslovné řetězce

Kompilátor C# má optimalizace týkající se doslovných řetězců. Například výraz s =„a“ + „b“ + c je vzhledem k levé asociativitě operátoru „+“ ekvivalentní s =(„a“ + „b“) + c se převede na

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

Výraz s =c + „a“ + „b“, navzdory levé asociativitě operace zřetězení (s =(c + „a“) ​​+ „b“) je převeden na

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

Obecně na pozici literálů nezáleží, kompilátor zřetězí vše, co může, a teprve poté se snaží vybrat vhodné přetížení metody Concat. Výraz s =a + „b“ + „c“ + d se převede na

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

Měly by být zmíněny také optimalizace spojené s prázdnými a NULL řetězci. Kompilátor ví, že přidání prázdného stingu neovlivní výsledek zřetězení, takže výraz s =a + „“ + b se převede na

s = string.Concat (a, b),

místo očekávaného

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

Podobně s řetězcem const, jehož hodnota je NULL, máme:

const string nullStr = null;

s = a + nullStr + b;

se převede na

s = string.Concat (a, b)

Výraz s =a + nullStr se převede na s =a ?? "", pokud a je řetězec, a volání metody string.Concat(a), pokud a není řetězec, například s =17 + nullStr, převede se na s =řetězec.Concat (17) .

Zajímavá funkce spojená s optimalizací doslovného zpracování a levou asociativitou řetězcového operátoru „+“.

Podívejme se na výraz:

var s1 = 17 + 17 + "abc";

vezmeme-li v úvahu levou asociativitu, je ekvivalentní k

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

Výsledkem je, že v době kompilace jsou čísla přidána, takže výsledek bude 34abc.

Na druhou stranu výraz

var s2 = "abc" + 17 + 17;

je ekvivalentní k

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

výsledkem bude abc1717.

Takže, máte to, stejný operátor zřetězení vede k různým výsledkům.

String.Concat VS StringBuilder.Append

Je nutné říci pár slov k tomuto srovnání. Podívejme se na následující kód:

string name = "Timur";

string surname = "Guev";

string patronymic = "Ahsarbecovich";

string fio = surname + name + patronymic;

Lze jej nahradit kódem pomocí StringBuilder:

var sb = new StringBuilder ();

sb.Append (surname);

sb.Append (name);

sb.Append (patronymic);

string fio = sb.ToString ();

V tomto případě však stěží získáme výhody z používání StringBuilder. Kromě toho, že se kód stal méně čitelným, stal se více či méně efektivním, protože implementace metody Concat vypočítává délku výsledného řetězce a alokuje paměť pouze jednou, na rozdíl od StringBuilder, který o délce nic neví. výsledného řetězce.

Implementace metody Concat pro 3 řetězce:

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;

}

Operátor „+“ v jazyce Java

Pár slov o řetězcovém operátoru „+“ v Javě. Sice neprogramuji v Javě, přesto mě zajímá, jak to tam funguje. Kompilátor Java optimalizuje operátor „+“ tak, aby používal třídu StringBuilder a volal metodu append.

Předchozí kód je převeden na

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

Stojí za zmínku, že záměrně odmítli takovou optimalizaci v C#, Eric Lippert má na toto téma příspěvek. Jde o to, že taková optimalizace není optimalizací jako takovou, je to přepisování kódu. Kromě toho tvůrci jazyka C# věří, že vývojáři by měli být obeznámeni s aspekty práce s třídou String a v případě potřeby přejít na StringBuilder.

Mimochodem, Eric Lippert byl ten, kdo pracoval na optimalizaci kompilátoru C# spojeného se zřetězením řetězců.

Závěr

Možná se na první pohled může zdát zvláštní, že třída String nedefinuje operátor „+“, dokud se nezamyslíme nad optimalizační kapacitou kompilátoru související s viditelností většího fragmentu kódu. Pokud by byl například operátor „+“ definován ve třídě String, výraz s =a + b + c + d by vedl k vytvoření dvou mezilehlých řetězců, jediného volání řetězce. Concat (a, b, c, d) metoda umožňuje provádět zřetězení efektivněji.


  1. Jak vytvořit offline aplikaci pro internacionalizaci:Vytvořte strukturu projektu

  2. Jak používat klauzuli HAVING v SQL

  3. desetinné (s,p) nebo číslo (s,p)?

  4. Oracle 11g - Jak optimalizovat výběr pomalé paralelní vložky?