MetodyPrezentacje metod string zaczniemy od metod statycznych.
Przy okazji metody jak “Equals()”, ”Intern()” czy “Copy()” poruszyły ciekawe aspekty klasy String.
Metoda String.Compare()
Metoda ta porównuje dwa stringi. Jeśli są sobie równe zwraca liczbę 0. Jeśli string “A” jest bliżej alfabetycznie od “B” to metoda zwróci liczbę ujemną. Gdy string “A” jest dalej w kolejności alfabetycznej od “B” to metoda zwróci liczbę dodatnią.
string.Compare(A,B);
string tak = "D:\\Programy\\Cezary\\";
string tak2 = @"D:\Programy\Cezary\";
string s1 = "abcde";
string s2 = "ABCDE";
int[] wyniki = new int[6];
wyniki[0] = string.Compare(tak,tak2);
wyniki[1] = string.Compare("Z", "A");
wyniki[2] = string.Compare("A", "Z");
wyniki[3] = string.Compare(s1,s2);
wyniki[4] = string.Compare(s2, s1);
//porównanie z ignorowaniem wielkości liter
wyniki[5] = string.Compare(s1, s2, true);
Porównywanie łańcuchów jest to jedna z operacji leksykalnych, których działanie jest zależne od ustawień regionalnych. Jeśli ustawienia regionalne wskazują na Polskę, metoda Compare interpretuje znak “a” jako mniejszy niż “A”
Co wykazuje ilustracja z wynikami poniżej.
Mogę porównać oba napisy ignorując wielkość liter. Wtedy napisy S1 i S2 są równe.
Metoda String.CompareOrdinal()
Bliźniacza metoda CompareOrdinal() porównuje znaki według wartości numerycznych dlatego niezależnie od ustawień regionalnych ,metoda ocenia “a” jako większe od “A”.
int[] wyniki2 = new int[5];
wyniki2[0] = string.CompareOrdinal(tak, tak2);
wyniki2[1] = string.CompareOrdinal("Z", "A");
wyniki2[2] = string.CompareOrdinal("A", "Z");
wyniki2[3] = string.CompareOrdinal(s1, s2);
wyniki2[4] = string.CompareOrdinal(s2, s1);
Nie wiem jak wytłumaczyć, ale teraz widać , że metoda zwraca inne wartości liczbowe niż 1 i –1. Może dlatego , że teraz występuje porównanie przy pomocy wartości numerycznych.
Tak czy siak, wartości liczbowe oznaczają w napisach ile znaków przed lub za występuje pomiędzy znakami.
Znaki są porównywane do momentu ,aż ich zabraknie ,albo do momentu, gdy nie równość zostanie znaleziona.
Metoda String.Concat()
Mało użyteczna metoda.Jej działanie jest zbliżone do operatora “+” dla string.
string s3 = "Mortal";
string s4 = "Kombat";
string s5 = ":Ultmiate";
string[] s_re_1 = new string[3];
s_re_1[0] = string.Concat(s3, s4, s5);
s_re_1[1] = s3 + s4 + s5;
System.Collections.Generic.IEnumerable<string> GeS =
new string[] {"Street","Fighter",":Gold"};
s_re_1[2] = string.Concat(GeS);
Jedyne co mnie zaciekawiło to możliwość dodania wszystkich napisów z kolekcji dziedziczącej po interfejsie “IEnumerable”. Fakt tego operator “+” nie może zrobić.
Przejdźmy dalej.
Metoda String.Copy()
Metoda kopiująca napisy.
C#string s6 = "Zdzisław Bohater Galaktyki";
string s6_t = s6;
string s7 = string.Copy(s6);
s6 = "0";
W sumie przyrównanie też kopiuje dany napis więc metoda nie jest aż tak użyteczna. Może kod jest bardziej czytelny.
Klasa string jest typem referencyjnym i z tego powodu ktoś może mógłby się spodziewać innego zachowania przy przyrównaniu.Chociaż nie.
Różnica jednak jest. Otóż. Metoda Copy() tworzy drugi kompletnie oddzielny łańcuch z inną referencją. Natomiast przy przyrównaniu oba łańcuchy wskazują na tą samo referencje. Czyli operacja w metodzie Copy bardziej obciąża stertę, ponieważ tworzy drugą referencje.
Jednak dlaczego w czasie zmiany jednego napisu nie zmienia się też drugi napis, skoro oba mają tę samą referencje. Otóż każda taka operacja na string jak np. przyrównanie do nowej wartości tworzy zupełnie inną referencje nie modyfikując poprzedniej. Dlatego nie ma niczego kosmicznego w ilustracji powyżej.
Jednym słowem ta metoda jest jeszcze bardziej bezużyteczna niż metoda Concat. Chociaż porusza tematykę “niezmiennych typów referencyjnych” (immutable reference types).
Metoda String.Format()
Tematyka tej metody jest tak obszerna ,że stworzyłem specjalnie dla niej oddzielny wpis.
Metoda Equals()
Z tą metodą zawsze są jakieś problemy. Podobno nie można jej ufać. No cóż zobaczmy jak ona działa dla typów string.
Metoda ta sprawdza czy oba napisy mają dokładnie tą samą wartość.
string s8 = "MegaPożeraczeTkanek";
string s9 = s8;
bool[] rowne = new bool[4];
rowne[0] = string.Equals(s8, s9); //te same referencje s8 = "PogromcaPożeraczy";
s9 = new StringBuilder().Append("Pogromca").Append("Pożeraczy").ToString();
rowne[1] = s8.Equals(s9); //różne referencja
rowne[2] = s8 == s9; //użycie operatora ==
object o = s8;
object o1 = s9;
rowne[3] = object.Equals(o, o1);
//polimorfizm uruchamia się metoda z klasy string
Wszystkie operacje zwracają true. Operator równości (==) działa tak samo, jak metoda Equals() zresztą nawet powinna. Operator ten jest przeciążony w klasie “string” więc ona nie sprawdza czy dany obiekt wskazuje na tą samą referencje. Metoda, jak i operator sprawdza czy dany tekst jest taki sam. W końcu s8 i s9 za drugim razem mają te same wartości tekstowe ,ale na pewno mają różne referencje.
s8 = "PogromcaPożeraczy";
s9 = "PogromcaPożeraczy"
Musiałem tutaj skorzystać z klasy StringBulider aby utworzyć dynamicznie napis string. ponieważ widać kompilator jest dosyć “mądry” i dla tych samych wartości statycznych tekstowych daje dokładnie tą samą referencje. A właściwie to istnieje w ogóle inny mechanizm przetrzymywania takich napisów w programie o czym dowiedziałem się przy okazji w metody “Inter”
Nie widzę tutaj jakiegoś błędnego zachowania. Poza tym , że rzeczywiście metoda Equals() działa inaczej w klasie string niż w innej klasie.
Warto zaznaczyć ,że gdy rzutujemy oba napisy i wykonamy metodę Equals() z klasy object ,a nie z klasy string wtedy będziemy sprawdzać czy obiekty mają taką samą referencje.
Aby wywołać metodę Equals() z klasy object trzeba trochę się napracować w końcu, jeśli przypiszemy napis do obiektu to wciąż zadziała nam polimorfizm (uruchomi się metoda z klasy string, co widać w kodzie powyżej). Na szczęście rzutowanie i operator (==) załatwia sprawę
C#object o = (object)s8;
object o1 = s9;
rowne[3] = (object)s8 == (object)s9;
Jednak jak sam się przekonałem istnieje pewien sposób na złamanie działania metody Equals(). To dlatego nie wolno jej do końca ufać, ponieważ nie zawsze ona działa jak trzeba. Magia polimorfizmu.
Więcej informacji o referencjach i metodzie Equals() przy metodzie Inter().
Metoda IsNullOrEmpty() i IsNullOrWithSpaces()
Metody, których celem jest sprawdzenie czy dany string nie jest pusty. Myślę ,że wyjaśnienie jak te metody działają nie jest potrzebne. Jeśli napis jest pusty to te metody zwracają true.
bool[] bbS = new bool[4];
bool[] bb = new bool[4];
bb[0] = string.IsNullOrEmpty("");
bb[1] = string.IsNullOrEmpty(null);
bb[2] = string.IsNullOrEmpty(" ");
bb[3] = string.IsNullOrEmpty("######");
bbS[0] = string.IsNullOrWhiteSpace("");
bbS[1] = string.IsNullOrWhiteSpace(null);
bbS[2] = string.IsNullOrWhiteSpace(" ");
bbS[3] = string.IsNullOrWhiteSpace("#######");
Jak widać metoda “IsNullOrWhiteSpace()” zwraca jeszcze true , gdy w napisie są same puste znaki. A tak obie metody działają prawie tak samo.
Metoda Inter
Metoda ta odzyskuje/odnajduje referencje dla danego napisu.
Osobiście nigdy nie korzystałem z tej metody ,a jest ona od .NET 4.0. Musiałem skorzystać z dokumentacji MSDN ,aby upewnić się , że nie powiem zaraz czegoś głupiego.
CLR przetrzymuje napisy utworzone programistycznie w programie w czymś, co nazywa się “Intern pool”. Zawiera ona pojedynczą referencje i każda z nich ma unikalny zadeklarowany literał. W tym zbiorze zawierają się też napisy, których wartość jest unikalna w całym systemie.
Czyli przy takim kodzie tak naprawdę każda z tych zmiennych ma tą samą referencje dzięki mechanice “Intern pool”.
s8 = "PogromcaPożeraczy";
s9 = "PogromcaPożeraczy";
Metoda Intern używa tego zbioru w poszukiwaniu referencji napisu, którego wartość jest taka sama jak argumentu podanego w metodzie.
Jeśli taki napis nie istnieje, to wtedy argument, który został użyty w metodzie zostaje dodany do tego zbioru i jego referencja zostaje zwrócona.
W tym przykładzie stringi s10 i s11 mają różne referencje . S10 jest już w zbiorze “Inter pool”, ponieważ została zadeklarowana programistycznie. S11 zostaje utworzony dynamicznie w czasie wykonywania programu wiec do tego zbioru nie może dołączyć , ma ona też tą samo wartość co S10.
Teraz za pomocą metody Inter() wyszukuje w zbiorze “Intern pool” referencji z taką samą wartością co S11, czyli ta metoda na pewno zwróci mi referencje S10.
string s10 = "PogromcaPożeraczy";
string s11 = new StringBuilder().Append("Pogromca").
Append("Pożeraczy").ToString();
string s12 = string.Intern(s11);
bool[] bbI = new bool[2];
bbI[0] = (object)s10 == (object)s11; //różne referencje
bbI[1] = (object)s10 == (object)s12; //te same referencje
Wywołując metodę Equal() z klasy object mogę tą sprawdzić.
Jak widzisz S12 i S10 są równe, ponieważ wskazują na tą samą referencje.
Internowanie napisów ma jednak kilka skutków ubocznych. Pamięć przeznaczona na obiekty string może być usunięta tylko przez sam CLR. Referencja w “intern pool” może istnieć nawet po tym usunięciu obiektu. Inna sprawa ,że zanim dodasz napis do tego zbioru musi być on najpierw zadeklarowany.
Metoda IsInterned()
Metoda działa podobnie jak “Inter”, chociaż jej nazwa jest trochę myląca początkowo myślałem ,że ta metoda sprawdza czy dany string jest w zbiorze “intern pool” i zwraca wartość logiczną ,ale gdzie tam.
Metoda ta zwraca referencje ze zbioru “intern pool”, którego wartość jest taka sama jak argumentu podanego w metodzie. Jednak ta metoda nie przypisuje argumentu do tego zbioru, gdy tej referencji nie znajdzie.
Logiczne rozumując referencja argumentu musi być w zbiorze “intern pool” , by ta metoda zwróciła jego referencje. Jeśli tak nie jest metoda zwróci wartość null. Teraz chyba rozumiem, dlaczego ta metoda nie zwraca wartości bool ,a natomiast musi zwracać string.
Na upartego metoda ta może zwracać wartość bool w ten sposób.
public static bool IsInternedBool(string arg)
{
return string.IsInterned(arg) != null;
}
No cóż, tak dla utrwalenie prosty przykład użycia tej metody.
string s13 = "PogromcaPożeraczy";
string s14 = new StringBuilder().Append("Pogromca")
.Append("Pożeraczy").ToString();
string s15 = new StringBuilder().Append("Słowo")
.Append("Ciałem").Append("Się").ToString();
string[] s_re_3 = new string[3];
s_re_3[0] = string.IsInterned(s13);
s_re_3[1] = string.IsInterned(s14);
s_re_3[2] = string.IsInterned(s15);
S13 jest już w zbiorze “intern pool”, S14 nie jest w tym zbiorze ,ale ma wartość, która jest zawarta w tym zbiorze. A S15 to napis z wartością, której nie ma w “intern pool” ,a on sam nie należy do tego zbioru.
Jak widać miałem rację ponieważ ostatnia metoda “IsInterned” zwróciła pustą referencje “null”.
Metoda Join
Metoda ta łączy tablice string w jeden łańcuch znaków. Chociaż w jego innych wersjach mogą to być obiekty i w każdej z nich musimy podać separator pomiędzy tymi łańcuchami.
C#string[] s_nap = new string[]
{"I","Słowo","Obiektem","Się","Stało"};
string s16 = string.Join("_",s_nap);
int[] liczby =
new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
string s17 = string.Join(" ", liczby);
object[] obiekty =
new object[] {true,1.14,1.2f,9,'g',DateTime.Now};
string s18 = string.Join("//#//", obiekty);
Wynik kodu:
Metodą tą możemy też określić, od którego indeksu ma łączyć łańcuchy oraz do ilu elementów.
Metoda Join ma też wersje, w której może przyjmować zbiór danych dziedziczących po interfejsie “IEnumerable” jedynym ograniczeniem jest podanie typu wartości w <T>. Z logicznych przyczyn w tej wersji nie możemy określić indeksu i ilości elementów.
C#List<string> listaNapisow= new List<string>()
{"Zdzisław","Bohater","Galaktyki"};
string s19 = string.Join<string>("__", listaNapisow);
Tyle na razie
Dużo było tych metod statycznych ,następnym razem zobaczymy jak działają metody niestatyczne.