ทำไมค่าเริ่มต้นของประเภทสตริงเป็นโมฆะแทนที่จะเป็นสตริงว่าง


218

มันน่ารำคาญมากทีเดียวที่จะทดสอบสตริงของฉันทั้งหมดnullก่อนที่ผมจะสามารถใช้วิธีการเช่นToUpper(), StartWith()ฯลฯ ...

หากค่าเริ่มต้นของstringสตริงว่างเปล่าฉันจะไม่ต้องทดสอบและฉันรู้สึกว่ามันจะสอดคล้องกับประเภทค่าอื่น ๆ เช่นintหรือdoubleตัวอย่าง นอกจากนี้Nullable<String>จะทำให้รู้สึก

เหตุใดนักออกแบบของ C # จึงเลือกใช้nullเป็นค่าเริ่มต้นของสตริง

หมายเหตุ: สิ่งนี้เกี่ยวข้องกับคำถามนี้แต่ให้ความสำคัญกับสาเหตุมากกว่าจะทำอย่างไรกับมัน


53
คุณพิจารณาถึงปัญหานี้สำหรับการอ้างอิงประเภทอื่นหรือไม่
Jon Skeet

17
@JonSkeet ไม่ แต่เป็นเพราะฉันเริ่มผิดคิดว่าสตริงเป็นประเภทค่า
Marcel

21
@ Marcel: นั่นเป็นเหตุผลที่ดีสำหรับการสงสัยเกี่ยวกับมัน
TJ Crowder

7
@JonSkeet ใช่ โอ้ใช่. (แต่คุณไม่ใช่คนแปลกหน้าในการอภิปรายประเภทอ้างอิงที่ไม่เป็นโมฆะ ... )
Konrad Rudolph

7
ผมเชื่อว่าคุณจะมีช่วงเวลาที่ดีมากถ้าคุณใช้ยืนยันในสายของคุณในสถานที่ที่คุณคาดหวังว่าพวกเขาจะไม่ถูกnull(และผมขอแนะนำให้คุณแนวคิดการรักษาnullสตริงและว่างเปล่าเป็นสิ่งที่แตกต่างกัน) ค่า Null อาจเป็นผลลัพธ์ของข้อผิดพลาดที่ใดที่หนึ่งในขณะที่สตริงว่างควรถ่ายทอดความหมายที่แตกต่างกัน
diegoreymendez

คำตอบ:


312

ทำไมค่าเริ่มต้นของประเภทสตริงเป็นโมฆะแทนที่จะเป็นสตริงว่าง

เพราะstringเป็นชนิดการอ้างอิงnullและค่าเริ่มต้นสำหรับทุกประเภทอ้างอิง

มันค่อนข้างน่ารำคาญที่จะทดสอบสตริงทั้งหมดของฉันเป็นโมฆะก่อนที่ฉันจะสามารถใช้วิธีการอย่าง ToUpper (), StartWith () และอื่น ๆ ...

ที่สอดคล้องกับพฤติกรรมของประเภทอ้างอิง ก่อนที่จะเรียกสมาชิกอินสแตนซ์ของพวกเขาหนึ่งควรใส่เช็คอินสำหรับการอ้างอิงเป็นโมฆะ

หากค่าเริ่มต้นของสตริงเป็นสตริงว่างเปล่าฉันจะไม่ต้องทดสอบและฉันจะรู้สึกว่ามันสอดคล้องกับประเภทค่าอื่น ๆ เช่น int หรือ double เป็นต้น

การกำหนดค่าเริ่มต้นการเฉพาะชนิดการอ้างอิงอื่น ๆ กว่าnullจะทำให้มันไม่สอดคล้องกัน

นอกจากนี้Nullable<String>จะทำให้รู้สึก

Nullable<T>ทำงานกับประเภทค่า สิ่งที่ควรทราบคือความจริงที่Nullableไม่ได้ถูกนำมาใช้ในแพลตฟอร์ม NETเดิมดังนั้นจะมีรหัสที่ใช้งานไม่ได้หากพวกเขาเปลี่ยนกฎนั้น ( Courtesy @jcolebrand )


10
@ HenkHolterman One สามารถใช้สิ่งต่าง ๆ ได้ทั้งหมด แต่ทำไมถึงมีความขัดแย้งที่เห็นได้ชัด?

4
@delnan - "ทำไม" เป็นคำถามที่นี่
Henk Holterman

8
@ HenkHolterman และ "ความสอดคล้อง" คือการโต้แย้งถึงจุดของคุณ "สตริงสามารถได้รับการปฏิบัติแตกต่างจากประเภทอ้างอิงอื่น ๆ "

6
@delnan: การทำงานกับภาษาที่ใช้สตริงเป็นประเภทของค่าและการทำงานใน dotnet 2 ปีขึ้นไปฉันเห็นด้วยกับ Henk ฉันเห็นว่าเป็นข้อบกพร่องที่สำคัญใน dotnet
Fabricio Araujo

1
@delnan: หนึ่งสามารถสร้างประเภทของค่าที่มีพฤติกรรมเป็นหลักStringยกเว้น (1) พฤติกรรมประเภทค่า ish ของการมีค่าเริ่มต้นที่ใช้งานได้และ (2) เลเยอร์พิเศษทางอ้อมที่โชคร้ายของเวลาที่มันถูกโยนไปObject. เนื่องจากการเป็นตัวแทนของกองstringนั้นมีความพิเศษมีการดูแลเป็นพิเศษเพื่อหลีกเลี่ยงการชกมวยพิเศษจะไม่ยืดมากนัก (จริง ๆ แล้วการระบุพฤติกรรมการชกมวยที่ไม่ใช่ค่าเริ่มต้นจะเป็นสิ่งที่ดีสำหรับประเภทอื่นเช่นกัน)
supercat

40

ฮาบิบพูดถูก - เพราะstringเป็นประเภทอ้างอิง

แต่ที่สำคัญคุณไม่ต้องตรวจสอบnullทุกครั้งที่ใช้งาน คุณอาจจะโยนArgumentNullExceptionถ้ามีใครบางคนผ่านฟังก์ชั่นnullอ้างอิงของคุณ

นี่คือสิ่งที่ - กรอบจะโยนNullReferenceExceptionให้คุณต่อไปถ้าคุณพยายามโทรหา.ToUpper()สาย โปรดจำไว้ว่ากรณีนี้ยังสามารถเกิดขึ้นได้ถ้าคุณทดสอบข้อโต้แย้งของคุณสำหรับnullตั้งแต่ทรัพย์สินใด ๆ nullหรือวิธีการที่วัตถุที่ผ่านมาการทำงานของคุณเป็นพารามิเตอร์อาจประเมิน

การตรวจสอบสตริงหรือโมฆะที่ว่างเปล่าเป็นเรื่องปกติที่ต้องทำดังนั้นพวกเขาจึงจัดหาString.IsNullOrEmpty()และString.IsNullOrWhiteSpace()เพื่อจุดประสงค์นี้เท่านั้น


30
คุณไม่ควรโยนNullReferenceExceptionตัวเอง ( msdn.microsoft.com/en-us/library/ms173163.aspx ); คุณโยนArgumentNullExceptionถ้าวิธีการของคุณไม่สามารถยอมรับการอ้างอิงที่เป็นโมฆะ นอกจากนี้ NullRef เป็นหนึ่งในข้อยกเว้นที่ยากกว่าในการวินิจฉัยเมื่อคุณแก้ไขปัญหาดังนั้นฉันไม่คิดว่าคำแนะนำที่จะไม่ตรวจสอบว่ามีค่าใดเป็นสิ่งที่ดีมาก
Andy

3
@Andy "NullRef เป็นหนึ่งในข้อยกเว้นที่ยากที่สุดในการวินิจฉัย" ฉันไม่เห็นด้วยอย่างยิ่งถ้าคุณบันทึกสิ่งต่าง ๆ มันเป็นเรื่องง่ายที่จะหา & แก้ไข
Louis Kottmann

6
การขว้างปาArgumentNullExceptionมีข้อดีเพิ่มเติมของความสามารถในการระบุชื่อพารามิเตอร์ ในระหว่างการดีบั๊กสิ่งนี้จะช่วย ... ผิดพลาดวินาที แต่วินาทีสำคัญ
Kos

2
@DaveMarkle คุณอาจต้องการรวม IsNullOrWhitespace ด้วยmsdn.microsoft.com/en-us/library/…
นาธานคูป

1
ฉันคิดว่าการตรวจสอบ null ทุกที่เป็นแหล่งที่มาของรหัสขยายใหญ่ มันน่าเกลียดและมันดูแฮ็คและมันยากที่จะอยู่อย่างสม่ำเสมอ ฉันคิดว่า (อย่างน้อยในภาษา C # เหมือน) กฎที่ดีคือ "ห้ามคำหลักที่เป็นโมฆะในรหัสการผลิตใช้มันอย่างบ้าคลั่งในรหัสทดสอบ"
ร่า

24

คุณสามารถเขียนวิธีการขยาย (สำหรับสิ่งที่คุ้มค่า):

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

ตอนนี้ทำงานได้อย่างปลอดภัย:

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

100
แต่โปรดอย่า สิ่งสุดท้ายที่โปรแกรมเมอร์อื่นต้องการเห็นคือโค้ดหลายพันบรรทัดที่มี. EmptyNull () ทุกที่เพราะชายคนแรกนั้น "กลัว" ข้อยกเว้น
Dave Markle

15
@DaveMarkle: แต่เห็นได้ชัดว่ามันคือสิ่งที่ OP ต้องการ "มันค่อนข้างน่ารำคาญที่จะทดสอบสตริงทั้งหมดของฉันเป็นโมฆะก่อนที่ฉันจะสามารถใช้วิธีการอย่าง ToUpper (), StartWith () และอื่น ๆ " ได้อย่างปลอดภัย
Tim Schmelter

19
ความคิดเห็นต่อ OP ไม่ใช่สำหรับคุณ ในขณะที่คำตอบของคุณถูกต้องชัดเจนผู้เขียนโปรแกรมที่ถามคำถามพื้นฐานเช่นนี้ควรระมัดระวังอย่างยิ่งต่อการวางวิธีการแก้ปัญหาของคุณไปสู่การปฏิบัติแบบ WIDE ซึ่งบ่อยครั้งที่พวกเขาไม่เคยชิน มีจำนวนการแลกเปลี่ยนที่คุณไม่ได้กล่าวถึงในคำตอบของคุณเช่นความเปิดกว้างความซับซ้อนที่เพิ่มขึ้นความยากลำบากในการปรับโครงสร้างใหม่วิธีการใช้นามสกุลที่มากเกินไปและใช่ประสิทธิภาพ บางครั้งคำตอบที่ถูกต้องไม่ใช่เส้นทางที่ถูกต้องและนี่คือเหตุผลที่ฉันแสดงความคิดเห็น
Dave Markle

5
@Andy: วิธีการแก้ปัญหาการตรวจสอบ null ไม่ถูกต้องคือการตรวจสอบ nulls อย่างถูกต้องไม่ให้ band-aid กับปัญหา
Dave Markle

7
หากคุณกำลังประสบปัญหาในการเขียน.EmptyNull()ทำไมไม่ใช้(str ?? "")แทนที่จำเป็น ที่กล่าวว่าฉันเห็นด้วยกับความรู้สึกที่แสดงในความคิดเห็นของ @ DaveMarkle: คุณอาจไม่ควร nullและString.Emptyมีแนวคิดแตกต่างกันและคุณไม่สามารถปฏิบัติต่อสิ่งเดียวกันกับที่อื่นได้
CVn

17

คุณสามารถใช้สิ่งต่อไปนี้ตั้งแต่ C # 6.0

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

ผลลัพธ์ของสตริงจะเป็นโมฆะ


1
เพื่อความถูกต้องตั้งแต่ c # 6.0 รุ่นของ IDE ไม่มีส่วนเกี่ยวข้องเนื่องจากเป็นคุณสมบัติภาษา
Stijn Van Antwerpen

3
ตัวเลือกอื่น -public string Name { get; set; } = string.Empty;
Jaja Harris

สิ่งนี้เรียกว่าอะไร? ? myString .ToUpper ();
Hunter Nelson

1
มันเรียกว่าตัวดำเนินการ Null-Conditional คุณสามารถอ่านเกี่ยวกับเรื่องนี้ได้ที่นี่msdn.microsoft.com/en-us/magazine/dn802602.aspx
russelrillema

14

สตริงและโมฆะที่ว่างเปล่านั้นแตกต่างกันโดยพื้นฐาน ค่า Null คือการไม่มีค่าและสตริงว่างคือค่าที่ว่าง

ภาษาการเขียนโปรแกรมการตั้งสมมติฐานเกี่ยวกับ "ค่า" ของตัวแปรในกรณีนี้สตริงว่างจะดีเท่ากับเริ่มต้นสตริงด้วยค่าอื่น ๆ ที่จะไม่ทำให้เกิดปัญหาการอ้างอิงโมฆะ

นอกจากนี้หากคุณส่งหมายเลขอ้างอิงไปยังตัวแปรสตริงนั้นไปยังส่วนอื่น ๆ ของแอปพลิเคชันรหัสนั้นจะไม่มีวิธีการตรวจสอบว่าคุณผ่านค่าว่างโดยเจตนาหรือคุณลืมเติมค่าของตัวแปรนั้น

อีกโอกาสที่ปัญหานี้จะเกิดขึ้นเมื่อสตริงเป็นค่าส่งคืนจากบางฟังก์ชัน เนื่องจากสตริงเป็นประเภทอ้างอิงและในทางเทคนิคสามารถมีค่าเป็น null และว่างทั้งคู่ดังนั้นฟังก์ชันจึงสามารถส่งคืนค่าว่างหรือเทคนิคว่างได้ในทางเทคนิค (ไม่มีอะไรจะหยุดมันได้ ตอนนี้เนื่องจากมี 2 แนวคิดเกี่ยวกับ "การขาดค่า" นั่นคือสตริงว่างและ null รหัสทั้งหมดที่ใช้ฟังก์ชันนี้จะต้องทำการตรวจสอบ 2 ครั้ง หนึ่งรายการว่างเปล่าและอีกรายการหนึ่งเป็นโมฆะ

ในระยะสั้นมันก็ดีเสมอที่จะมีเพียง 1 การแทนสำหรับสถานะเดียว สำหรับการอภิปรายที่กว้างขึ้นเกี่ยวกับที่ว่างเปล่าและไม่มีค่าใช้จ่ายดูลิงค์ด้านล่าง

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

NULL vs Empty เมื่อจัดการกับอินพุตของผู้ใช้


2
แล้วคุณเห็นความแตกต่างตรงนี้พูดในกล่องข้อความอย่างไร ผู้ใช้ลืมที่จะป้อนค่าในฟิลด์หรือพวกเขาตั้งใจปล่อยว่างไว้? Null ในภาษาการเขียนโปรแกรมมีความหมายเฉพาะ ยังไม่ได้กำหนด เรารู้ว่ามันไม่มีค่าซึ่งไม่เหมือนกับฐานข้อมูลว่าง
Andy

1
มีความแตกต่างไม่มากเมื่อคุณใช้กับกล่องข้อความ ไม่ว่าจะด้วยวิธีใดการมีหนึ่งสัญกรณ์แสดงถึงการไม่มีค่าในสตริงเป็นสิ่งสำคัญยิ่ง ถ้าฉันต้องเลือกหนึ่งฉันจะเลือกเป็นโมฆะ
Nerrve

ใน Delphi สตริงเป็นประเภทค่าจึงไม่สามารถเป็นค่าว่างได้ มันทำให้ชีวิตง่ายขึ้นมากในแง่นี้ - ฉันพบว่าทำให้สตริงน่ารำคาญเป็นประเภทอ้างอิง
Fabricio Araujo

1
ภายใต้ COM (โมเดลวัตถุทั่วไป) ซึ่งมี. predated. สตริงประเภทจะถือตัวชี้ไปยังข้อมูลของสตริงหรือnullเพื่อเป็นตัวแทนของสตริงที่ว่างเปล่า มีหลายวิธีที่. net สามารถนำความหมายที่คล้ายกันมาใช้ได้หากพวกเขาเลือกที่จะทำเช่นนั้นโดยเฉพาะอย่างยิ่งเนื่องจากStringมีคุณสมบัติหลายอย่างที่ทำให้เป็นประเภทที่ไม่ซ้ำกัน [เช่นมันและประเภทอาร์เรย์สองชนิดเป็นประเภทเดียวที่มีการจัดสรร ขนาดไม่คงที่]
supercat

7

เหตุผล / ปัญหาพื้นฐานคือผู้ออกแบบข้อกำหนด CLS (ซึ่งกำหนดวิธีการที่ภาษาโต้ตอบกับ. net) ไม่ได้กำหนดวิธีการที่สมาชิกชั้นเรียนสามารถระบุได้ว่าพวกเขาจะต้องถูกเรียกโดยตรงแทนที่จะผ่านcallvirtโดยไม่มีผู้โทรทำการ การตรวจสอบการอ้างอิงเป็นศูนย์ และมันก็ไม่ได้ให้ความสำคัญกับการกำหนดโครงสร้างซึ่งจะไม่เป็นเรื่องปกติ "มวย"

มีข้อกำหนด CLS ที่กำหนดวิธีการดังกล่าวจากนั้นมันจะเป็นไปได้สำหรับ. net ที่จะปฏิบัติตามผู้นำที่สร้างขึ้นโดย Common Object Model (COM) อย่างสม่ำเสมอซึ่งการอ้างอิงสตริง null นั้นถูกพิจารณาว่ามีความหมายเทียบเท่ากับสตริงว่างและอื่น ๆ ประเภทที่ไม่เปลี่ยนรูปแบบที่ผู้ใช้กำหนดซึ่งควรจะมีความหมายของค่าเพื่อกำหนดค่าเริ่มต้นเช่นเดียวกัน โดยพื้นฐานแล้วอะไรจะเกิดขึ้นจะเป็นสำหรับสมาชิกแต่ละคนของStringเช่นการเขียนเป็นสิ่งที่ชอบLength [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }วิธีการนี้จะให้ความหมายที่ดีมากสำหรับสิ่งที่ควรทำตัวเหมือนค่านิยม แต่เนื่องจากปัญหาการใช้งานจะต้องเก็บไว้ในกอง ปัญหาที่ใหญ่ที่สุดด้วยวิธีนี้คือความหมายของการแปลงระหว่างประเภทดังกล่าวและObjectอาจมืดมัวเล็กน้อย

อีกวิธีหนึ่งที่จะช่วยให้คำจำกัดความของประเภทโครงสร้างพิเศษที่ไม่ได้รับมรดกObjectแต่แทนที่จะมีการชกมวยแบบกำหนดเองและการดำเนินการ unboxing (ซึ่งจะแปลงเป็น / จากประเภทคลาสอื่น ๆ ) ภายใต้วิธีการดังกล่าวจะมีประเภทคลาสNullableStringที่ทำงานเป็นสตริงทำในขณะนี้และโครงสร้าง struct แบบกล่องที่กำหนดเองStringซึ่งจะเก็บฟิลด์ส่วนตัวValueประเภทStringเดียว ความพยายามที่จะแปลงStringไปNullableStringหรือObjectจะกลับมาValueถ้าไม่ใช่ null หรือString.Emptyถ้า null ความพยายามที่จะส่งต่อStringการอ้างอิงที่ไม่ใช่ค่า null ไปยังNullableStringอินสแตนซ์จะเก็บการอ้างอิงไว้ในValue(อาจเก็บค่าว่างหากความยาวเป็นศูนย์); การส่งการอ้างอิงอื่นใดจะทำให้เกิดข้อยกเว้น

แม้ว่าสตริงจะต้องถูกเก็บไว้ในฮีป แต่ก็ไม่มีเหตุผลว่าทำไมพวกเขาไม่ควรทำตัวเหมือนประเภทของค่าที่มีค่าเริ่มต้นที่ไม่เป็นโมฆะ การเก็บไว้เป็นโครงสร้าง "ปกติ" ซึ่งมีการอ้างอิงจะมีประสิทธิภาพสำหรับรหัสที่ใช้เป็นสตริง "type" แต่จะเพิ่มเลเยอร์ทางอ้อมและความไร้ประสิทธิภาพพิเศษเมื่อส่งไปยัง "object" ในขณะที่ฉันไม่คาดการณ์. net เพิ่มคุณสมบัติข้างต้นอย่างใดอย่างหนึ่งในวันที่ล่าช้านี้บางทีนักออกแบบของกรอบงานในอนาคตอาจพิจารณารวมถึงพวกเขา


1
พูดในฐานะคนที่ทำงานใน SQL เป็นจำนวนมากและมีการจัดการกับอาการปวดหัวของออราเคิลไม่ได้ทำให้ความแตกต่างระหว่างโมฆะและความยาวเป็นศูนย์ที่ฉันดีใจมากที่ .NET ไม่ "Empty" เป็นค่า "null" ไม่ใช่

@JonofAllTrades: ฉันไม่เห็นด้วย ในโค้ดแอปพลิเคชันยกเว้นการจัดการกับรหัส db ไม่มีความหมายว่าสตริงถูกถือว่าเป็นคลาส เป็นประเภทค่าและแบบพื้นฐาน Supercat: +1 ให้กับคุณ
Fabricio Araujo

1
รหัสฐานข้อมูลใหญ่ "ยกเว้น" ตราบใดที่มีโดเมนปัญหาบางอย่างที่คุณต้องแยกความแตกต่างระหว่าง "ปัจจุบัน / รู้แล้วสตริงว่าง" และ "ไม่อยู่ / ไม่รู้จัก / ไม่เหมาะสม" เช่นฐานข้อมูลจากนั้นภาษาจะต้องสนับสนุน แน่นอนว่าตอนนี้. NET มีNullable<>สตริงที่สามารถนำมาใช้ใหม่เป็นประเภทของค่า; ฉันไม่สามารถพูดกับต้นทุนและผลประโยชน์ของตัวเลือกดังกล่าวได้

3
@JonofAllTrades: รหัสที่เกี่ยวข้องกับตัวเลขจะต้องมีวิธีการแยกแยะความแตกต่างของค่าเริ่มต้นศูนย์จาก "ไม่ได้กำหนด" เพราะมันเป็นรหัสการจัดการ nullable ที่ทำงานกับสตริงและตัวเลขจะต้องใช้วิธีการหนึ่งสำหรับสตริง nullable และอื่นสำหรับตัวเลข nullable แม้ว่าประเภทคลาสที่ไม่สามารถใช้ได้จะstringมีประสิทธิภาพมากกว่าที่Nullable<string>เป็นอยู่การใช้วิธี "มีประสิทธิภาพมากขึ้น" นั้นเป็นภาระมากกว่าที่จะสามารถใช้วิธีการเดียวกันสำหรับค่าฐานข้อมูลข้อมูลที่ไม่สามารถใช้ได้ทั้งหมด
supercat

5

เพราะตัวแปรสตริงคือการอ้างอิงที่ไม่อินสแตนซ์

การกำหนดค่าเริ่มต้นเป็น Empty โดยค่าเริ่มต้นนั้นเป็นไปได้ แต่จะมีการแนะนำที่ไม่สอดคล้องกันมากมายทั่วกระดาน


3
ไม่มีเหตุผลใดที่stringจะต้องเป็นประเภทอ้างอิง เพื่อให้แน่ใจว่าอักขระจริงที่ประกอบเป็นสตริงจะต้องถูกจัดเก็บไว้ใน heap แต่เนื่องจากจำนวนการสนับสนุนเฉพาะที่สตริงมีใน CLR อยู่แล้วจะไม่เป็นการยืดที่System.Stringจะเป็นประเภทค่าที่มี ฟิลด์ส่วนตัวValueประเภทHeapStringเดียว ข้อมูลที่จะเป็นชนิดการอ้างอิงและจะเริ่มต้นกับnullแต่Stringstruct ที่มีValueข้อมูลเป็น null จะประพฤติตัวเป็นสตริงที่ว่างเปล่า ข้อเสียเพียงอย่างเดียวของวิธีนี้คือ ...
supercat

1
... ที่หล่อStringไปObjectก็จะในกรณีที่ไม่มีรหัสกรณีพิเศษในการรันไทม์ที่ก่อให้เกิดการสร้างชนิดบรรจุกล่องเช่นในกองมากกว่าเพียงแค่การคัดลอกการอ้างอิงถึงString HeapString
supercat

1
@supercat - ไม่มีใครพูดว่าสตริงควร / อาจเป็นประเภทค่า
Henk Holterman

1
ไม่มีใครยกเว้นฉัน การมีสตริงเป็นประเภทค่า "พิเศษ" (พร้อมเขตข้อมูลการอ้างอิงส่วนตัว) จะช่วยให้การจัดการส่วนใหญ่มีประสิทธิภาพอย่างที่เป็นอยู่ในขณะนี้ยกเว้นการตรวจสอบโมฆะเพิ่มเติมเกี่ยวกับวิธีการ / คุณสมบัติเช่น.Lengthฯลฯ เพื่อให้อินสแตนซ์ที่เก็บ การอ้างอิง Null จะไม่พยายามอ้างอิงซ้ำ แต่ทำตัวตามความเหมาะสมกับสตริงว่าง ไม่ว่าจะเป็นกรอบการทำงานจะดีขึ้นหรือแย่ลงด้วยstringการดำเนินการวิธีการที่ถ้าใครอยากdefault(string)จะเป็นสตริงที่ว่างเปล่า ...
SuperCat

1
... การstringเป็น wrapper ประเภทค่าในเขตข้อมูลการอ้างอิงจะเป็นวิธีการที่จำเป็นต้องมีการเปลี่ยนแปลงที่น้อยที่สุดในส่วนอื่น ๆ ของ. net [แน่นอนถ้ามีใครยินดีที่จะยอมรับการแปลงจากStringการObjectสร้างรายการกล่องพิเศษ ใคร ๆ ก็Stringสามารถเป็นโครงสร้างธรรมดาที่มีประเภทChar[]ที่ไม่เคยเปิดเผย] ฉันคิดว่าการHeapStringพิมพ์อาจจะดีกว่า แต่ในบางวิธีสตริงที่มีค่าประเภทการถือChar[]จะง่ายกว่า
supercat

5

ทำไมนักออกแบบของ C # เลือกใช้ null เป็นค่าเริ่มต้นของสตริง

เนื่องจากสตริงเป็นประเภทการอ้างอิงประเภทการอ้างอิงจึงเป็นค่าเริ่มต้นคือnullประเภทอ้างอิงค่าเริ่มต้นคือตัวแปรประเภทการอ้างอิงจะเก็บการอ้างอิงไปยังข้อมูลจริง

มาใช้defaultคำสำคัญสำหรับกรณีนี้

string str = default(string); 

strเป็นstringดังนั้นจึงเป็นชนิดการอ้างอิงnullเพื่อเป็นค่าเริ่มต้น

int str = (default)(int);

strเป็นintจึงเป็นประเภทค่าzeroดังนั้นค่าเริ่มต้นคือ


4

หากค่าเริ่มต้นของstringสตริงว่างเปล่าฉันจะไม่ต้องทดสอบ

ไม่ถูกต้อง! การเปลี่ยนค่าเริ่มต้นจะไม่เปลี่ยนแปลงความจริงที่ว่ามันเป็นชนิดการอ้างอิงและคนที่ยังคงสามารถอย่างชัดเจนตั้งค่าnullอ้างอิงที่จะเป็น

นอกจากนี้Nullable<String>จะทำให้รู้สึก

จุดที่แท้จริง มันจะสมเหตุสมผลมากกว่าที่จะไม่อนุญาตให้nullใช้ประเภทการอ้างอิงใด ๆ แทนที่จะต้องการNullable<TheRefType>คุณสมบัตินั้น

เหตุใดนักออกแบบของ C # จึงเลือกใช้nullเป็นค่าเริ่มต้นของสตริง

ความสอดคล้องกับประเภทอ้างอิงอื่น ๆ ตอนนี้ทำไมอนุญาตให้ใช้nullในประเภทอ้างอิงเลย อาจจะเพื่อให้มันรู้สึกเหมือน C Nullableแม้ว่านี่คือการตัดสินใจการออกแบบที่น่าสงสัยในภาษาที่ยังมี


3
อาจเป็นเพราะ Nullable เปิดตัวใน. NET 2.0 Framework เท่านั้นดังนั้นก่อนหน้านี้จึงไม่พร้อมใช้งาน
jcolebrand

3
ขอบคุณ Dan Burton ที่ชี้ให้เห็นว่าบางคนสามารถตั้งค่าเริ่มต้นเป็นโมฆะในประเภทการอ้างอิงในภายหลัง การคิดในเรื่องนี้บอกฉันว่าเจตนาดั้งเดิมของฉันในคำถามนำไปสู่การไม่ใช้ประโยชน์
Marcel

4

บางทีถ้าคุณต้องการใช้??โอเปอเรเตอร์เมื่อกำหนดตัวแปรสตริงของคุณมันอาจช่วยคุณได้

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

2

String เป็นวัตถุที่ไม่เปลี่ยนรูปซึ่งหมายความว่าเมื่อกำหนดค่าแล้วค่าเก่าจะไม่ถูกลบออกจากหน่วยความจำ แต่ยังคงอยู่ในตำแหน่งเดิมและค่าใหม่จะถูกใส่ในตำแหน่งใหม่ ดังนั้นถ้าค่าเริ่มต้นของString aเป็นString.EmptyมันจะเสียString.Emptyบล็อกในหน่วยความจำว่างเมื่อได้รับค่าแรก

แม้ว่ามันจะดูจิ๋ว แต่ก็อาจกลายเป็นปัญหาเมื่อเริ่มต้นสตริงจำนวนมากที่มีค่าเริ่มต้นString.Emptyเป็น แน่นอนว่าคุณสามารถใช้StringBuilderคลาสที่ไม่แน่นอนได้หากนี่จะเป็นปัญหา


ขอขอบคุณที่กล่าวถึงสิ่งแรกที่เริ่มต้น
Marcel

3
มันจะเป็นปัญหาได้อย่างไรเมื่อเริ่มต้นอาร์เรย์ขนาดใหญ่? String.Emptyตั้งแต่ที่คุณกล่าวว่าสายจะไม่เปลี่ยนรูปองค์ประกอบทั้งหมดของอาร์เรย์ก็จะชี้ไปเหมือนกัน ฉันเข้าใจผิด
Dan Burton

2
ค่าเริ่มต้นสำหรับประเภทใด ๆจะมีบิตทั้งหมดตั้งค่าเป็นศูนย์ วิธีเดียวที่ค่าดีฟอลต์stringคือสตริงว่างคืออนุญาตให้ all-bits-zero แทนค่าสตริงว่าง มีจำนวนของวิธีนี้อาจจะประสบความสำเร็จมี String.Emptyแต่ฉันไม่คิดว่าเกี่ยวข้องกับการเริ่มต้นการอ้างอิงถึง
supercat

คำตอบอื่น ๆ กล่าวถึงประเด็นนี้เช่นกัน ผมคิดว่าคนได้ข้อสรุปว่ามันจะไม่ทำให้ความรู้สึกในการรักษาระดับสตริงเป็นกรณีพิเศษและให้สิ่งอื่น ๆ กว่าทุกบิตเป็นศูนย์เริ่มต้นแม้ถ้ามันเป็นสิ่งที่ต้องการหรือString.Empty ""
djv

@DanV: การเปลี่ยนพฤติกรรมการเริ่มต้นของstringสถานที่จัดเก็บจะต้องยังมีการเปลี่ยนแปลงพฤติกรรมการเริ่มต้นของ structs stringทั้งหมดหรือชั้นเรียนที่มีสาขาประเภท นั่นจะแสดงถึงการเปลี่ยนแปลงครั้งใหญ่ในการออกแบบของ. net ซึ่งปัจจุบันคาดว่าจะเริ่มต้นเป็นศูนย์ชนิดใดก็ได้โดยไม่ต้องคิดเกี่ยวกับสิ่งที่มันเป็นประหยัดเพียงสำหรับขนาดรวมของมัน
supercat

2

เนื่องจากสตริงเป็นประเภทอ้างอิงและค่าเริ่มต้นสำหรับประเภทอ้างอิงเป็นโมฆะ


0

บางทีstringคำหลักอาจทำให้คุณสับสนเนื่องจากดูเหมือนว่าการประกาศประเภทค่าอื่น ๆแต่จริงๆแล้วมันเป็นชื่อแทนSystem.Stringตามที่อธิบายไว้ในคำถามนี้
นอกจากนี้ยังมีสีเข้มสีฟ้าใน Visual Studio structและตัวอักษรตัวแรกพิมพ์เล็กอาจทำให้เข้าใจผิดคิดว่ามันเป็น


3
objectคำหลักนั้นเหมือนกันหรือไม่ แม้ว่าจะเป็นที่ยอมรับ แต่ก็ใช้น้อยกว่าstringมาก

2
ในฐานะที่เป็นนามแฝงสำหรับint System.Int32ประเด็นของคุณคืออะไร? :)
Thorarin

@Thorari @delnan: พวกเขาทั้งนามแฝง แต่System.Int32เป็นStructจึงมีค่าเริ่มต้นในขณะที่System.Stringเป็นต้องชี้กับค่าเริ่มต้นของClass nullพวกมันถูกนำเสนอด้วยสายตาในแบบอักษร / สีเดียวกัน หากไม่มีความรู้เราสามารถคิดได้ว่าพวกเขาทำแบบเดียวกัน (= มีค่าเริ่มต้น) คำตอบของฉันถูกเขียนด้วยen.wikipedia.org/wiki/Cognitive_psychologyความคิดทางจิตวิทยาเกี่ยวกับความรู้ความเข้าใจด้านหลัง :-)
Alessandro Da Rugna

ฉันค่อนข้างแน่ใจว่า Anders Hejlsberg พูดในการสัมภาษณ์ช่อง 9 ฉันรู้ความแตกต่างระหว่างฮีปและสแต็ก แต่ความคิดกับ C # คือโปรแกรมเมอร์ธรรมดาไม่จำเป็นต้องทำ
โทมัสโคเอล

0

ประเภท Nullable ไม่ได้เข้ามาจนถึง 2.0

หากมีประเภท nullable ได้ทำในการเริ่มต้นของภาษาแล้วสตริงจะไม่เป็นโมฆะและสตริง? คงจะเป็นโมฆะ แต่พวกเขาไม่สามารถทำสิ่งนี้เพื่อรองรับความเข้ากันได้แบบย้อนหลัง

หลายคนพูดคุยเกี่ยวกับประเภทอ้างอิงหรือไม่อ้างอิงประเภท แต่สตริงเป็นออกจากชั้นสามัญและการแก้ปัญหาจะได้รับการพบเพื่อให้เป็นไปได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.