ทำไม String ถึงไม่ส่งค่าคงที่?


188

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


5
นี้คำถามที่สามารถแก้ปัญหานี้คำตอบสั้น ๆ คือไม่มีใครรู้ว่า ...
gdoron สนับสนุน Monica

ใช่ +1 สำหรับคำตอบของ Eric Lippert ขอบคุณ!
รวิส

โดยเฉพาะอย่างยิ่ง Decimal.Zero เป็น const (จากมุมมองของผู้ใช้นั่นคือ ... )
Hamish Grubijan

คำตอบ:


148

เหตุผลที่static readonlyถูกนำมาใช้แทนconstเป็นเพราะใช้กับรหัสที่ไม่มีการจัดการตามที่ระบุโดยไมโครซอฟท์ที่นี่ในแหล่งที่มาร่วมกันภาษาทั่วไปโครงสร้างพื้นฐาน 2.0 ที่วางจำหน่าย sscli20\clr\src\bcl\system\string.csแฟ้มดูที่เป็น

ค่าคงที่ว่างเปล่าเก็บค่าสตริงว่าง เราจำเป็นต้องเรียกใช้ตัวสร้างสตริงเพื่อให้คอมไพเลอร์ไม่ได้ทำเครื่องหมายสิ่งนี้ว่าเป็นตัวอักษร

การทำเครื่องหมายสิ่งนี้เป็นตัวอักษรหมายถึงว่ามันไม่ปรากฏเป็นเขตข้อมูลที่เราสามารถเข้าถึงได้จากเจ้าของภาษา

ผมพบว่าข้อมูลนี้จากบทความที่มีประโยชน์นี้ที่ CodeProject


ฉันจะขอบคุณมากถ้าคุณสามารถอธิบายความคิดเห็นนี้ (เพราะ Jon Skeet ไม่สามารถ ... ) ดูที่นี่: stackoverflow.com/questions/8462697/…
gdoron ให้การสนับสนุน Monica

2
@gdoron: ฉันเดา (และมันคือการเดา) คือสิ่งนี้ เมื่อค่าถูกกำหนดเป็นตัวอักษร (ค่าคงที่) ค่าของมันจะถูกแทรกในสถานที่ที่มันถูกอ้างอิงในขณะที่เมื่อมันไม่ได้ถูกกำหนดเป็นตัวอักษรแหล่งที่มาของค่าที่มีการอ้างอิงและค่าที่แท้จริงจะถูกดึงที่รันไทม์ ฉันสงสัยว่าหลังอาจมั่นใจได้ว่า marshalling ที่เหมาะสมของสตริงเกิดขึ้นระหว่าง native และ. NET ที่ runtime - ถ้ามันเป็นตัวอักษรบางทีคอมไพเลอร์เนทีฟจะต้องดึงค่าตัวอักษรลงในรหัสพื้นเมืองของมันซึ่งอาจไม่ เป็นไปได้ นี่คือการคาดเดาทั้งหมดในส่วนของฉันแม้ว่า
Jeff Yates

7
หมายความว่าต้องใช้ "" แทนการใช้สตริงแทนค่าพารามิเตอร์ในเมธอด ซึ่งน่ารำคาญเล็กน้อย
nicodemus13

17
"" สามารถดูเหมือนความผิดพลาดในขณะที่สตริงตัวเล็กแสดงความตั้งใจโดยเจตนา
Christopher Stevenson

3
@JeffYates ฉันต้องการเพิ่มความจริงที่ว่ามันไม่สอดคล้องกันเป็นที่น่ารำคาญอยู่แล้ว ผู้คนจะเห็นรหัสที่เหลือและสงสัยว่า "ทำไมเขาถึงใช้" "ที่นี่แทน String.Empty?" ฉันกำลังพิจารณาอย่างจริงจังว่าจะไม่ใช้String.Emptyอีกต่อไปด้วยเหตุผลเพียงอย่างเดียว
julealgon

24

ฉันคิดว่ามีความสับสนและการตอบสนองที่ไม่ดีที่นี่

ก่อนอื่นconstฟิลด์คือstaticสมาชิก ( ไม่ใช่สมาชิกอินสแตนซ์ )

ตรวจสอบส่วนที่ 10.4 ค่าคงที่ของข้อกำหนดภาษา C #

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

หากpublic constสมาชิกเป็นสมาชิกคงไม่สามารถคิดได้ว่าค่าคงที่จะสร้างวัตถุใหม่

ให้นี้บรรทัดของรหัสต่อไปทำว่าสิ่งเดียวกันในส่วนที่เกี่ยวกับการสร้างวัตถุใหม่

public static readonly string Empty = "";
public const string Empty = "";

นี่คือบันทึกจาก Microsoft ที่อธิบายถึงความแตกต่างระหว่าง 2:

คำหลักแบบอ่านอย่างเดียวแตกต่างจากคำหลัก const ฟิลด์ const สามารถเริ่มต้นได้ที่การประกาศของฟิลด์เท่านั้น เขตข้อมูลแบบอ่านอย่างเดียวสามารถเริ่มต้นได้ทั้งที่การประกาศหรือในตัวสร้าง ดังนั้นเขตข้อมูลแบบอ่านอย่างเดียวสามารถมีค่าแตกต่างกันขึ้นอยู่กับตัวสร้างที่ใช้ นอกจากนี้ในขณะที่เขตข้อมูล const เป็นค่าคงที่เวลารวบรวม, เขตข้อมูลแบบอ่านอย่างเดียวสามารถใช้สำหรับค่าคงที่แบบรันไทม์, ...

ดังนั้นฉันจึงพบว่าคำตอบที่น่าเชื่อถือเพียงข้อเดียวคือเจฟฟ์เยตส์


+1 สำหรับคำชนิดและชี้แจงเกี่ยวกับ C # spec ใน const และคงอ่านได้อย่างเดียว
Jeff Yates

17
อ่านอีกครั้งนี้ฉันไม่เห็นด้วยconst stringและstatic readonly stringทำสิ่งเดียวกัน ค่า Const ถูกแทนที่ในโค้ดที่เชื่อมโยงในขณะที่ค่าคงที่อ่านอย่างเดียวจะถูกอ้างอิง หากคุณมีconstไลบรารีใน A ที่ใช้โดยไลบรารี B ไลบรารี B จะแทนที่การอ้างอิงทั้งหมดไปยังconstตัวแปรนั้นด้วยค่าตัวอักษร หากตัวแปรนั้นถูกstatic readonlyแทนที่มันจะถูกอ้างอิงและค่าของมันจะถูกกำหนดที่รันไทม์
เจฟฟ์เยตส์

3
ประเด็นของเจฟฟ์มีความสำคัญเมื่ออ้างอิงถึงห้องสมุด หากคุณทำการคอมไพล์ใหม่ A และทำการแจกจ่ายซ้ำโดยไม่ทำการคอมไพล์ Bซ้ำB จะยังคงใช้ค่าเดิมอยู่
Mark Sowul

5
String.Empty read only instead of a constant?

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

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

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

ดูรหัสนี้เช่น:

public class OneName
{
    const string cConst = "constant string";
    static string cStatic = "static string";
    readonly string cReadOnly = "read only string";

    protected void Fun()
    {
        string cAddThemAll ;

        cAddThemAll = cConst;
        cAddThemAll = cStatic ;
        cAddThemAll = cReadOnly;    
    }
}

คอมไพเลอร์จะมาเป็น:

public class OneName
{
    // note that the const exist also here !
    private const string cConst = "constant string";
    private readonly string cReadOnly;
    private static string cStatic;

    static OneName()
    {
        cStatic = "static string";
    }

    public OneName()
    {
        this.cReadOnly = "read only string";
    }

    protected void Fun()
    {
        string cAddThemAll ;

        // look here, will replace the const string everywhere is finds it.
        cAddThemAll = "constant string";
        cAddThemAll = cStatic;
        // but the read only will only get it from "one place".
        cAddThemAll = this.cReadOnly;

    }
}

และการเรียกชุมนุม

        cAddThemAll = cConst;
0000003e  mov         eax,dword ptr ds:[09379C0Ch] 
00000044  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cStatic ;
00000047  mov         eax,dword ptr ds:[094E8C44h] 
0000004c  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cReadOnly;
0000004f  mov         eax,dword ptr [ebp-3Ch] 
00000052  mov         eax,dword ptr [eax+0000017Ch] 
00000058  mov         dword ptr [ebp-44h],eax 

แก้ไข: พิมพ์ผิด


ดังนั้นนี่หมายความว่าสตริง const ต้องถูกสร้างอินสแตนซ์กับคลาสที่มี const นั้นเสมอ? ดูเหมือนว่าจะดีกว่าการใช้แบบอ่านอย่างเดียวแล้ว
คนบ้าคลั่ง

@Theberserker well นั้นดีกว่า แต่คุณมีตัวเลือกทั้งหมดให้ใช้
Aristos

> จากนั้นคอมไพล์ dll จะไม่ทำงานเหมือนเดิมอีกต่อไปเพราะค่าใช้จ่ายทำโค้ดด้านในเพื่อเก็บสำเนาของสตริงในทุกการโทร @Aristos นั่นไม่ถูกต้อง เมื่อโค้ดถูกคอมไพล์มันจะเป็น "copy" ของสตริงที่จะถูกอ้างอิงในบล็อก TEXT ของ executable และโค้ดทั้งหมดจะอ้างอิงหน่วยความจำเดียวกันนั้น สิ่งที่คุณอ้างถึงในขั้นตอนที่สองเป็นเพียงขั้นตอนกลาง
Peter Dolkens

@ user1533523 ขอบคุณสำหรับการบันทึก - ฉันจะทำการทดสอบเมื่อฉันหาเวลาเพื่อตรวจสอบนี้
Aristos

คุณได้รับรหัสการประกอบได้อย่างไร C # ไม่ได้รวบรวมการชุมนุม!
jv110

0

คำตอบนี้มีอยู่เพื่อวัตถุประสงค์ทางประวัติศาสตร์

แต่เดิม:

เพราะStringเป็นคลาสจึงไม่สามารถเป็นค่าคงที่ได้

ขยายการสนทนา:

กล่องโต้ตอบที่มีประโยชน์มากมายถูกตอกย้ำในการตอบคำถามนี้และแทนที่จะลบออกเนื้อหานี้จะถูกทำซ้ำโดยตรง:

ใน. NET สตริง (ซึ่งแตกต่างจากใน Java) และสตริงเหมือนกันทุกประการ และใช่คุณสามารถมีค่าคงที่ตัวอักษรสตริงใน. NET - DrJokepu 3 กุมภาพันธ์ 09 เวลา 16:57

คุณกำลังบอกว่าคลาสไม่สามารถมีค่าคงที่ได้หรือไม่? - StingyJack 3 ก.พ. 09 เวลา 16:58

ใช่วัตถุต้องใช้แบบอ่านอย่างเดียว structs เท่านั้นที่สามารถทำค่าคงที่ ฉันคิดว่าเมื่อคุณใช้stringแทนStringคอมไพเลอร์เปลี่ยน const เป็นแบบอ่านอย่างเดียวสำหรับคุณ ทุกอย่างเกี่ยวกับการทำให้โปรแกรมเมอร์ C มีความสุข - Garry Shutler 3 ก.พ. 09 เวลา 16:59

tvanfosson เพิ่งอธิบายมัน verbose เพิ่มเติมอีกนิด "X ไม่สามารถเป็นค่าคงที่ได้เนื่องจาก Y ที่บรรจุอยู่ในชั้นเรียน" เป็นเพียงบริบทน้อยเกินไป;) #: 38211 Leonidas

string.Empty เป็นคุณสมบัติสแตติกที่ส่งคืนอินสแตนซ์ของคลาส String กล่าวคือสตริงว่างไม่ใช่คลาสสตริงเอง - tvanfosson 3 กุมภาพันธ์ 2552 เวลา 17:01 น

Empty เป็นอินสแตนซ์แบบอ่านอย่างเดียว (ไม่ใช่คุณสมบัติ) ของคลาส String - senfo 3 ก.พ. 09 เวลา 17:02

ปวดหัว ฉันยังคิดว่าฉันพูดถูก แต่ตอนนี้ฉันไม่ค่อยแน่ใจ งานวิจัยที่ต้องการคืนนี้! - Garry Shutler 3 ก.พ. 09 เวลา 17:07 น

สตริงว่างเป็นตัวอย่างของคลาสสตริง Empty เป็นฟิลด์สแตติก (ไม่ใช่คุณสมบัติฉันยืนแก้ไข) ในคลาส String โดยทั่วไปความแตกต่างระหว่างตัวชี้และสิ่งที่ชี้ไป ถ้ามันไม่ได้อ่านอย่างเดียวเราสามารถเปลี่ยนอินสแตนซ์ที่ฟิลด์ว่างอ้างถึงได้ - tvanfosson 3 ก.พ. 09 เวลา 17:07

แกร์รีคุณไม่จำเป็นต้องทำการวิจัยใด ๆ ลองคิดดู String เป็นคลาส Empty เป็นตัวอย่างของ String - senfo 3 ก.พ. 09 เวลา 17:12

มีบางอย่างที่ฉันไม่ค่อยได้รับ: ตัวสร้างสแตติกของคลาส String สามารถสร้างอินสแตนซ์ของคลาส String ได้อย่างไรบนโลก นั่นไม่ใช่สถานการณ์แบบ "ไก่หรือไข่" ใช่ไหม - DrJokepu 3 ก.พ. 09 เวลา 17:12 5

คำตอบนี้จะถูกต้องสำหรับเกือบทุกชั้นอื่น ๆ แต่ System.String .NET ทำปลอกประสิทธิภาพเป็นพิเศษสำหรับสตริงจำนวนมากและหนึ่งในนั้นคือคุณสามารถมีค่าคงที่สตริงได้เพียงลองใช้ ในกรณีนี้ Jeff Yates มีคำตอบที่ถูกต้อง - Joel Mueller 3 ก.พ. 09 เวลา 19:25

ดังที่อธิบายไว้ใน§7.18นิพจน์คงที่คือนิพจน์ที่สามารถประเมินได้ทั้งหมดในเวลาคอมไพล์ เนื่องจากวิธีเดียวในการสร้างค่าที่ไม่เป็น null ของการอ้างอิงชนิดอื่นที่ไม่ใช่สตริงคือการใช้โอเปอเรเตอร์ใหม่และเนื่องจากไม่อนุญาตให้โอเปอเรเตอร์ใหม่ในนิพจน์คงที่จึงมีความเป็นไปได้สำหรับค่าคงที่ประเภทอ้างอิง นอกเหนือจากสตริงเป็นโมฆะ ความคิดเห็นสองรายการก่อนหน้าถูกนำมาโดยตรงจากข้อกำหนดภาษา C # และย้ำถึงสิ่งที่ Joel Mueller พูดถึง - senfo 4 ก.พ. 09 เวลา 15:05 5


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

ไม่ใช่ฉันที่ลงคะแนนให้คุณ แต่ใน. NET สตริง (ซึ่งแตกต่างจากใน Java) และสตริงจะเหมือนกันทุกประการ และใช่คุณสามารถมีค่าคงที่ตัวอักษรสตริงใน. NET
Tamas Czinege

10
คำตอบนี้จะถูกต้องสำหรับเกือบทุกชั้นอื่น ๆ แต่ System.String .NET ทำปลอกประสิทธิภาพเป็นพิเศษสำหรับสตริงจำนวนมากและหนึ่งในนั้นคือคุณสามารถมีค่าคงที่สตริงได้เพียงลองใช้ ในกรณีนี้ Jeff Yates มีคำตอบที่ถูกต้อง
Joel Mueller

7
ฉันเกือบจะลบคำตอบนี้เมื่อมีคำตอบที่ดีกว่า แต่การอภิปรายในความคิดเห็นเหล่านี้ก็คุ้มค่า
Garry Shutler

1
@Garry คุณโชคดีที่ฉันอ่านความคิดเห็นล่าสุดของคุณมิฉะนั้นฉันก็จะลงคะแนน สตริงมีคุณสมบัติพิเศษใน. NET ซึ่งเป็นคลาสอ้างอิงซึ่งสามารถเป็น const ได้
Shimmy Weitzhandler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.