Jason. Cała informatyka w jednym miejscu!

"CSharp" jeszcze raz złapany w światła reflektorów ;). A dzisiaj staniemy nieco dłużej nad jednym z podstawowych typów danych jakim jest "string" w języku C#, czyli łańcuch znaków. Prócz poznania kilku dodatkowych szczegółów nieukazanych wcześniej, udowodnię Wam dlaczego on w rzeczywistości powinien być traktowany jak typ referencyjny, a nie znakowy :O. Mówię Wam, wbijcie do środka, poczytajcie i uchrońcie się w ten sposób od stałego popełniania jednego z kardynalnych błędów.

"STRING" W JĘZYKU C# TO TAKI "STRING" JAK KAŻDY INNY

Nie ma najmniejszych wątpliwości, że omawiany typ jest zdolny do przechowywania łańcuchów znaków, czyli znanych (mam nadzieję już wszystkim) literałów odgradzanych od reszty świata cudzysłowami:

string s = "Mój łańcuch znaków.";

Na dzień dobry przypomnienie: łańcuch znaków jest czasem mylnie nazywany "tekstem". Błąd, bo zawarte w nim znaki wcale nie muszą przypominać tekstu. Znaki nie muszą być nawet literami. Czy to można nazwać tekstem?

string s = "?!$*%^@#";

Nie, a to jest nadal łańcuch znaków.

"STRING" JEST TYLKO JEDEN

W C# natraficie na kilka bardzo podobnych do siebie nazw określających typ łańcuchowy. Oprócz "string" w języku C# istnieje także "System.String" oraz "String". Najpierw odpowiedź na najgorętsze pytanie: NIE, to nie są żadne odmiany łańcucha znaków! Wszystkie trzy nazwy odnoszą się do tego samego:

System.String = String = string

OK? Teraz odsłaniamy nagą prawdę :P:

  • "System.String" to pełna nazwa typu "string" w języku C# wbudowana w architekturę .NET,
  • "String" to to samo co "System.String", tylko "z obciętą" przestrzenią nazw, od której ten typ pochodzi, "System". Jeżeli umieszczacie na samej górze programu dyrektywę "using", wtedy macie prawo skorzystać ze skróconej nazwy,
  • "string" to zwyczajny alias obu powyższych nazw.

Najbardziej powszechną rekomendacją jest korzystanie z aliasu pisanego małą literą, czyli "string". Być może to wynika z panującej konwencji nazewniczej. Nie ma żadnych znanych dowodów na to, że "string" pisane małą literą daje jakieś korzyści od strony efektywności lub bezpieczeństwa programu, chodzi tylko o same zmaksymalizowanie komfortu w pisaniu.

PRZEDOSTAWAJ SIĘ WYGODNIE DO KAŻDEGO ZNAKU JAK W TABLICY!

Dowiedzmy się czegoś fajniejszego. Na przykład możliwości odwołania się do dowolnego pojedynczego znaku jaki zawiera "string" w języku C#. I tu niespodzianka - nie musimy wywoływać żadnej metody. Spójrzcie i zacznijcie nie dowierzać :):

string s = "Mój łańcuch znaków.";
char lastCharacter = s[s.Length - 1];

Ale...przecież to nie jest tablica (to znaczy jest, ale nie w rozumieniu języka C#, bo nie wymaga nawiasów kwadratowych obok typu)! Racja, tylko "string" w języku C# został wzbogacony o taką notację indeksową, jakby to była tablica. Takie podejście jest całkowicie "legit" i możecie wstawić dowolny indeks, byleby nie przekraczał długości łańcucha (pomoże Wam to opanować właściwość "Length"). Wydobytym znakiem będzie w tym wypadku kropka ('.').

METODY TYPU / KLASY "STRING"

"string" w języku C# ma się czym pochwalić od strony posiadanych metod. Od poszukiwania, do manipulowania. Przyjrzyjcie się tej liście:

NAZWA ZNACZENIE
Compare Zwrócenie liczby całkowitej jako efekt porównania obu łańcuchów i ustalenia który z nich jest "większy".
Concat Zwrócenie nowego łańcucha znaków będącego połączeniem obu osobnych literałów podanych jako parametry.
Contains Zwrócenie wartości logicznej jako wynik sprawdzenia czy podany ciąg występuje w docelowym łańcuchem.
EndsWith Zwrócenie wartości logicznej jako wynik badania czy docelowy łańcuch znaków jest zakończony podanym w parametrze ciągiem.
Equals Zwrócenie wartości logicznej jako wynik weryfikacji czy oba ciągi (docelowy i podany jako parametr aktualny) są takie same.
IndexOf Zwrócenie liczby całkowitej oznaczającej indeks pierwszego wystąpienia podanego w parametrze łańcucha w łańcuchu docelowym; zwraca -1, jeśli nie znaleziono takiego wystąpienia.
Insert Zwrócenie nowego łańcucha znaków będącego wynikiem wstawienia podanego w parametrze łańcucha do docelowego łańcucha; przy pomocy parametru liczbowego można zdecydować o miejscu wstawienia np. w środek.
IsNullOrEmpty Zwrócenie wartości logicznej w zależności od tego, czy docelowy łańcuch znaków jest pusty lub przyjmuje wartość "null".
Join Zwrócenie łańcucha znaków będącego połączeniem wszystkich osobnych łańcuchów zawartych w podanej tablicy, odseparowanych od siebie podanym ciągiem w roli separatora.
LastIndexOf Zwrócenie liczby całkowitej oznaczającej indeks ostatniego wystąpienia podanego w parametrze łańcucha w łańcuchu docelowym; zwraca -1, jeśli nie znaleziono takiego wystąpienia.
Replace Zwrócenie łańcucha znaków będącego modyfikacją każdego ciągu w łańcuchu docelowym podanego jako parametr na drugi łańcuch także w postaci parametru.
Split Zwrócenie tablicy łańcuchów znaków oddzielanych od siebie separatorami podanymi w parametrze jako tablica znaków.
StartsWith Zwrócenie wartości logicznej jako wynik badania czy docelowy łańcuch znaków rozpoczyna się podanym ciągiem jako parametr aktualny.
Substring Zwrócenie łańcucha znaków w charakterze "wycinka" tego samego łańcucha od podanego w parametrze indeksu (od którego znaku w kolejności).
ToLower Zwrócenie łańcucha znaków będącego modyfikacją wszystkich liter na małe (minuskuły).
ToUpper Zwrócenie łańcucha znaków będącego modyfikacją wszystkich liter na duże (majuskuły).
Trim Zwrócenie łańcucha znaków pozbawionego białych znaków na początku i na końcu.

Poszczególne przeciążenia wszystkich metod "string" w języku C# znajdują się w oficjalnej dokumentacji, wystarczy kliknąć :). Warto zwrócić uwagę, że wszystkie metody są statyczne, zatem dozwolone jest wywoływanie ich zarówno z poziomu klasy, jak i od obiektu łańcuchowego, który określiłem "docelowym"! Ponadto żadna z tych metod nie modyfikuje łańcucha bezpośrednio (jeżeli jakaś metoda zwraca edytowany łańcuch, to jako całkiem nowa kopia). Powód wyjaśniam w kolejnym podpunkcie.

TA RÓŻNICA ODDZIELA ZIARNO OD PLEW

OK, przeszliśmy do najważniejszego podpunktu dzisiejszego materiału. W mojej ocenie, świadomość tego szczegółu jest jedną z rzeczy, która odróżnia początkującego od doświadczonego, więc radzę Wam przeczytać to uważnie.

Najpierw zacznę od stwierdzenia być może szokującego faktu. "string" w języku C# to tak naprawdę typ REFERENCYJNY! Jak tłumaczyłem Wam kilkanaście wpisów do tyłu, instancja typu referencyjnego przechowuje wskaźnik w postaci adresu wskazującego na konkretne miejsce na stercie, i w taki sposób można operować na obiekcie. Zgodnie z tymi teoretycznymi wywodami, pisząc tę samą instrukcję jak zwykle:

string s = "Mój łańcuch znaków.";

tworzycie w rzeczywistości alokację pamięci na stercie w postaci tablicy o długości 19 znaków i przypisujecie zmiennej wskaźnik do niego. Oto pierwsza ważna informacja. O wiele ważniejsza jest świadomość, że mimo faktu iż język C# (i nie tylko on) dopuszcza możliwość modyfikacji łańcucha znaków, powinno się omijać tę sposobność szerokim łukiem. Wiecie co się dzieje podczas konkatenacji albo ponownego przypisania?

s = "Mój nowy łańcuch znaków.";
s += "245";

Tworzycie za każdym razem nowy łańcuch znaków, wskaźnik wskazuje od teraz na nowy / edytowany łańcuch, a tamten jest PORZUCANY przez program i ewentualnie wskazany do posprzątania po nim przez "Garbage Collector", "odśmiecacz" kontrolujący odzyskiwanie dynamicznie alokowanej pamięci. Może to doprowadzić do wycieku pamięci, sytuacji w której żaden wskaźnik nie wskazuje na nieużywany obiekt "wędrujący" sobie po stercie, przez co nie może być już nigdy zlokalizowany ani usunięty.

Tak się kończy przerażająca historyjka o prawdziwym działaniu typu "string" w języku C#. Ciąg dalszy nie nastąpi ;). A dlaczego to tak zostało skonstruowane? To już trzeba zajrzeć do korzeni języka C i przyjąć do wiadomości, że próba takiego modyfikowania kończy się naruszeniem ochrony pamięci :O!

Jaka moja rada? Skorzystać z obiektu "StringBuilder"! On pozwala na równie swobodną i BEZPIECZNĄ edycję łańcucha znaków. Wystarczy wstawić sobie dyrektywę "using" w stosunku do "System.Text" i możecie korzystać:

StringBuilder sb = new StringBuilder("Mój łańcuch znaków.");
Typ danych "string" w języku C#

"string" w języku C# to typ reprezentujący łańcuch znaków. Choć dopuszczalna jest jego modyfikacja po przypisaniu pierwotnego łańcucha, początkujący nawet się nie domyślają jakie to może nieść za sobą konsekwencje!

Obiekt "StringBuilder" w języku C#

Jeśli masz pewność, że łańcuch znaków będzie musiał zostać poddany modyfikacjom, użyj obiektu "StringBuilder"!


Dzięki serdeczne za przeczytanie! Miejcie to z tyłu głowy co Wam przekazałem na górze. Głównym powodem dlaczego poświęciłem kolejną sztukę na rzecz prawie oczywistą było gruntowne wytłumaczenie "nagiej prawdy" odnośnie natury przechowywania łańcuchów w pamięci. Zostało wyłożone na tacy dlaczego ten typ...to "ciemny typ" :P.

PODOBNE ARTYKUŁY