คุณสมบัติอัตโนมัติกับ getter เท่านั้นตั้งได้เพราะอะไร?


97

ฉันสร้างคุณสมบัติอัตโนมัติ:

public int Foo { get; } 

นี่คือ getter เท่านั้น แต่เมื่อฉันสร้างตัวสร้างฉันสามารถเปลี่ยนค่า:

public MyClass(string name)
{
    Foo = 5;
}

ทำไมถึงเป็นไปได้ทั้งๆที่เป็นแบบ get-only?


มันไม่ได้ใช้ setter จริงๆ (เพราะมันไม่มี) มันตั้งค่าฟิลด์พื้นฐานโดยตรง (ซึ่งซ่อนจากเราซึ่งเป็นสาเหตุที่คุณต้องใช้ชื่อคุณสมบัติ)
Dennis_E

14
คุณสมบัติคือการใช้งานอะไรหากไม่สามารถเริ่มต้น / ตั้งค่าได้ Yacoub Massad ตอบได้อย่างสมบูรณ์แบบ
Vikhram

คำตอบ:


125

นี่คือคุณลักษณะ C # 6 ใหม่ "คุณสมบัติอัตโนมัติสำหรับ Getter เท่านั้น" หรือที่เรียกว่า "Auto-Property Initializers for Read-Only Properties" ตามที่กล่าวไว้ในบทความในนิตยสาร MSDN 'C #: The New and Improved C # 6.0' โดย Mark MichaelisและในC # 6.0 ร่างภาษาข้อมูลจำเพาะ

ตัวตั้งค่าของฟิลด์แบบอ่านอย่างเดียวสามารถเข้าถึงได้ในตัวสร้างเท่านั้นในสถานการณ์อื่น ๆ ทั้งหมดฟิลด์ยังคงอ่านอย่างเดียวและทำงานเหมือนเดิม

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

คุณลักษณะนี้ถูกมองว่ามีความสำคัญเนื่องจากมีการนำคุณสมบัติที่ใช้งานอัตโนมัติมาใช้ใน C # 3 คุณสมบัติที่ไม่แน่นอน (ผู้ที่มีตัวรับและตัวตั้งค่า) ได้กลายเป็นสิ่งที่เขียนได้เร็วกว่าคุณสมบัติที่ไม่เปลี่ยนรูป ถูกล่อลวงให้ใช้คุณสมบัติที่เปลี่ยนแปลงได้เพื่อหลีกเลี่ยงการพิมพ์รหัสสำหรับเขตข้อมูลสำรองที่มักจะต้องใช้สำหรับคุณสมบัติอ่านอย่างเดียว มีการอภิปรายมากขึ้นของคุณสมบัติ Auto-ดำเนินการในเป็นส่วนที่เกี่ยวข้องของไมโครซอฟท์เขียนโปรแกรม C # คู่มือ

บล็อกโพสต์นี้ '# 1,207 - C # 6.0 - ตัวเริ่มต้นคุณสมบัติอัตโนมัติสำหรับคุณสมบัติอ่านอย่างเดียว' โดย Sean Sextonมีคำอธิบายและตัวอย่างที่ดีดังต่อไปนี้:

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

public class Dog 
{
    public string Name { get; set; }

    // DogCreationTime is immutable
    private readonly DateTime creTime;
    public DateTime DogCreationTime 
    {
        get { return creTime; }
    }

    public Dog(string name)
    {
        Name = name;
        creTime = DateTime.Now;
    }
}

ใน C # 6.0 คุณสามารถใช้คุณสมบัติที่ติดตั้งอัตโนมัติเพื่อใช้คุณสมบัติแบบอ่านอย่างเดียว คุณทำได้โดยใช้ตัวเริ่มต้นคุณสมบัติอัตโนมัติ ผลลัพธ์ที่ได้จะสะอาดกว่าตัวอย่างข้างต้นมากซึ่งเราต้องประกาศเขตข้อมูลสำรองอย่างชัดเจน

public class Dog
{
    public string Name { get; set; }

    // DogCreationTime is immutable
    public DateTime DogCreationTime { get; } = DateTime.Now;

    public Dog(string name)
    {
        Name = name;
    }
}

รายละเอียดเพิ่มเติมสามารถพบได้ในrepo dotnet Roslyn บน GitHub :

คุณสมบัติอัตโนมัติสามารถประกาศได้โดยไม่ต้องใช้ตัวตั้งค่า

ฟิลด์สำรองของคุณสมบัติอัตโนมัติ getter-only ถูกประกาศโดยปริยายว่าเป็นแบบอ่านอย่างเดียว (แม้ว่าจะมีความสำคัญเพื่อจุดประสงค์ในการสะท้อนเท่านั้น) สามารถเริ่มต้นได้โดยใช้ initializer บนคุณสมบัติดังตัวอย่างด้านบน นอกจากนี้คุณสมบัติ getter-only สามารถถูกกำหนดให้ในเนื้อหาตัวสร้างของชนิดการประกาศซึ่งทำให้ค่าถูกกำหนดโดยตรงไปยังฟิลด์ที่อยู่ภายใต้:

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

และในข้อกำหนดภาษาแบบร่าง C # 6.0 (หมายเหตุ: ข้อกำหนดของภาษาถือเป็นที่สิ้นสุดเท่าที่ Microsoft เกี่ยวข้อง แต่ยังไม่ได้รับการอนุมัติให้เป็นมาตรฐาน EMCA / ISOดังนั้นจึงเป็น 'ร่าง'):

ใช้คุณสมบัติโดยอัตโนมัติ

คุณสมบัติที่นำไปใช้โดยอัตโนมัติ (หรือคุณสมบัติอัตโนมัติสำหรับระยะสั้น) เป็นคุณสมบัติที่ไม่ใช่นามธรรมที่ไม่ใช่ภายนอกที่มีเนื้อหาของตัวเข้าถึงอัฒภาคเท่านั้น คุณสมบัติอัตโนมัติต้องมี get accessor และสามารถเลือก set accessor ได้

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

คุณสมบัติอัตโนมัติอาจมีตัวเลือก property_initializer ซึ่งถูกนำไปใช้โดยตรงกับฟิลด์การสำรองข้อมูลเป็น variable_initializer (Variable initializers)


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

ความแปลกสำหรับฉันคือยังคงมีข้อผิดพลาดของคอมไพเลอร์ที่ระบุว่าCS0200 C# Property or indexer cannot be assigned to -- it is read onlyเมื่อใช้คุณสมบัติปกติ "คุณสมบัติอัตโนมัติสำหรับ Getter เท่านั้น" ถือเป็นแบบอ่านอย่างเดียวหรือไม่
Kyle Delaney

25

นี่เป็นคุณสมบัติใหม่ใน C # 6ที่ให้คุณสร้างคุณสมบัติแบบอ่านอย่างเดียวและเริ่มต้นค่าจากตัวสร้าง (หรืออินไลน์เมื่อคุณประกาศ)

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

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


แล้วมีคำอธิบายอย่างไร? คำจำกัดความของ getter คืออะไร?
Noam B.

16

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

ในการกำหนดคุณสมบัติ getter-only ที่แท้จริง (ซึ่งไม่สามารถเตรียมใช้งานได้จากตัวสร้าง) คุณต้องระบุสิ่งที่ส่งคืนเป็นส่วนหนึ่งของนิยาม:

public int Foo { get { return 5; } }

หรือกระชับมากขึ้นใน C # 6:

public int Foo => 5;

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

3
@sebagomez: ฉันไม่แน่ใจว่าฉันเข้าใจประเด็นของคุณ - นั่นเป็นสิ่งที่ฉันแสดงให้เห็นในตัวอย่างไม่ใช่หรือ
Douglas

5

"คุณสมบัติที่ใช้งานโดยอัตโนมัติแบบอ่านอย่างเดียว"

ก่อนอื่นอยากชี้แจงว่าคุณสมบัติเช่น

public string FirstName { get; }

เรียกว่า "คุณสมบัติที่ใช้งานโดยอัตโนมัติแบบอ่านอย่างเดียว"

ในการตรวจสอบสิ่งนี้คุณสามารถเรียกใช้และตรวจสอบโค้ดด้านบนด้วย Visual Studio หากคุณเปลี่ยนเวอร์ชันภาษาจาก C # 6.0 เป็น C # 5.0 คอมไพเลอร์จะโยนข้อยกเว้นต่อไปนี้ คุณสมบัติ 'คุณสมบัติที่ใช้งานโดยอัตโนมัติแบบอ่านอย่างเดียว' ไม่พร้อมใช้งานใน C # 5 โปรดใช้ภาษาเวอร์ชัน 6 ขึ้นไป

หากต้องการเปลี่ยนเวอร์ชันภาษา C # โปรดไปที่นี่

ตอนนี้ฉันมาถึงคำถามที่สองของคุณ

“ นี่เป็นเพียงผู้เริ่มต้นเท่านั้น แต่เมื่อฉันสร้างตัวสร้างฉันสามารถเปลี่ยนค่าได้”

Microsoft แนะนำ "คุณสมบัติที่ใช้งานโดยอัตโนมัติแบบอ่านอย่างเดียว" บนตรรกะของการอ่านอย่างเดียว อย่างที่เราทราบกันดีว่าคำหลัก“ อ่านอย่างเดียว” มีให้ตั้งแต่ C # 1.0 เราใช้“อ่านได้อย่างเดียว” เป็นคำหลักปรับปรุงบนสนามและข้อมูลที่สามารถกำหนดใน2 วิธีอย่างใดอย่างหนึ่งในช่วงเวลาของการประกาศหรือในผู้สร้างในระดับเดียวกัน

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

Way1 (ในขณะที่ประกาศ):

public string FirstName { get; } = "Banketeshvar";

Way2 (ในตัวสร้างในคลาสเดียวกัน)

Person()
{
 FirstName  = "Banketeshvar";
}

คุณสมบัติอ่านอย่างเดียวล้วนๆ

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

public string FullName => "Manish Sharma";

ตอนนี้คุณไม่สามารถกำหนดค่าที่เหมาะสมของ“ FullName” จากตัวสร้างได้ หากคุณพยายามทำเช่นนั้นจะทำให้เกิดข้อยกเว้นต่อไปนี้

"คุณสมบัติหรือผู้จัดทำดัชนี" Person.FullName "ไม่สามารถกำหนดให้ - เป็นแบบอ่านอย่างเดียว"


2
โปรดทราบว่า FullName => "foo bar" จะได้รับการประเมินทุกครั้ง
juFo

4

คุณลักษณะคุณสมบัติอัตโนมัติถูกเพิ่มลงในภาษาในระหว่างการเผยแพร่ C # 3.0 ช่วยให้คุณกำหนดคุณสมบัติได้โดยไม่ต้องมีฟิลด์สำรอง แต่คุณยังต้องใช้ตัวสร้างเพื่อเริ่มต้นคุณสมบัติอัตโนมัติเหล่านี้เป็นค่าที่ไม่ใช่ค่าเริ่มต้น C # 6.0 แนะนำคุณสมบัติใหม่ที่เรียกว่า auto property initializer ซึ่งช่วยให้คุณสามารถเริ่มต้นคุณสมบัติเหล่านี้ได้โดยไม่ต้องมีตัวสร้างเช่นด้านล่าง:

ก่อนหน้านี้ต้องใช้ตัวสร้างหากคุณต้องการสร้างวัตถุโดยใช้คุณสมบัติอัตโนมัติและเริ่มต้นคุณสมบัติอัตโนมัติเป็นค่าที่ไม่ใช่ค่าเริ่มต้นดังต่อไปนี้:

public class MyClass
{
    public int Foo { get; }

    public Foo(int foo)
    {
        Foo = foo;
    }
}

ตอนนี้ใน C # 6.0 ความสามารถในการใช้ initializer กับคุณสมบัติอัตโนมัติหมายความว่าไม่จำเป็นต้องมีรหัสตัวสร้างที่ชัดเจน

public string Foo { get; } = "SomeString";

public List<string> Genres { get; } = new List<string> { "Comedy", "Drama" };

คุณสามารถดูข้อมูลเพิ่มเติมได้ที่นี่


1

ตัวแปรที่ประกาศreadonlyสามารถเขียนได้ภายในคอนสตรัคเตอร์ แต่ในภาษาที่ใช้กับแอ็ตทริบิวต์จะไม่สามารถแก้ไขได้หลังจากคอนสตรัคเตอร์ส่งคืน ตัวกำหนดคุณสมบัตินั้นถูกจัดให้เป็นคุณลักษณะของภาษาเนื่องจากมักจำเป็นสำหรับฟิลด์ที่ค่าจะแตกต่างกันไปตามพารามิเตอร์ตัวสร้าง (หมายความว่าไม่สามารถเริ่มต้นได้ก่อนที่ตัวสร้างจะเริ่มทำงาน) แต่จะไม่ต้องเปลี่ยนหลังจากตัวสร้างกลับมา แต่มันก็เป็น ใช้ได้เฉพาะกับตัวแปรที่แสดงเป็นฟิลด์ ความหมายของreadonlyเขตข้อมูลที่มีคุณสมบัติเหมาะสมในหลาย ๆ กรณีจะสมบูรณ์แบบสำหรับสมาชิกสาธารณะยกเว้นว่าชั้นเรียนมักจะเปิดเผยสมาชิก - แม้กระทั่งสิ่งที่ไม่เปลี่ยนรูป - เป็นคุณสมบัติมากกว่าเขตข้อมูล

เช่นเดียวกับคุณสมบัติอัตโนมัติแบบอ่าน - เขียนที่มีอยู่เพื่อให้คลาสแสดงคุณสมบัติที่เปลี่ยนแปลงได้อย่างง่ายดายเช่นเดียวกับฟิลด์ทั่วไปคุณสมบัติอัตโนมัติแบบอ่านอย่างเดียวก็มีอยู่เพื่อให้คลาสแสดงคุณสมบัติที่ไม่เปลี่ยนรูปได้อย่างง่ายดายเหมือนกับreadonlyฟิลด์ -qualified เช่นเดียวกับreadonly-qualified fields สามารถเขียนใน constructor ได้เช่นกันด้วยคุณสมบัติ get-only

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