"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.");
![]() |
"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!
![]() |
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.