Dlaczego domyślna wartość typu ciągu jest pusta zamiast pustego?


218

Jest to dość denerwujące, aby przetestować wszystkie moje struny dla nullprzed mogę bezpiecznie stosować metody takie jak ToUpper(), StartWith()etc ...

Gdyby domyślną wartością stringbył pusty ciąg, nie musiałbym testować i czułbym, że jest bardziej spójny z innymi typami wartości, takimi jak intlub doublena przykład. Dodatkowo Nullable<String>miałoby to sens.

Dlaczego więc projektanci C # zdecydowali się użyć nulljako domyślnej wartości ciągów?

Uwaga: odnosi się to do tego pytania , ale bardziej skupia się na tym, dlaczego, a nie co z tym zrobić.


53
Czy uważasz to za problem w przypadku innych typów referencji?
Jon Skeet

17
@JonSkeet Nie, ale tylko dlatego, że początkowo błędnie myślałem, że łańcuchy są typami wartości.
Marcel

21
@Marcel: To całkiem dobry powód, aby się nad tym zastanawiać.
TJ Crowder

7
@JonSkeet Tak. O tak. (Ale nie jest ci obca dyskusja na temat niedopuszczalnego odniesienia ...)
Konrad Rudolph

7
Uważam, że lepiej byś spędził czas, gdybyś używał twierdzeń na swoich ciągach w miejscach, w których nie spodziewałbyś się, że będą null(a także zalecam koncepcyjne traktowanie nulli opróżnianie ciągów jako różnych rzeczy). Wartość null może być gdzieś wynikiem błędu, a pusty łańcuch powinien mieć inne znaczenie.
diegoreymendez

Odpowiedzi:


312

Dlaczego domyślna wartość typu ciągu jest pusta zamiast pustego?

Ponieważ stringjest to typ odwołania, a wartością domyślną dla wszystkich typów odniesienia jest null.

Testowanie wszystkich moich ciągów pod kątem wartości zerowej jest dość denerwujące, zanim będę mógł bezpiecznie zastosować metody takie jak ToUpper (), StartWith () itp.

Jest to zgodne z zachowaniem typów referencyjnych. Przed wywołaniem członków ich instancji należy sprawdzić, czy istnieje odwołanie zerowe.

Gdyby domyślną wartością łańcucha był pusty łańcuch, nie musiałbym testować i czułem, że jest on bardziej spójny z innymi typami wartości, takimi jak na przykład int lub double.

Przypisanie wartości domyślnej do określonego typu odwołania innego niż nullspowodowałoby niespójność .

Dodatkowo Nullable<String>miałoby to sens.

Nullable<T>współpracuje z typami wartości. Godny uwagi jest fakt, że Nullablenie został wprowadzony na oryginalnej platformie .NET, więc byłoby dużo złamanego kodu, gdyby zmienili tę zasadę. ( Dzięki uprzejmości @jcolebrand )


10
@HenkHolterman Można wprowadzić całą masę rzeczy, ale po co wprowadzać tak rażącą niekonsekwencję?

4
@delnan - pytanie brzmiało „dlaczego”.
Henk Holterman

8
@HenkHolterman A „Spójność” jest obaleniem twojego punktu „ciąg może być traktowany inaczej niż inne typy referencji”.

6
@delnan: Pracując nad językiem, który traktuje łańcuch znaków jako typy wartości, i pracuję nad dotnetem przez ponad 2 lata, zgadzam się z Henkiem. Widzę to jako poważny FLAW w dotnet.
Fabricio Araujo,

1
@delnan: Można stworzyć typ wartości, który zachowuje się zasadniczo podobnie String, z wyjątkiem (1) zachowania typu wartości z posiadaniem użytecznej wartości domyślnej i (2) niefortunnej dodatkowej warstwy pośredniej boksowania za każdym razem, gdy zostanie on oddany do Object. Biorąc pod uwagę, że reprezentacja stosu stringjest unikalna, specjalne traktowanie w celu uniknięcia dodatkowego boksu nie byłoby zbyt dużym wysiłkiem (w rzeczywistości możliwość określenia niestandardowych zachowań bokserskich byłaby dobra dla innych typów).
supercat

40

Habib ma rację - ponieważ stringjest to typ odniesienia.

Ale co ważniejsze, nie musisz sprawdzać za nullkażdym razem, gdy go używasz. Prawdopodobnie powinieneś rzucić a, ArgumentNullExceptionjeśli ktoś przejdzie twoją funkcjęnull referencję, .

Oto rzecz - framework i tak rzuciłby NullReferenceExceptionza ciebie, jeśli spróbujesz wywołać .ToUpper()ciąg. Pamiętaj, że ten przypadek nadal może się zdarzyć, nawet jeśli przetestujesz argumenty, nullponieważ dowolna właściwość lub metoda obiektów przekazanych do funkcji, ponieważ parametry mogą oceniaćnull .

To powiedziawszy, sprawdzanie pustych ciągów lub wartości zerowych jest powszechną czynnością, więc zapewniają String.IsNullOrEmpty()i String.IsNullOrWhiteSpace()tylko w tym celu.


30
Nigdy nie powinieneś NullReferenceExceptionsam rzucać ( msdn.microsoft.com/en-us/library/ms173163.aspx ); rzucasz, ArgumentNullExceptionjeśli twoja metoda nie może zaakceptować wartości zerowej. Ponadto NullRef są zwykle jednym z trudniejszych wyjątków od diagnoz, gdy naprawiasz problemy, więc nie sądzę, aby zalecenie, aby nie sprawdzać null, jest bardzo dobre.
Andy,

3
@Andy „NullRef to zazwyczaj jeden z najtrudniejszych wyjątków do zdiagnozowania”. Zdecydowanie się nie zgadzam, jeśli logujesz rzeczy, to naprawdę łatwo je znaleźć i naprawić (wystarczy poradzić sobie z przypadkiem zerowym).
Louis Kottmann

6
Rzucanie ArgumentNullExceptionma tę dodatkową zaletę, że może podać nazwę parametru. Podczas debugowania oszczędza to ... err, sekundy. Ale ważne sekundy.
Kos

2
@DaveMarkle, możesz również chcieć dołączyć IsNullOrWhitespace msdn.microsoft.com/en-us/library/…
Nathan Koop

1
Naprawdę myślę, że sprawdzanie wartości null wszędzie jest źródłem ogromnego rozdęcia kodu. jest brzydka i wygląda na zrzędliwą i trudno jest zachować spójność. Myślę, że (przynajmniej w językach podobnych do C #) dobrą zasadą jest „zakazać słowa kluczowego null w kodzie produkcyjnym, używać go jak szalonego w kodzie testowym”.
sara

24

Możesz napisać metodę rozszerzenia (na ile jest warta):

public static string EmptyNull(this string str)
{
    return str ?? "";
}

Teraz działa to bezpiecznie:

string str = null;
string upper = str.EmptyNull().ToUpper();

100
Ale proszę nie. Ostatnią rzeczą, którą inny programista chce zobaczyć, jest tysiące wierszy kodu wypełnionych .EmptyNull () wszędzie, tylko dlatego, że pierwszy facet był „wystraszony” na wyjątki.
Dave Markle

15
@DaveMarkle: Ale oczywiście tego właśnie szukał OP. „Testowanie wszystkich moich ciągów pod kątem wartości zerowej jest dość denerwujące, zanim mogę bezpiecznie zastosować metody takie jak ToUpper (), StartWith () itp.”
Tim Schmelter,

19
Komentarz był do OP, nie do ciebie. Podczas gdy twoja odpowiedź jest wyraźnie poprawna, programista zadający podstawowe pytanie, takie jak to, powinien być ostroŜnie ostroŜny przed faktycznym wdroŜeniem twojego rozwiązania w SZEROKĄ praktykę, jak to często bywa. Istnieje wiele kompromisów, których nie omawiasz w swojej odpowiedzi, takich jak nieprzezroczystość, zwiększona złożoność, trudność refaktoryzacji, potencjalne nadużywanie metod rozszerzenia i tak, wydajność. Czasami (wiele razy) poprawna odpowiedź nie jest właściwą ścieżką i dlatego skomentowałem.
Dave Markle

5
@Andy: Rozwiązaniem problemu braku prawidłowego sprawdzania wartości zerowej jest poprawne sprawdzenie wartości zerowych, a nie umieszczanie pomocy dla problemu.
Dave Markle

7
Jeśli masz problem z pisaniem .EmptyNull(), dlaczego po prostu nie użyć (str ?? "")tego, gdzie jest to potrzebne? To powiedziawszy, zgadzam się z sentymentem wyrażonym w komentarzu @ DaveMarkle: prawdopodobnie nie powinieneś. nulli String.Emptysą koncepcyjnie różne i nie można traktować jednego tak samo jak drugiego.
CVn

17

Można również użyć następujących, począwszy od C # 6.0

string myString = null;
string result = myString?.ToUpper();

Wynik ciągu będzie pusty.


1
Prawdę mówiąc, od wersji c # 6.0 wersja IDE nie ma z tym nic wspólnego, ponieważ jest to funkcja językowa.
Stijn Van Antwerpen

3
Inna opcja -public string Name { get; set; } = string.Empty;
Jaja Harris

Jak to się nazywa? myString? .ToUpper ();
Hunter Nelson

1
Nazywa się to operatorem warunkowym zerowym. Możesz przeczytać o tym tutaj msdn.microsoft.com/en-us/magazine/dn802602.aspx
russelrillema

14

Puste łańcuchy i wartości null są zasadniczo różne. Wartość null to brak wartości, a pusty ciąg to wartość, która jest pusta.

Język programowania przyjmujący założenia dotyczące „wartości” zmiennej, w tym przypadku pustego łańcucha, będzie tak dobry, jak inicjowanie łańcucha z jakąkolwiek inną wartością, która nie spowoduje problemu z zerowym odwołaniem.

Ponadto, jeśli przekażesz uchwyt tej zmiennej ciągowej do innych części aplikacji, wówczas kod ten nie będzie miał możliwości sprawdzenia, czy celowo przekazałeś pustą wartość, czy zapomniałeś wypełnić wartość tej zmiennej.

Innym przypadkiem, w którym może to być problem, jest to, że ciąg znaków jest wartością zwracaną przez jakąś funkcję. Ponieważ ciąg znaków jest typem referencyjnym i technicznie może mieć zarówno wartość pustą, jak i pustą, dlatego funkcja może również zwrócić wartość zerową lub pustą (nic nie stoi na przeszkodzie, aby to zrobić). Teraz, ponieważ istnieją 2 pojęcia „braku wartości”, tj. Pusty ciąg i zero, cały kod, który korzysta z tej funkcji, będzie musiał wykonać 2 kontrole. Jeden za pusty, a drugi za zerowy.

Krótko mówiąc, zawsze dobrze jest mieć tylko 1 reprezentację dla jednego stanu. Aby uzyskać szerszą dyskusję na temat wartości pustych i zerowych, zobacz poniższe linki.

/software/32578/sql-empty-string-vs-null-value

NULL vs Pusty w przypadku wprowadzania danych przez użytkownika


2
A jak dokładnie widzisz tę różnicę, powiedzmy w polu tekstowym? Czy użytkownik zapomniał wpisać wartość w polu, czy celowo pozostawia ją pustą? Null w języku programowania ma określone znaczenie; nieprzypisany. Wiemy, że nie ma wartości, która nie jest taka sama jak null bazy danych.
Andy,

1
nie ma dużej różnicy, gdy używasz go z polem tekstowym. Tak czy inaczej, najważniejsza jest jedna notacja reprezentująca brak wartości w ciągu. Gdybym musiał wybrać jeden, wybrałbym zero.
Nerrve,

W Delphi łańcuch znaków jest typem wartości i dlatego nie może być pusty. Pod tym względem życie jest o wiele łatwiejsze - naprawdę denerwuję się, jeśli ciąg znaków jest typem referencyjnym.
Fabricio Araujo,

1
Zgodnie z modelem COM (Common Object Model), który był wcześniejszy niż .net, typ łańcucha zawierałby wskaźnik do danych łańcucha lub nullreprezentowałby pusty łańcuch. Istnieje wiele sposobów .net mógł zaimplementować podobną semantykę, gdyby tak postanowili, szczególnie biorąc pod uwagę, że Stringma szereg cech, które czynią go unikalnym typem [np. To i dwa typy tablic są jedynymi typami, których alokacja rozmiar nie jest stały].
supercat

7

Podstawowym powodem / problemem jest to, że projektanci specyfikacji CLS (która definiuje sposób interakcji języków z .net) nie zdefiniowali sposobu, w jaki członkowie klasy mogliby określić, że muszą być wywoływani bezpośrednio, a nie za pośrednictwemcallvirt , bez wykonującego kontrola zerowego odniesienia; nie dostarczył też wielu definicji struktur, które nie podlegałyby „normalnemu” boksu.

Gdyby specyfikacja CLS zdefiniowała takie środki, to .net byłby w stanie konsekwentnie podążać za wyprzedzeniem ustalonym przez Common Object Model (COM), zgodnie z którym odwołanie do ciągu zerowego traktowano semantycznie jako równoważne pustemu ciągowi, a dla innych zdefiniowane przez użytkownika niezmienne typy klas, które powinny mieć semantykę wartości, aby podobnie definiować wartości domyślne. Zasadniczo to, co by się stało, byłoby dla każdego członka String, np. LengthNapisane jako coś w rodzaju [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }. Takie podejście zapewniłoby bardzo dobrą semantykę dla rzeczy, które powinny zachowywać się jak wartości, ale z powodu problemów z implementacją muszą być przechowywane na stercie. Największą trudnością przy takim podejściu jest to, że semantyka konwersji między takimi typami Objectmoże być nieco mętna.

Alternatywnym podejściem byłoby zezwolenie na definicję specjalnych typów struktur, które nie dziedziczyły, Objectale zamiast tego miały niestandardowe operacje boksu i rozpakowania (które konwertowałyby na / z jakiegoś innego typu klasy). Przy takim podejściu istniałby typ klasy, NullableStringktóry zachowuje się jak ciąg znaków, oraz niestandardowy typ struktury String, który przechowywałby pojedyncze prywatne pole Valuetypu String. Próba konwersji Stringna NullableStringlub Objectzwróci, Valuejeśli nie jest zerowa lub String.Emptyjeśli jest zerowa. Próba Stringużycia rzutowania na odwołanie inne niż null do NullableStringinstancji spowoduje zapisanie odwołania w Value(być może przechowywanie wartości null, jeśli długość wynosi zero); rzutowanie dowolnego innego odwołania spowodowałoby wyjątek.

Chociaż ciągi muszą być przechowywane na stercie, nie ma koncepcyjnego powodu, dla którego nie powinny zachowywać się jak typy wartości o wartości domyślnej innej niż null. Przechowywanie ich jako „normalnej” struktury, która zawierała odniesienie, byłoby skuteczne w przypadku kodu, który używał ich jako „łańcucha” typu, ale dodałoby dodatkową warstwę pośredniczości i nieefektywności podczas rzutowania na „obiekt”. Chociaż nie przewiduję dodania żadnej z powyższych funkcji w późniejszym terminie, być może projektanci przyszłych ram mogą rozważyć ich włączenie.


1
Mówiąc jak ktoś, kto dużo pracuje w SQL i poradził sobie z bólem głowy Oracle, nie czyniąc rozróżnienia między NULL a zerową długością, bardzo się cieszę, że .NET to robi . „Pusty” to wartość, „null” nie.

@JonofAllTrades: Nie zgadzam się. W kodzie aplikacji, oprócz obsługi kodu db, nie ma znaczenia, że ​​ciąg traktowany jest jak klasa. Jest to typ wartości i podstawowy. Supercat: +1 dla Ciebie
Fabricio Araujo

1
Kod bazy danych jest dużym „wyjątkiem”. Tak długo, jak istnieją pewne problematyczne domeny, w których należy rozróżnić „obecny / znany, pusty ciąg” i „nieobecny / nieznany / nie dotyczy”, takie jak bazy danych, język musi go obsługiwać. Oczywiście teraz, gdy ma .NET Nullable<>, ciągi znaków mogą zostać ponownie zaimplementowane jako typy wartości; Nie mogę mówić o kosztach i korzyściach takiego wyboru.

3
@JonofAllTrades: Kod zajmujący się liczbami musi mieć pozapasmowy sposób odróżnienia wartości domyślnej zero od „niezdefiniowanej”. W tej chwili kod obsługujący wartości zerowe, który działa z ciągami i liczbami, musi używać jednej metody dla łańcuchów zerowych, a drugiej dla liczb zerowych. Nawet jeśli typ klasy zerowalnej stringjest bardziej wydajny niż Nullable<string>byłby, konieczność zastosowania metody „bardziej wydajnej” jest bardziej uciążliwa niż możliwość zastosowania tego samego podejścia dla wszystkich wartości dopuszczalnych zerowych danych.
supercat

5

Ponieważ zmienna łańcuchowa jest odwołaniem , a nie instancją .

Domyślnie zainicjowanie go do Opróżnij byłoby możliwe, ale wprowadziłoby wiele niespójności na całym forum.


3
Nie ma konkretnego powodu, stringże musiałby to być typ odniesienia. Oczywiście, rzeczywiste znaki, które składają się na ciąg, z pewnością muszą być przechowywane na stercie, ale biorąc pod uwagę ilość dedykowanego wsparcia, jakie ciągi mają już w CLR, nie byłoby rozciągnięciem mieć System.Stringtyp wartości z wartością pojedyncze prywatne pole Valuetypu HeapString. To pole byłoby typem referencyjnym i domyślnie tak null, ale Stringstruktura, której Valuepole było puste, zachowywałaby się jak pusty ciąg. Jedyną wadą tego podejścia byłoby ...
supercat

1
... że rzutowanie Stringna Objectto, przy braku kodu specjalnego przypadku w środowisku wykonawczym, spowoduje utworzenie Stringinstancji pudełkowej na stercie, zamiast po prostu kopiowania odwołania do pliku HeapString.
supercat

1
@ supercat - nikt nie mówi, że łańcuch powinien / może być typem wartości.
Henk Holterman

1
Nikt oprócz mnie. Posiadanie łańcucha będącego „specjalnym” typem wartości (z prywatnym polem typu referencyjnego) pozwoliłoby większości obsługi być w zasadzie tak samo wydajnym, jak jest teraz, z wyjątkiem dodanego sprawdzenia zerowego metod / właściwości takich jak .Lengthitp., Tak aby instancje, które przechowują odwołanie zerowe nie będzie próbowało wyłuskać go, ale zachowa się odpowiednio do pustego ciągu. Czy ramy byłoby lepiej lub gorzej ze stringrealizowane w ten sposób, jeśli ktoś chciał default(string)być pusty ciąg ...
SuperCat

1
... stringbycie opakowaniem typu wartości w polu typu odniesienia byłoby podejściem, które wymagałoby najmniejszych zmian w innych częściach .net [w rzeczywistości, gdyby ktoś był skłonny zaakceptować konwersję z, Stringaby Objectutworzyć dodatkowy element w pudełku, można po prostu Stringbyć zwykłą strukturą z polem typu, Char[]którego nigdy nie ujawnił]. Wydaje mi się, że posiadanie HeapStringtypu byłoby prawdopodobnie lepsze, ale pod pewnymi względami ciąg wartości typu trzymający a Char[]byłby prostszy.
supercat

5

Dlaczego projektanci C # wybrali null jako domyślną wartość ciągów?

Ponieważ ciągi znaków są typami referencyjnymi, typy referencyjne mają wartość domyślną to null. Zmienne typów referencji przechowują odniesienia do rzeczywistych danych.

Użyjmy defaultsłowa kluczowego w tym przypadku;

string str = default(string); 

strjest a string, więc jest to typ odniesienia , więc wartością domyślną jest null.

int str = (default)(int);

strjest int, więc jest to typ wartości , więc wartością domyślną jest zero.


4

Gdyby domyślną wartością stringbył pusty ciąg, nie musiałbym testować

Źle! Zmiana wartości domyślnej nie zmienia faktu, że jest to typ referencji i ktoś nadal może jawnie ustawić referencję na null.

Dodatkowo Nullable<String>miałoby to sens.

Prawda Bardziej sensowne byłoby, aby nie zezwalać nullna żadne typy referencji, zamiast tego wymagać Nullable<TheRefType>tej funkcji.

Dlaczego więc projektanci C # zdecydowali się użyć nulljako domyślnej wartości ciągów?

Spójność z innymi typami referencyjnymi. Teraz, nullpo co w ogóle pozwalać na typy referencyjne? Prawdopodobnie dlatego, że wydaje się C, nawet jeśli jest to wątpliwa decyzja projektowa w języku, który również zapewnia Nullable.


3
Może być tak, ponieważ Nullable został wprowadzony tylko w .NET 2.0 Framework, więc wcześniej nie był dostępny?
jcolebrand

3
Dziękuję Danowi Burtonowi za zwrócenie uwagi, że ktoś może później ustawić wartość inicjowaną na null dla typów referencyjnych. Przemyślenie tego mówi mi, że moja pierwotna intencja w pytaniu nie ma sensu.
Marcel

4

Być może, jeśli użyjesz ??operatora podczas przypisywania zmiennej łańcuchowej, może ci to pomóc.

string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.

2

String jest niezmiennym obiektem, co oznacza, że ​​po podaniu wartości stara wartość nie jest usuwana z pamięci, ale pozostaje w starej lokalizacji, a nowa wartość jest umieszczana w nowej lokalizacji. Więc jeśli wartość domyślna String abyło String.Empty, byłoby to zmarnowaćString.Empty blok w pamięci, gdy otrzymałby swoją pierwszą wartość.

Chociaż wydaje się to niewielkie, może stać się problemem podczas inicjowania dużej tablicy ciągów z domyślnymi wartościami String.Empty. Oczywiście zawsze możesz użyć zmiennej StringBuilderklasy, jeśli będzie to stanowić problem.


Dziękujemy za wzmiankę o „pierwszej inicjalizacji”.
Marcel

3
Jaki byłby problem przy inicjowaniu dużej tablicy? Ponieważ, jak powiedziałeś, ciągi są niezmienne, wszystkie elementy tablicy będą po prostu wskaźnikami do tego samego String.Empty. Czy się mylę?
Dan Burton,

2
Domyślną wartością dla każdego typu będzie zerowanie wszystkich bitów. Jedynym sposobem, aby wartość domyślna stringbyła pustym łańcuchem, jest zezwolenie na zero bitów jako reprezentację pustego łańcucha. Można to osiągnąć na wiele sposobów, ale nie sądzę, aby jakikolwiek wymagał inicjalizacji odniesień String.Empty.
supercat

Inne odpowiedzi również omawiały ten punkt. Myślę, że ludzie doszli do wniosku, że nie ma sensu traktować klasy String jako specjalnego przypadku i zapewniać coś innego niż zero bitów jako inicjalizację, nawet jeśli byłoby to coś w rodzaju String.Emptylub "".
djv

@DanV: Zmiana zachowania inicjalizacji stringlokalizacji pamięci wymagałaby również zmiany zachowania inicjalizacji wszystkich struktur lub klas, które mają pola typu string. Oznaczałoby to dość dużą zmianę w projekcie .net, który obecnie oczekuje, że zainicjuje zero dowolnego typu, nawet nie myśląc o tym, co to jest, z wyjątkiem jego całkowitego rozmiaru.
supercat

2

Ponieważ ciąg znaków jest typem odniesienia, a domyślna wartość dla typu odwołania jest pusta.


0

Być może stringsłowo kluczowe wprawiło Cię w zakłopotanie, ponieważ wygląda dokładnie tak samo, jak każda inna deklaracja typu wartości , ale tak naprawdę jest to alias do System.Stringwyjaśnienia w tym pytaniu .
Również ciemnoniebieski kolor w programie Visual Studio i mała pierwsza litera mogą wprowadzać w błąd, myśląc, że to struct.


3
Czy to samo nie dotyczy objectsłowa kluczowego? Chociaż wprawdzie jest to znacznie mniej używane niż string.

2
Jak intalias dla System.Int32. O co ci chodzi? :)
Thorarin

@Thorari @delnan: Są obie aliasy, ale System.Int32to Structma zatem wartość domyślną, podczas gdy System.Stringjest Classo wskaźnik o wartości domyślnej null. Są wizualnie przedstawione w tej samej czcionce / kolorze. Bez wiedzy można myśleć, że działają w ten sam sposób (= mając wartość domyślną). Moja odpowiedź została napisana w
Alessandro Da Rugna

Jestem dość pewien, że Anders Hejlsberg powiedział to w wywiadzie dla kanału 9. Znam różnicę między stertą a stosem, ale pomysł z C # polega na tym, że zwykły programista nie musi.
Thomas Koelle,

0

Typy dopuszczające wartości zerowe pojawiły się dopiero w wersji 2.0.

Gdyby na początku języka utworzono typy zerowalne, to łańcuch nie byłby zerowy i ciąg? byłby zerowy. Ale nie mogli tego zrobić, aby zachować zgodność wsteczną.

Wiele osób mówi o typie ref lub ref, ale łańcuch znaków jest wyjątkowy i można by znaleźć rozwiązania, które by to umożliwiały.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.