การเริ่มต้นสตริงเริ่มต้น: NULL หรือว่างเปล่า? [ปิด]


130

ฉันเริ่มต้นสตริงของฉันเป็น NULL เสมอโดยคิดว่า NULL หมายถึงการไม่มีค่าและ "" หรือ String.Empty เป็นค่าที่ถูกต้อง ฉันได้เห็นตัวอย่างเพิ่มเติมของโค้ดเมื่อเร็ว ๆ นี้โดยที่ String.Empty ถือเป็นค่าเริ่มต้นหรือแสดงถึงไม่มีค่า สิ่งนี้ทำให้ฉันแปลกประหลาดโดยประเภทที่เพิ่มใหม่ว่างใน c # ดูเหมือนว่าเรากำลังก้าวถอยหลังด้วยสตริงโดยไม่ใช้ NULL เพื่อแสดงถึง 'ไม่มีค่า'

คุณใช้อะไรเป็นตัวเริ่มต้นเริ่มต้นและทำไม?

แก้ไข: จากคำตอบที่ฉันคิดต่อไป

  1. การหลีกเลี่ยงการจัดการข้อผิดพลาดหากค่าไม่ควรเป็นโมฆะเหตุใดจึงตั้งค่าเป็นค่าเริ่มNULLต้นตั้งแต่แรก บางทีอาจเป็นการดีกว่าที่จะระบุข้อผิดพลาดในสถานที่ที่เกิดขึ้นแทนที่จะปกปิดข้อผิดพลาดทั้งหมดในโค้ดเบสของคุณ

  2. การหลีกเลี่ยงการตรวจสอบโมฆะหากคุณเบื่อที่จะทำการตรวจสอบค่าว่างในโค้ดแล้วจะไม่เป็นการดีกว่าที่จะสรุปการตรวจสอบค่าว่าง บางทีห่อ (หรือขยาย!) วิธีการสตริงเพื่อให้NULLปลอดภัย? จะเกิดอะไรขึ้นถ้าคุณใช้งานอยู่ตลอดเวลาString.Emptyและโมฆะเกิดขึ้นเพื่อทำงานในระบบของคุณคุณเริ่มเพิ่มการNULLตรวจสอบหรือไม่?

ฉันอดไม่ได้ที่จะกลับความเห็นว่ามันคือความเกียจคร้าน DBA ใด ๆ จะตบคุณเก้าวิธีเพื่อโง่ถ้าคุณใช้ '' แทนnullในฐานข้อมูลของเขา ฉันคิดว่าหลักการเดียวกันนี้ใช้ในการเขียนโปรแกรมและควรมีใครสักคนที่จะตีหัวคนที่ใช้String.Emptyมากกว่าที่NULLจะไม่แสดงคุณค่า

คำถามที่เกี่ยวข้อง


'The Sane'? ต้องไม่ใช่ดาน่าที่ฉันรู้
vfilby

@ Joel ฉันรู้สึกประหลาดใจที่มีคนไม่รู้เรื่อง Zim หรือ GIR มากมายฉันยังประหลาดใจกับเพื่อนบางคนที่คิดว่าน่ารังเกียจ ไม่ได้บอกว่ามันเป็นความดีที่บริสุทธิ์ แต่มีนักเก็ตที่น่ากลัวอยู่ในนั้น
vfilby

ฉันรู้ แต่บางครั้งก็สนุกที่ได้แกล้งทำเป็นอย่างอื่น
Dana the Sane

1
ฉันพบปัญหานี้ในคอลเลกชันฟอร์ม MVC หรือตัวแปรเซสชันเป็นจำนวนมากฉันพบว่าสิ่งที่มีประโยชน์ที่สุดคือการแปลง null เป็น String ว่างเปล่าด้วย ?? ชวเลขแล้วใช้การดำเนินการสตริงที่ต้องการ เช่น. (item ?? String.Empty) .Trim (). ToUpper ()
sonjz

4
มันไม่สร้างสรรค์ ??
nawfal

คำตอบ:


111

+1 สำหรับการแยกแยะระหว่าง "ว่าง" และ NULL ฉันยอมรับว่า "ว่าง" ควรหมายถึง "ถูกต้อง แต่ว่าง" และ "NULL" ควรหมายถึง "ไม่ถูกต้อง"

ดังนั้นฉันจะตอบคำถามของคุณดังนี้:

ว่างเปล่าเมื่อฉันต้องการค่าเริ่มต้นที่ถูกต้องซึ่งอาจเปลี่ยนแปลงได้หรือไม่ก็ได้เช่นชื่อกลางของผู้ใช้

NULLเมื่อเป็นข้อผิดพลาดหากรหัสที่ตามมาไม่ได้ตั้งค่าไว้อย่างชัดเจน


11
การแยกแยะความแตกต่างระหว่าง NULL และว่างจะดีมากเมื่อมีความแตกต่างระหว่างทั้งสอง มีหลายกรณีที่ไม่มีความแตกต่างกันดังนั้นการมีสองวิธีในการแสดงสิ่งเดียวกันคือความรับผิด
Greg Smalter

6
@Greg: แม้ว่าฉันจะยอมรับว่าความหลากหลายมีโอกาสทำให้เกิดความสับสน แต่ก็เป็นทรัพย์สินที่ดีเช่นกัน หลักการเขียน "" หรือ NULL ที่เรียบง่ายและสอดคล้องกันเพื่อแยกความแตกต่างระหว่างค่าที่ถูกต้องและไม่ถูกต้องจะทำให้โค้ดของคุณเข้าใจง่ายขึ้น นี่คือเหตุผลที่ฉันมักจะทดสอบบูลีนด้วย "if (var)" พอยน์เตอร์ด้วย "if (var! = NULL)" และจำนวนเต็มด้วย "if (var! = 0)" - ทั้งหมดนี้หมายถึงคอมไพเลอร์เหมือนกัน แต่พวกเขามีข้อมูลเพิ่มเติมที่ช่วยให้นักพัฒนาที่ไม่ดีที่ดูแลรหัสของฉัน
Adam Liss

32

ตามMSDN :

การเริ่มต้นสตริงด้วยEmptyค่าแทนการเริ่มต้นnullคุณสามารถลดโอกาสที่จะNullReferenceExceptionเกิดขึ้นได้

IsNullOrEmpty()อย่างไรก็ตามการใช้เสมอคือการปฏิบัติที่ดี


45
เพียงเพราะคุณลดโอกาสของข้อยกเว้นไม่ได้หมายความว่าข้อยกเว้นไม่ควรเกิดขึ้น หากรหัสของคุณขึ้นอยู่กับค่าที่มีอยู่ก็ควรมีข้อยกเว้น!
rmeador

1
แน่นอนว่าไม่มีข้อโต้แย้ง OTOH ถ้าคุณแค่ต่อท้ายสตริงเข้าด้วยกัน ... ฉันคิดว่ามันขึ้นอยู่กับรูปแบบการเข้ารหัสประสบการณ์และสถานการณ์
Tomalak

นี่คือสิ่งที่ฉันใช้แยกแยะว่าจะใช้อะไรเป็นหลัก
PositiveGuy

3
อย่าลืมIsNullOrWhiteSpace ()สำหรับ. NET framework 4+
Coops

13

ทำไมคุณถึงต้องการให้สตริงของคุณเริ่มต้นเลย? คุณไม่จำเป็นต้องเริ่มต้นตัวแปรเมื่อคุณประกาศและ IMO คุณควรทำก็ต่อเมื่อค่าที่คุณกำหนดนั้นถูกต้องในบริบทของบล็อกโค้ด

ฉันเห็นสิ่งนี้มาก:

string name = null; // or String.Empty
if (condition)
{
  name = "foo";
}
else
{
  name = "bar";
}

return name;

การไม่กำหนดค่าเริ่มต้นให้เป็นโมฆะจะมีประสิทธิภาพเช่นเดียวกับ นอกจากนี้บ่อยครั้งที่คุณต้องการกำหนดค่า เมื่อเริ่มต้นเป็น null คุณอาจพลาดเส้นทางรหัสที่ไม่ได้กำหนดค่า ชอบมาก:

string name = null; // or String.Empty
if (condition)
{
  name = "foo";
}
else if (othercondition)
{
  name = "bar";
}

return name; //returns null when condition and othercondition are false

เมื่อคุณไม่ได้กำหนดค่าเริ่มต้นเป็น null คอมไพลเลอร์จะสร้างข้อผิดพลาดโดยบอกว่าไม่ใช่เส้นทางรหัสทั้งหมดที่กำหนดค่า แน่นอนว่านี่เป็นตัวอย่างที่ง่ายมาก ...

Matthijs


ใน Visual Studio ซึ่งฉันเชื่อว่าโปรแกรมเมอร์ C # เกือบทุกคนใช้สถานการณ์ที่สองของคุณ (โดยไม่มี= null) จะสร้างคำเตือนด้วยเหตุผลที่คุณระบุ - ไม่สำคัญว่าค่าเริ่มต้นของสตริงจะเป็นค่าว่างหรือไม่ หากคุณไม่รับประกันการกำหนดผ่านทุกเส้นทางรหัส IDE (และ / หรือฉันคิดว่าคอมไพลเลอร์อ้างอิง [?]) จะสร้างคำเตือน แม้ว่าคำเตือนจะไม่ป้องกันการคอมไพล์ แต่ก็ยังคงอยู่ที่นั่นการปล่อยให้คำเตือนที่แก้ไขได้ง่ายสามารถช่วยทำให้ผู้อื่นเข้าใจผิดซึ่งอาจรับประกันความสนใจของโปรแกรมเมอร์
Code Jockey

สำหรับความรู้ของฉันสถานการณ์แรกจะมีความสุขอย่างสมบูรณ์โดยไม่ต้องเริ่มต้นnameเป็นnull(ไม่มีคำเตือน) เพราะทุกเส้นทางรหัสกำหนดค่าให้name- ไม่จำเป็นต้องเริ่มต้นที่นั่นเลย
Code Jockey

8

สำหรับซอฟต์แวร์ส่วนใหญ่ที่ไม่ใช่ซอฟต์แวร์ประมวลผลสตริงตรรกะของโปรแกรมไม่ควรขึ้นอยู่กับเนื้อหาของตัวแปรสตริง เมื่อใดก็ตามที่ฉันเห็นสิ่งนี้ในโปรแกรม:

if (s == "value")

ฉันรู้สึกแย่ เหตุใดจึงมีสตริงลิเทอรัลในวิธีนี้ การตั้งค่าsคืออะไร? รู้หรือไม่ว่าตรรกะขึ้นอยู่กับค่าของสตริง ไม่ทราบว่าต้องเป็นตัวพิมพ์เล็กจึงจะทำงานได้? ฉันควรจะแก้ไขโดยเปลี่ยนเป็นใช้String.Compareหรือไม่? ฉันควรสร้างEnumและแยกวิเคราะห์หรือไม่

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

เมื่อรู้สิ่งนี้ฉันจะไม่กระพริบตาเมื่อเห็นสิ่งนี้ในฐานรหัสของเรา:

string msg = Validate(item);
if (msg != null)
{
   DisplayErrorMessage(msg);
   return;
}

ฉันรู้ว่าValidateจะไม่มีวันกลับมาString.Emptyเพราะเราเขียนโค้ดได้ดีกว่านั้น

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

(เพื่อให้แน่ใจว่าฉันไม่ได้พูดออกไปจากตูดของฉันฉันเพิ่งค้นหา codebase ของเราสำหรับ `` String.IsNullOrEmpty 'การเกิดขึ้นทั้งหมด 54 ครั้งอยู่ในวิธีการที่ประมวลผลการป้อนข้อมูลของผู้ใช้คืนค่าจากสคริปต์ Python ตรวจสอบค่าที่ดึงมาจาก API ภายนอก ฯลฯ )


6

นี่คือช่องโหว่ในภาษา C # ไม่มีวิธีการกำหนดสตริงที่ไม่สามารถเป็นโมฆะได้ สิ่งนี้ทำให้เกิดปัญหาง่ายๆเหมือนกับที่คุณกำลังอธิบายซึ่งบังคับให้โปรแกรมเมอร์ต้องตัดสินใจว่าไม่ควรทำเนื่องจากในหลาย ๆ กรณี NULL และ String การว่างเปล่าหมายถึงสิ่งเดียวกัน ในทางกลับกันสามารถบังคับให้โปรแกรมเมอร์คนอื่นต้องจัดการทั้ง NULL และ String.Empty ซึ่งน่ารำคาญในภายหลัง

ปัญหาที่ใหญ่กว่าคือฐานข้อมูลอนุญาตให้คุณกำหนดฟิลด์ที่แมปกับสตริง C # แต่ฟิลด์ฐานข้อมูลสามารถกำหนดเป็น NULL ได้ ดังนั้นจึงไม่มีวิธีใดที่จะแสดงฟิลด์ varchar (100) NOT NULL ได้อย่างถูกต้องใน SQL Server โดยใช้ประเภท C #

ภาษาอื่น ๆ เช่น Spec # อนุญาตสิ่งนี้

ในความคิดของฉันการที่ C # ไม่สามารถกำหนดสตริงที่ไม่อนุญาตให้มี null นั้นแย่พอ ๆ กับการไม่สามารถกำหนด int ที่ไม่อนุญาตให้เป็น null ได้

เพื่อตอบคำถามของคุณอย่างสมบูรณ์: ฉันมักจะใช้สตริงว่างสำหรับการกำหนดค่าเริ่มต้นเนื่องจากมันคล้ายกับการทำงานของชนิดข้อมูลฐานข้อมูลมากกว่า (แก้ไข: คำสั่งนี้ไม่ชัดเจนมากควรอ่านว่า "ฉันใช้สตริงว่างสำหรับการเริ่มต้นเริ่มต้นเมื่อ NULL เป็นสถานะที่ไม่จำเป็นมากในลักษณะเดียวกับที่ฉันตั้งค่าคอลัมน์ฐานข้อมูลเป็น NOT NULL ถ้า NULL จะเป็นสถานะที่ไม่จำเป็นในทำนองเดียวกัน คอลัมน์ DB จำนวนมากของฉันถูกตั้งค่าเป็น NOT NULL ดังนั้นเมื่อฉันนำคอลัมน์เหล่านั้นมาเป็นสตริง C # สตริงจะว่างเปล่าหรือมีค่า แต่จะไม่เป็นโมฆะกล่าวอีกนัยหนึ่งฉันเริ่มต้นสตริงเป็น NULL เท่านั้น ถ้า null มีความหมายที่แตกต่างจากความหมายของ String.Empty และฉันพบว่ากรณีนั้นมีค่าน้อยกว่าทั่วไป (แต่คนที่นี่ได้ยกตัวอย่างกรณีนี้ที่ถูกต้อง) ")


ใช้ String.Empty เป็นเพียงคล้ายกับหนึ่งในวิธีการที่สตริงฐานข้อมูลที่ถูกกำหนด การใช้ null เพื่อแทนค่าไม่มีความสอดคล้องกับ nvarchar null มากกว่า ฉันคิดว่า DBA ใด ๆ ที่คุ้มค่ากับเกลือของพวกเขาจะตบคุณเก้าวิธีในการโง่ถ้าคุณใช้ '' เพื่อแสดงว่าไม่มีค่า
vfilby

จริงๆแล้วเกร็กคุณเข้าใจผิดแล้ว เป็นประเภทค่าที่ไม่เป็นค่าว่างซึ่งน้อยที่สุด "ประเภทฐานข้อมูลทำงานอย่างไร" เนื่องจากไม่สามารถเก็บค่าว่างได้จึงไม่สามารถแมปกับคอลัมน์ที่เป็นโมฆะได้ ในสัญญาสตริงใด ๆ สามารถแมปกับคอลัมน์ varchar ใดก็ได้
Tor Haugen

คุณพูดถูกการยืนยันครั้งสุดท้ายของฉันยังไม่ชัดเจนพอ บ่อยครั้งที่คอลัมน์ฐานข้อมูลของฉันไม่ได้เป็นโมฆะ (เพราะจะไม่มีความแตกต่างกันระหว่างความหมายของสตริงว่างและ NULL) ดังนั้นฉันจึงพยายามทำให้สตริงของฉันเหมือนกันโดยไม่เก็บค่าว่างไว้ในนั้นและนั่นคือสิ่งที่ฉันหมายถึง
Greg Smalter

5

มันขึ้นอยู่กับ.

คุณต้องสามารถบอกได้ว่าค่านั้นหายไปหรือไม่ (เป็นไปได้หรือไม่ที่จะไม่กำหนดค่า)

สตริงว่างเป็นค่าที่ถูกต้องสำหรับการใช้งานสตริงนั้นหรือไม่

หากคุณตอบว่า "ใช่" ทั้งคู่คุณจะต้องใช้ค่าว่าง มิฉะนั้นคุณจะไม่สามารถบอกความแตกต่างระหว่าง "ไม่มีค่า" และ "สตริงว่าง" ได้

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



3

ฉันตั้งค่าเป็น "" หรือ null - ฉันมักจะตรวจสอบโดยใช้ String.IsNullOrEmpty ดังนั้นก็ใช้ได้

แต่คนในตัวฉันบอกว่าฉันควรตั้งค่าเป็นโมฆะก่อนที่ฉันจะมีค่าที่เหมาะสมสำหรับมัน ...



2

เป็นไปได้หรือไม่ว่านี่เป็นเทคนิคการหลีกเลี่ยงข้อผิดพลาด (แนะนำหรือไม่ .. )? เนื่องจาก "" ยังคงเป็นสตริงคุณจะสามารถเรียกใช้ฟังก์ชันสตริงได้ซึ่งจะทำให้เกิดข้อยกเว้นหากเป็น NULL?


1
นั่นเป็นข้อแก้ตัวที่ฉันได้ยินตามปกติฟังดูเหมือนความเกียจคร้าน "ฉันไม่ต้องการรบกวนการตรวจสอบค่านี้ดังนั้นฉันจะใช้ทางลัด" เป็นอย่างไรสำหรับฉัน
vfilby

ใช่ฉันไม่เห็นด้วย อาจมีบางสถานการณ์ที่การลดจำนวนรหัสตรวจสอบข้อผิดพลาดเป็นสิ่งที่ดี แต่การเรียกฟังก์ชันที่ไม่มีผลก็ไม่ได้ดีที่สุดเช่นกัน ..
Dana the Sane

2

ฉันมักจะเริ่มต้นเป็น NULL .

ผม มักจะใช้string.IsNullOrEmpty(someString)เพื่อตรวจสอบค่าของมัน

ง่าย


1

มันขึ้นอยู่กับสถานการณ์ ในกรณีส่วนใหญ่ฉันใช้ String.Empty เพราะฉันไม่ต้องการทำการตรวจสอบค่าว่างทุกครั้งที่พยายามใช้สตริง ทำให้โค้ดง่ายขึ้นมากและคุณมีโอกาสน้อยที่จะแนะนำ NullReferenceException ที่ไม่ต้องการล่ม

ฉันจะตั้งค่าสตริงเป็น null ก็ต่อเมื่อฉันต้องการทราบว่าถูกตั้งค่าหรือไม่และสตริงว่างที่ใดเป็นสิ่งที่ถูกต้องเพื่อตั้งค่าเป็น ในทางปฏิบัติฉันพบว่าสถานการณ์เหล่านี้หายาก


1

สตริงว่างคือค่า (ข้อความที่บังเอิญไม่มีตัวอักษรใด ๆ ) Null หมายถึงไม่มีค่า

ฉันเริ่มต้นตัวแปรให้เป็นโมฆะเมื่อฉันต้องการระบุว่าพวกมันไม่ได้ชี้ไปที่หรือมีค่าจริง - เมื่อเจตนานั้นไม่มีค่า


1

การย้ำการตอบกลับของ Tomalak โปรดทราบว่าเมื่อคุณกำหนดตัวแปรสตริงให้เป็นค่าเริ่มต้นของ null ตัวแปรของคุณจะไม่ใช่สตริงออบเจ็กต์อีกต่อไป เหมือนกันกับวัตถุใด ๆ ใน C # ดังนั้นหากคุณพยายามเข้าถึงวิธีการหรือคุณสมบัติใด ๆ สำหรับตัวแปรของคุณและคุณสมมติว่าเป็นวัตถุสตริงคุณจะได้รับข้อยกเว้น NullReferenceException


1

ควรใช้ Null ในกรณีที่ค่าเป็นทางเลือก หากค่านี้ไม่สามารถระบุได้ (เช่น 'ชื่อ' หรือ 'ที่อยู่') ค่านั้นไม่ควรเป็นโมฆะ สิ่งนี้ใช้กับฐานข้อมูลเช่นเดียวกับ POCO และส่วนต่อประสานผู้ใช้ Null หมายถึง "ค่านี้เป็นทางเลือกและไม่มีอยู่ในขณะนี้"

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

โดยส่วนตัวแล้วฉันอยากให้สตริงไม่เป็นโมฆะโดยค่าเริ่มต้น แต่จะเป็นโมฆะแทนถ้าเราประกาศ "สตริง?" แม้ว่าอาจจะไม่เป็นไปได้หรือมีเหตุผลในระดับลึก ไม่แน่ใจ.



0

ฉันคิดว่าไม่มีเหตุผลที่จะไม่ใช้ null สำหรับค่าที่ไม่ได้กำหนด (หรือ ณ ที่นี้ในขั้นตอนของโปรแกรมที่ไม่เกิดขึ้น) ถ้าคุณต้องการแยกแยะมี == null หากคุณแค่ต้องการตรวจสอบค่าบางค่าและไม่สนใจว่าจะเป็นค่าว่างหรือค่าอื่น String.Equals ("XXX", MyStringVar) ก็ทำได้ดี

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