Database
 sql >> база данни >  >> RDS >> Database

Толкова прост ли е низовият оператор „+“?

Въведение

Типът данни низ е един от основните типове данни, заедно с числови (int, long, double) и логически (булеви). Едва ли можете да си представите поне една полезна програма, която да не използва този тип.

На платформата .NET типът низ е представен като неизменяем клас String. В допълнение, той е силно интегриран в CLR средата и също се поддържа от C# компилатора.

Тази статия е посветена на конкатенацията – операция, изпълнявана върху низове толкова често, колкото операцията за събиране на числа. Може да си помислите:„Какво има да се каже?“, в крайна сметка всички знаем за низовия оператор „+“, но както се оказа, той има свои собствени странности.

Езикова спецификация за низовия оператор „+“

Спецификацията на езика C# предоставя три претоварвания за низовия оператор „+“:

string operator + (string x, string y)

string operator + (string x, object y)

string operator + (object x, string y)

Ако един от операндите на конкатенацията на низове е NULL, се вмъква празният низ. В противен случай всеки аргумент, който не е низ, се представя като низ чрез извикване на виртуалния метод ToString. Ако методът ToString върне NULL, се вмъква празен низ. Трябва да се отбележи, че според спецификацията тази операция никога не трябва да връща NULL.

Описанието на оператора е достатъчно ясно, но ако погледнем реализацията на класа String, ще открием ясна дефиниция само на два оператора “==” и “!=”. Възниква разумен въпрос:какво се случва зад кулисите на конкатенацията на низове? Как компилаторът обработва низовия оператор “+”?

Оказа се, че отговорът на този въпрос не е толкова труден. Нека разгледаме по-отблизо статичния метод String.Concat. Методът String.Concat свързва един или повече екземпляри на класа String или изгледи като String стойности на един или повече екземпляри на Object. Има следните претоварвания на този метод:

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)

Подробности

Да предположим, че имаме следния израз s =a + b, където a и b са низове. Компилаторът го преобразува в извикване на статичен метод Concat, т.е.

s = string.Concat (a, b)

Операцията за конкатенация на низове, както всяка друга операция за добавяне в езика C#, е лява асоциативна.

Всичко е ясно с два реда, но какво ще стане, ако има повече редове? Изразът s =a + b + c, като се има предвид лявата асоциативност на операцията, може да бъде заменен с:

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

Въпреки това, като се има предвид претоварването, което приема три аргумента, то ще бъде преобразувано в:

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

Подобна ситуация е и с конкатенацията на четири низа. За да конкатенираме 5 или повече низа, имаме претоварване на string.Concat (params string[]), така че е необходимо да се вземат предвид допълнителните разходи, свързани с разпределението на паметта за масив.

Трябва да се отбележи също, че операторът за конкатенация на низове е напълно асоциативен :няма значение в кой ред конкатенираме низове, така че изразът s =a + (b + c), въпреки изрично посочения приоритет на изпълнение на конкатенацията, ще бъде обработен по следния начин

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

вместо очакваното:

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

По този начин, обобщавайки горното:операцията за конкатенация на низове винаги се представя отляво надясно и извиква статичния метод String.Concat.

Оптимизиращ компилатор за литерални низове

Компилаторът на C# има оптимизации, свързани с литерални низове. Например изразът s =“a” + “b” + c, като се има предвид лявата асоциативност на оператора “+”, е еквивалентен на s =(“a” + “b”) + c се преобразува в

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

Изразът s =c + “a” + “b”, въпреки лявата асоциативност на операцията на конкатенация (s =(c + “a”) + “b”) се преобразува в

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

Като цяло позицията на литералите няма значение, компилаторът обединява всичко, което може, и едва след това се опитва да избере подходящо претоварване на метода Concat. Изразът s =a + “b” + “c” + d се преобразува в

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

Оптимизациите, свързани с празни и NULL низове, също трябва да бъдат споменати. Компилаторът знае, че добавянето на празно жило не влияе на резултата от конкатенацията, така че изразът s =a + “” + b се преобразува в

s = string.Concat (a, b),

вместо очакваното

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

По същия начин, с константния низ, чиято стойност е NULL, имаме:

const string nullStr = null;

s = a + nullStr + b;

се преобразува в

s = string.Concat (a, b)

Изразът s =a + nullStr се преобразува в s =a ?? “”, ако a е низ и извикването на метода string.Concat(a), ако a не е низ, например s =17 + nullStr, той се преобразува в s =string.Concat (17) .

Интересна функция, свързана с оптимизирането на литералната обработка и лявата асоциативност на низовия оператор „+“.

Нека разгледаме израза:

var s1 = 17 + 17 + "abc";

като се има предвид лявата асоциативност, е еквивалентен на

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

В резултат на това по време на компилиране числата се добавят, така че резултатът ще бъде 34abc.

От друга страна, изразът

var s2 = "abc" + 17 + 17;

е еквивалентен на

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

резултатът ще бъде abc1717.

И така, един и същ оператор за конкатенация води до различни резултати.

String.Concat VS StringBuilder.Append

Трябва да кажем няколко думи за това сравнение. Нека разгледаме следния код:

string name = "Timur";

string surname = "Guev";

string patronymic = "Ahsarbecovich";

string fio = surname + name + patronymic;

Може да бъде заменен с кода с помощта на StringBuilder:

var sb = new StringBuilder ();

sb.Append (surname);

sb.Append (name);

sb.Append (patronymic);

string fio = sb.ToString ();

В този случай обаче едва ли ще извлечем ползи от използването на StringBuilder. Освен факта, че кодът стана по-малко четим, той стана повече или по-малко ефективен, тъй като реализацията на метода Concat изчислява дължината на резултантния низ и разпределя памет само веднъж, за разлика от StringBuilder, който не знае нищо за дължината на получения низ.

Внедряване на метода Concat за 3 низа:

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;

}

Оператор “+” в Java

Няколко думи за низовия оператор „+“ в Java. Въпреки че не програмирам на Java, все пак се интересувам как работи там. Компилаторът на Java оптимизира оператора „+“, така че да използва класа StringBuilder и да извика метода append.

Предишният код се преобразува в

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

Струва си да се отбележи, че те умишлено отказаха такава оптимизация в C#, Eric Lippert има публикация по тази тема. Въпросът е, че такава оптимизация не е оптимизация като такава, това е пренаписване на код. Освен това създателите на езика C# смятат, че разработчиците трябва да са запознати с аспектите на работата с класа String и, ако е необходимо, да преминат към StringBuilder.

Между другото, Ерик Липърт беше този, който работи по оптимизирането на C# компилатора, свързан с конкатенацията на низове.

Заключение

Може би на пръв поглед може да изглежда странно, че класът String не дефинира оператора „+“, докато не помислим за капацитета за оптимизация на компилатора, свързан с видимостта на по-голям кодов фрагмент. Например, ако операторът “+” е дефиниран в класа String, изразът s =a + b + c + d ще доведе до създаването на два междинни низа, едно извикване на низа.Concat (a, b, в, г) методът позволява по-ефективно извършване на конкатенацията.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да поръчам по брой в SQL?

  2. Сравняване на SQL, конструктори на заявки и ORM

  3. Прагове за оптимизиране – групиране и агрегиране на данни, част 5

  4. Свържете ODBC приложения на Windows към QuickBooks Online

  5. Модел на база данни за система за съобщения