Dize türünün varsayılan değeri neden boş bir dize yerine null?


218

Bunun için bütün dizeleri test etmek oldukça can sıkıcı nullgüvenle gibi yöntemleri uygulamadan önce ToUpper(), StartWith()vs ...

Varsayılan değeri stringboş dize olsaydı, sınamak zorunda kalmazdım ve örneğin intveya gibi diğer değer türleriyle daha tutarlı olmasını hissederdim double. Ayrıca Nullable<String>mantıklı olurdu.

Peki neden C # tasarımcıları nulldizelerin varsayılan değeri olarak kullanmayı seçtiler?

Not: Bu, bu soru ile ilgilidir , ancak bununla ne yapmak yerine neden daha çok odaklanmaktadır.


53
Bunun diğer referans türleri için bir sorun olduğunu düşünüyor musunuz ?
Jon Skeet

17
@JonSkeet Hayır, ama sadece başlangıçta, yanlış olarak, dizelerin değer türleri olduğunu düşündüğüm için.
Marcel

21
@Marcel: Bu merak etmek için oldukça iyi bir neden.
TJ Crowder

7
@JonSkeet Evet. Oh evet. (Ama null olmayan referans türü tartışmasına yabancı değilsin…)
Konrad Rudolph

7
Eğer onları değil bekliyoruz yerlerde dizelerde iddiaları kullandıysanız çok daha iyi bir zaman olurdu inanıyorum null(ve ben de kavramsal nullolarak dizeleri farklı şeyler olarak tedavi ve boş öneririz ). Boş değer, bir yerde bir hatanın sonucu olabilirken, boş bir dize farklı bir anlam ifade etmelidir.
diegoreymendez

Yanıtlar:


312

Dize türünün varsayılan değeri neden boş bir dize yerine null?

Çünkü stringbir referans türüdür ve tüm referans tipleri için varsayılan değerdir null.

ToUpper (), StartWith () vb.Gibi yöntemleri güvenle uygulayabilmek için tüm dizelerimi null için test etmek oldukça can sıkıcı bir durum.

Bu, referans türlerinin davranışıyla tutarlıdır. Örnek üyelerini çağırmadan önce, boş bir başvuru için onay alınmalıdır.

Dize'nin varsayılan değeri boş dize olsaydı, sınamak zorunda kalmazdım ve örneğin int veya double gibi diğer değer türleriyle daha tutarlı olmasını hissederdim.

Varsayılan değeri başka bir referans türüne atamak, değeri tutarsıznull hale getirir .

Ayrıca Nullable<String>mantıklı olurdu.

Nullable<T>değer türleriyle çalışır. Dikkat çekici bir gerçektir Nullableorijinal üzerinde tanıtılan değildi .NET platformunun kırık kod çok onlar o kuralı değişmişti olurdu böylece. ( İzniyle @jcolebrand )


10
@HenkHolterman Kişi bir ton şey uygulayabilir, ama neden bu kadar göze çarpan bir tutarsızlık getirelim?

4
@delnan - "neden" buradaki soru.
Henk Holterman

8
@HenkHolterman Ve "Tutarlılık" noktaya çürütme olduğunu "dize diğer başvuru türlerinin aksine tedavi edilebilir".

6
@delnan: Dizgiyi değer türü olarak gören bir dil üzerinde çalışıyor ve dotnet üzerinde 2 yıldan fazla çalışıyorum, Henk'e katılıyorum. Dotnet üzerinde büyük bir FLAW olarak görüyorum .
Fabricio Araujo

1
@delnan: String(1) kullanılabilir bir varsayılan değere sahip olan değer-tip-ish davranışı ve (2) her yayınlandığında talihsiz ekstra bir boks indirimi katmanı dışında esasen gibi davranan bir değer türü yaratılabilir . Object. Öbek temsilinin stringbenzersiz olduğu göz önüne alındığında, ekstra bokstan kaçınmak için özel bir tedaviye sahip olmak çok fazla bir şey olmazdı (aslında, varsayılan olmayan boks davranışlarını belirleyebilmek diğer türler için de iyi bir şey olurdu).
Supercat

40

Habib haklı - çünkü stringbir referans türü.

Ancak daha da önemlisi, her kullandığınızda kontrol etmeniz gerekmeznull . ArgumentNullExceptionBirisi işlevinizi bir nullreferans geçerse, muhtemelen bir atmalısınız .

İşte bir şey - bir dize NullReferenceExceptionçağırmaya çalışırsanız çerçeve yine de sizin için bir atar .ToUpper(). nullParametrelerin değerlendirebileceği gibi işlevinize iletilen nesneler üzerindeki herhangi bir özellik veya yöntem için argümanlarınızı test etseniz bile bu durumun hala olabileceğini unutmayın null.

Varlık dedi ki, boş dizeleri veya boş değerlere kontrol yapmak için bir ortak şey, sağladıkları bu yüzden String.IsNullOrEmpty()ve String.IsNullOrWhiteSpace()sadece bu amaç için.


30
Asla NullReferenceExceptionkendiniz atmamalısınız ( msdn.microsoft.com/en-us/library/ms173163.aspx ); ArgumentNullExceptionyönteminiz null referansları kabul edemezse bir atarsınız . Ayrıca, sorunları giderirken NullRef'ler genellikle tanı için daha zor istisnalardan biridir, bu yüzden null olup olmadığını kontrol etmenin önerisinin çok iyi olduğunu düşünmüyorum.
Andy

3
@Andy "NullRef's genellikle teşhis için en zor istisnalardan biridir" Kesinlikle katılmıyorum, eğer bir şey kaydederseniz bulmak ve düzeltmek gerçekten kolaydır (sadece boş durumda işlemek).
Louis Kottmann

6
Fırlatma ArgumentNullExceptionişleminin, parametre adını sağlayabilmesinin ek bir yararı vardır. Hata ayıklama sırasında, bu ... hata, saniye kaydeder. Ama önemli saniye.
Kos

2
@DaveMarkle, IsNullOrWhitespace'i de dahil etmek isteyebilirsiniz msdn.microsoft.com/en-us/library/…
Nathan Koop

1
Gerçekten her yerde null için kontrol muazzam kod şişkinlik kaynağı olduğunu düşünüyorum. bu çirkin ve küstah görünüyor ve tutarlı kalmak zor. (En azından C # benzeri dillerde) iyi bir kural "üretim kodu null anahtar kelime yasağı, test kodu deli gibi kullanın" olduğunu düşünüyorum.
sara

24

Bir uzantı yöntemi yazabilirsiniz (değeri için):

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

Şimdi bu güvenli bir şekilde çalışıyor:

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

100
Ama lütfen yapma. Başka bir programcının görmek istediği son şey, sadece ilk adam istisnalardan "korktuğu" için .EmptyNull () ile karalanmış binlerce kod satırıdır.
Dave Markle

15
@DaveMarkle: Ama açıkça OP'nin aradığı şey tam olarak bu. "ToUpper (), StartWith () vb.Gibi yöntemleri güvenle uygulayabilmem için tüm dizelerimi null için test etmek oldukça can sıkıcı"
Tim Schmelter

19
Yorum sizin için değil OP içindi. Cevabınız açıkça doğru olsa da, bunun gibi temel bir soru soran bir programcı, çözümünüzü genellikle WIDE uygulamasına koymaya karşı, genellikle alışkanlık olarak olduğu gibi şiddetle uyarılmalıdır. Opaklık, artan karmaşıklık, yeniden düzenleme zorluğu, uzatma yöntemlerinin potansiyel aşırı kullanımı ve evet, performans gibi, yanıtınızda tartışmamanız gereken bazı ödünler vardır. Bazen (birçok kez) doğru cevap doğru yol değildir ve bu yüzden yorum yaptım.
Dave Markle

5
@Andy: Uygun null denetiminin yapılmamasının çözümü, null'ları düzgün bir şekilde kontrol etmek, bir soruna yara bandı koymak değil.
Dave Markle

7
Yazma sorununu yaşıyorsanız .EmptyNull(), neden (str ?? "")gerekli olduğu yerde kullanmıyorsunuz ? Bununla birlikte, @ DaveMarkle'ın yorumunda ifade edilen düşünceye katılıyorum: muhtemelen yapmamalısınız. nullve String.Emptykavramsal olarak farklıdırlar ve bir başkasına aynı şekilde davranamazsınız.
CVn

17

C # 6.0 itibarıyla aşağıdakileri de kullanabilirsiniz

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

Dize sonucu boş olacaktır.


1
Doğru olmak gerekirse, c # 6.0'dan beri, IDE'nin sürümünün bununla bir ilgisi yoktur, çünkü bu bir dil özelliğidir.
Stijn Van Antwerpen

3
Başka bir seçenek -public string Name { get; set; } = string.Empty;
Jaja Harris

Buna ne denir? ? myString .ToUpper ();
Hunter Nelson

1
Buna Null-Koşullu Operatör denir. Bu konuda buradan okuyabilirsiniz msdn.microsoft.com/en-us/magazine/dn802602.aspx
russelrillema

14

Boş dizeler ve boş değerler temelde farklıdır. Boş, bir değerin olmaması ve boş bir dize, boş olan bir değerdir.

Bu durumda boş bir dize olan bir değişkenin "değeri" hakkında varsayımlar yapan programlama dili, dizeyi boş bir başvuru sorununa neden olmayacak başka bir değerle başlatmak kadar iyi olacaktır.

Ayrıca, tanıtıcıyı bu dize değişkenine uygulamanın diğer bölümlerine iletirseniz, bu kodun bilerek boş bir değer geçirip geçirmediğinizi veya bu değişkenin değerini doldurmayı unuttuğunuzu doğrulamanın hiçbir yolu olmaz.

Bunun bir sorun olacağı başka bir durum, dizenin bazı işlevlerden bir dönüş değeri olmasıdır. Dize bir başvuru türü olduğundan ve teknik olarak her ikisi de boş ve boş olarak bir değere sahip olabileceğinden, işlev aynı zamanda teknik olarak bir boş veya boş döndürebilir (bunu durdurmak için hiçbir şey yoktur). Şimdi, "bir değerin yokluğu", yani boş bir dize ve bir boş olmak üzere 2 kavram olduğundan, bu işlevi tüketen tüm kodun 2 kontrol yapması gerekecektir. Biri boş, diğeri boş.

Kısacası, tek bir devlet için sadece 1 temsile sahip olmak her zaman iyidir. Boş ve boş değerlerle ilgili daha geniş bir tartışma için aşağıdaki bağlantılara bakın.

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

Kullanıcı girişi ile uğraşırken NULL vs Empty


2
Ve bir metin kutusunda bu farkı tam olarak nasıl görüyorsunuz? Kullanıcı alana bir değer girmeyi mi unuttu yoksa bilerek boş bırakıyor mu? Bir programlama dilinde null'un belirli bir anlamı vardır; atanmamış. Veritabanıyla aynı olmayan bir değere sahip olmadığını biliyoruz.
Andy

1
bir metin kutusuyla kullandığınızda çok fazla fark yok. Her iki şekilde de, bir dizede bir değerin yokluğunu temsil eden bir gösterime sahip olmak çok önemlidir. Birini seçmek zorunda olsaydım, boş seçerdim.
Nerrve

Delphi'de dize bir değer türüdür ve bu nedenle boş olamaz. Hayatı bu açıdan çok daha kolay hale getiriyor - gerçekten çok can sıkıcı bir dize referans tipi buluyorum.
Fabricio Araujo

1
.Net'i önceden doldurmuş olan COM (Ortak Nesne Modeli) altında, bir dize türü ya dizenin verilerine bir işaretçi tutar ya nullda boş dizeyi temsil eder. .Net'in benzer semantikleri uygulayabilmeleri için bunu seçmiş olsalardı, özellikle Stringde onu benzersiz bir tür haline getiren bir takım özelliklere sahip olduğu göz önüne alındığında , [örneğin ve iki dizi türü, tahsisi olan tek türdür. boyutu sabit değil].
Supercat

7

Temel neden / sorun, CLS spesifikasyonunun tasarımcılarının (dillerin .net ile nasıl etkileşime girdiğini tanımlayan) sınıf üyelerinin callvirt, arayan bir null-referans kontrolü; ne de "normal" bokslamaya tabi olmayacak yapıların tanımlanması için bir ortalama sağlamadı.

CLS spesifikasyonu böyle bir araç tanımlamış olsaydı, o zaman .net'in Ortak Nesne Modeli (COM) tarafından kurulan ve altında boş bir dize başvurusunun semantik olarak boş bir dizeye eşdeğer olduğu kabul edilen lead'i tutarlı bir şekilde izlemesi mümkün olurdu. varsayılan değerleri tanımlamak için değer semantiğine sahip olması gereken kullanıcı tanımlı değişmez sınıf türleri. Esasen, her bir üye için ne olacağını String, örneğin bir Lengthşey gibi yazılacaktı [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }. Bu yaklaşım, değer gibi davranması gereken şeyler için çok güzel bir anlambilim sunacaktır, ancak uygulama sorunlarının yığın üzerinde depolanması gerekir. Bu yaklaşımla ilgili en büyük zorluk, bu türler arasındaki dönüşüm semantiğinin ve Objectbiraz bulanık olabilmesidir.

Alternatif bir yaklaşım, miras almayan, Objectbunun yerine özel boks ve kutudan çıkarma operasyonlarına sahip olan (diğer bazı sınıf tiplerine dönüşebilen) özel yapı tiplerinin tanımlanmasına izin vermek olurdu. Böyle bir yaklaşıma göre, bir sınıf tipi olacağını NullableStringdize şimdi yaptığı gibi davranır ve özel kutulu yapı tipi Stringtek özel alan yapacağını, ValueÇeşidi String. Bir dönüştürmek için çalışılıyor Stringetmek NullableStringveya Objectdönecekti Valueolmayan boş ise, ya String.Emptyboş eğer. Yayınlamaya çalışıldığında String, bir NullableStringörneğe null olmayan bir başvuru başvuruyu depolar Value(belki de uzunluk sıfırsa null değerini saklar); başka bir atıfta bulunmak istisna oluşturur.

Dizelerin yığın üzerinde depolanması gerekmesine rağmen, kavramsal olarak null olmayan varsayılan değere sahip değer türleri gibi davranmamaları için hiçbir neden yoktur . Bir referansı tutan "normal" bir yapı olarak saklanmaları, bunları "dize" türü olarak kullanan kod için etkili olurdu, ancak "nesneye" döküm yaparken fazladan bir dolaylama ve verimsizlik katmanı ekleyecektir. .Net bu geç tarihte yukarıdaki özelliklerden birini eklemeyi öngörmese de, belki de gelecekteki çerçevelerin tasarımcıları bunları dahil etmeyi düşünebilir.


1
SQL'de çok çalışan ve Oracle'ın baş ağrısı ile NULL ve sıfır uzunluk arasında bir ayrım yapmamaya başlamış biri olarak konuşurken, .NET'in yaptığı için çok memnunum . "Boş" bir değerdir, "boş" değildir.

@JonofAllTrades: Kabul etmiyorum. Uygulama kodunda, db koduyla uğraşmak dışında, bir dizenin sınıf olarak değerlendirilmesinin bir anlamı yoktur. Bu bir değer türü ve temel değerdir. Supercat: Sana +1
Fabricio Araujo

1
Veritabanı kodu büyük bir "hariç". Veritabanları gibi "mevcut / bilinen, boş bir dize" ile "mevcut / bilinmeyen / uygulanamaz" arasında ayrım yapmanız gereken bazı sorun alanları olduğu sürece , dilin bunu desteklemesi gerekir. Tabii ki şimdi .NET sahip Nullable<>, dizeleri değer türleri olarak yeniden uygulanabilir; Böyle bir seçimin maliyet ve faydalarından söz edemem.

3
@JonofAllTrades: Sayılarla ilgilenen kod, varsayılan sıfır değerini "undefined" değerinden ayırt etmek için bant dışı bir araca sahip olmalıdır. Olduğu gibi, dize ve sayılarla çalışan null edilebilir işleme kodu null olabilecek dize için bir yöntem ve null edilebilir sayılar için başka bir yöntem kullanmalıdır. Sıfırlanabilir sınıf türü stringolması gerekenden daha verimli Nullable<string>olsa da, "daha verimli" yöntemini kullanmak zorunda kalmak, aynı yaklaşımın tüm boş değer veri veritabanı değerleri için kullanılabilmesinden daha külfetlidir.
Supercat

5

Çünkü bir dize değişkeni bir örnek değil , bir referanstır .

Varsayılan olarak Boş olarak başlatılması mümkün olurdu, ancak tüm tahtada çok fazla tutarsızlık ortaya çıkarırdı.


3
stringReferans türü olmak için özel bir neden yoktur . Emin olmak için, dizeyi oluşturan gerçek karakterlerin yığın üzerinde depolanması gerekir, ancak dizelerin zaten CLR'de sahip olduğu özel destek miktarı göz önüne alındığında, System.Stringbir değer türüne sahip bir tür olması bir streç olmayacaktır. Valuetürün tek özel alanı HeapString. Bu alan bir başvuru türü olacak ve varsayılan olarak kullanılacaktı null, ancak alanı null Stringolan bir yapı Valueboş bir dize gibi davranacaktı. Bu yaklaşımın tek dezavantajı ...
supercat

1
... bir döküm olduğunu Stringiçin Objectçalışma zamanı özel durum kodu yokluğunda, olur, bir kutulu oluşturulmasını neden Stringsadece bir başvuru kopyalama yerine, öbek üzerinde örneğin HeapString.
Supercat

1
@supercat - kimse dizenin bir değer türü olabileceğini / olabileceğini söylemez.
Henk Holterman

1
Benden başka kimse yok. Dizenin "özel" bir değer türü olması (özel bir başvuru türü alanı ile), çoğu yöntemin, yöntem gibi özellikler / özellikler üzerinde ek bir sıfır denetimi dışında, şu anda olduğu kadar verimli olmasını sağlar .Length. bir boş başvuru, referansı kaldırmayı denemez, bunun yerine boş bir dize için uygun davranır. Çerçevenin stringbu şekilde uygulanmasıyla daha iyi veya daha kötü olup olmayacağı, eğer default(string)boş bir dize olmak istediyse ...
Supercat

1
... sahip string.net [diğer bölgelerine en az sayıda değişikliği gerekli yaklaşım olur bir referans tipi alanına bir değer tipi sarmalayıcı olmak gerçekten de bir dönüşüm var kabul etmeye istekli olsaydı Stringiçin Objectekstra bir kutulu öğe oluşturmak, kişi hiçbir zaman maruz kalmadığı bir Stringtür alana sahip sıradan bir yapı olabilirdi Char[]]. Ben bir HeapStringtür olması muhtemelen daha iyi olacağını düşünüyorum , ama bazı açılardan a tutarak değer türü dize Char[]daha basit olurdu.
Supercat

5

Neden C # tasarımcıları varsayılan dize değeri olarak null kullanmayı seçtiler?

Dizeler başvuru türleri olduğundan , başvuru türleri varsayılan değerdir null. Referans türlerinin değişkenleri, gerçek verilere referansları saklar.

defaultBu durum için anahtar kelime kullanalım ;

string str = default(string); 

stra string, bu nedenle bir referans türüdür , bu nedenle varsayılan değer 'dir null.

int str = (default)(int);

strbir olan intbir yüzden, değer türü varsayılan değerdir yüzden zero.


4

Varsayılan değeri stringboş dize olsaydı, test etmek zorunda kalmazdım

Yanlış! Varsayılan değerin değiştirilmesi, bunun bir referans türü olduğu gerçeğini değiştirmez ve birisi yine de başvuruyu açık olarak ayarlayabilirnull .

Ayrıca Nullable<String>mantıklı olurdu.

Gerçek nokta. nullHerhangi bir referans türüne izin vermemek , Nullable<TheRefType>bu özelliğe ihtiyaç duymak daha mantıklı olacaktır .

Peki neden C # tasarımcıları nulldizelerin varsayılan değeri olarak kullanmayı seçtiler?

Diğer referans türleriyle tutarlılık. Şimdi, neden nullreferans türlerine izin veriyorsunuz ? Muhtemelen C gibi hissettirir, ancak bu da bir dilde tartışmalı bir tasarım kararı olsa da Nullable.


3
Nullable yalnızca .NET 2.0 Framework'te tanıtıldığından, bu nedenle daha önce kullanılamıyor olabilir mi?
jcolebrand

3
Dan Burton, birisinin daha sonra referans türlerinde başlatılan değeri null olarak ayarlayabildiğine işaret ettiği için teşekkürler. Bunu düşünmek bana sorudaki asıl amacımın işe yaramayacağını söylüyor.
Marcel

4

Belki de ??string değişkeninizi atarken operatörü kullanırsanız, bu size yardımcı olabilir.

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

2

Dize değişmez bir nesnedir, yani bir değer verildiğinde, eski değer bellekten silinmez, ancak eski konumda kalır ve yeni değer yeni bir konuma yerleştirilir. Varsayılan değeri Yani eğer String aoldu String.Empty, ziyan olur String.Emptyonun ilk değeri verildiğinde bellekte bloğu.

Küçük gibi görünse de, varsayılan değerleri olan büyük bir dizi diziyi başlatırken soruna dönüşebilir String.Empty. Tabii ki, StringBuildereğer bu bir problem olacaksa , değişebilir sınıfı her zaman kullanabilirsiniz .


"İlk başlatma" öğesinden bahsettiğiniz için teşekkür ederiz.
Marcel

3
Büyük bir diziyi başlatırken sorun nasıl olur? Söylediğiniz gibi, Dizeler değişmez olduğundan, dizinin tüm öğeleri basitçe aynı işaretçiler olacaktır String.Empty. Yanlış mıyım?
Dan Burton

2
Herhangi bir tür için varsayılan değer tüm bitleri sıfıra ayarlayacaktır. Varsayılan değerinin stringboş bir dize olmasının tek yolu, all-bit-zero değerinin boş bir dize olarak gösterilmesine izin vermektir. Bunun başarılması için birkaç yol vardır, ancak referansların başlatılmasını içermediğini düşünmüyorum String.Empty.
Supercat

Diğer cevaplar da bu noktayı tartıştı. İnsanların buna benzer bir şey olsa bile, özel bir durum olarak String sınıfı tedavi etmek ve tüm bitlik sıfır bir başlangıç olarak görüyoruz dışında bir şey sunmak için mantıklı olmaz sonucuna vardık düşünüyorum String.Emptyya "".
djv

@DanV: stringDepolama konumlarının başlatma davranışını değiştirmek, tür alanları olan tüm yapıların veya sınıfların başlatma davranışını da değiştirmeyi gerektirecektir string. Bu, şu anda ne olduğunu düşünmek zorunda kalmadan herhangi bir türü sıfır başlatmayı bekleyen, sadece toplam boyutu için tasarruf eden .net tasarımında oldukça büyük bir değişikliği temsil edecektir.
Supercat

2

Dize bir başvuru türü olduğundan ve başvuru türü için varsayılan değer boştur.


0

Belki stringde diğerleri gibi tam göründüğü anahtar kelime, karıştırdım değer türü bildiriminde, ama aslında bir takma olduğu System.Stringaçıklandığı gibi bu soruya .
Ayrıca Visual Studio'daki koyu mavi renk ve küçük harfli ilk harfin yanlış olduğunu düşünebilir struct.


3
objectAnahtar kelime için aynı şey geçerli değil mi? Kuşkusuz, bu çok daha az kullanılır string.

2
Gibi intbir takma addır System.Int32. Ne demek istiyorsun? :)
Thorarin

Her ikisi de takma adları, ama: @Thorari @delnan System.Int32bir olduğu Structsüre böylece varsayılan bir değeri olan System.Stringbir olduğu Classvarsayılan değerine sahip bir işaretçi sahip null. Görsel olarak aynı yazı tipi / renkte sunulurlar. Bilgi olmadan, aynı şekilde davrandıklarını düşünebilir (= varsayılan değere sahip). Cevabım arkasında bir en.wikipedia.org/wiki/Cognitive_psychology bilişsel psikoloji fikri ile yazılmış :-)
Alessandro Da Rugna

Kanal 9 röportajında ​​bunu söyleyen Anders Hejlsberg'den oldukça eminim. Yığın ve yığın arasındaki farkı biliyorum ama C # ile fikir rahat programcı gerekmez.
Thomas Koelle

0

Null olabilecek türler 2.0'a kadar gelmedi.

Null olabilecek türler dilin başında yapılmış olsaydı, dize null edilemez ve dize olur mu? geçersiz olacaktır. Ancak geriye dönük uyumluluk için bunu yapamadılar.

Birçok insan ref-tipi veya ref tipi hakkında konuşmaz, ancak dize sıradan sınıfın dışındadır ve bunu mümkün kılmak için çözümler bulunmuş olurdu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.