ฉันจะสร้างคลาสที่ไม่เปลี่ยนรูปได้อย่างไร


113

ฉันกำลังสร้างคลาสที่ไม่เปลี่ยนรูป
ฉันได้ทำเครื่องหมายคุณสมบัติทั้งหมดเป็นแบบอ่านอย่างเดียว

ฉันมีรายการสิ่งของในชั้นเรียน
แม้ว่าคุณสมบัติเป็นแบบอ่านอย่างเดียวรายการก็สามารถแก้ไขได้

การเปิดเผยรายการ IE ที่ไม่สามารถเปลี่ยนแปลงได้ทำให้ไม่เปลี่ยนรูป
ฉันอยากรู้ว่ากฎพื้นฐานที่ต้องปฏิบัติตามเพื่อให้คลาสไม่เปลี่ยนรูปคืออะไร?


2
ผมขอแนะนำให้คุณอ่านชุดบล็อกเอริค Lippert ฯ เมื่อวันที่เปลี่ยนไม่ได้โดยเฉพาะอย่างยิ่งรายการใน"ชนิดไม่เปลี่ยนรูป" ความคิดเห็นของคุณที่ว่า "การเปิดเผยรายการของ IE ทำให้ไม่สามารถเปลี่ยนแปลงได้" ดูเหมือนจะค่อนข้างแปลกสำหรับฉัน คุณหมายถึงอะไร?
Jon Skeet

สิ่งที่ฉันหมายถึงคือแทนที่จะอนุญาตให้เข้าถึงรายการ (หากวัตถุมีรายการของวัตถุอื่น ๆ ) ทำให้ผู้ใช้สามารถเข้าถึงสมาชิกด้วย IEnumerable รายการพูดคุยที่นี่เป็นตัวอย่างเฉพาะ แต่อาจเป็นโครงสร้างข้อมูลใดก็ได้
Biswanath

1
ลิงก์ของ Jon Skeet ไปยังบล็อกซีรีส์ของ Eric Lippert เสียเมื่อ MSDN เก็บบล็อกเหล่านี้ โปรดดู"ชนิดไม่เปลี่ยนรูป" ส่วนที่เหลือของซีรีส์ดูเหมือนว่าจะอยู่ในแผงควบคุมด้านซ้ายของเดือนธันวาคม 2550 ถึงมกราคม 2551
John Doe

โปรดดูชุดบล็อกเอริค Lippert เกี่ยวกับวิธีการที่คนทั่วไปเกิดความสับสนข้อตกลงatomicity, volatilityและimmutability: Part One , ส่วนที่สองและส่วนที่สาม สิ่งเหล่านี้มาจากบล็อกส่วนตัวของเขาและฉันเชื่อว่าเป็นมิตรกับมือใหม่มากกว่าโพสต์ MSDN ของเขา
John Doe

คำตอบ:


121

ฉันคิดว่าคุณมาถูกทางแล้ว -

  • ข้อมูลทั้งหมดที่ฉีดเข้าไปในชั้นเรียนควรได้รับการจัดเตรียมไว้ในตัวสร้าง
  • คุณสมบัติทั้งหมดควรเป็น getters เท่านั้น
  • หากคอลเลกชัน (หรืออาร์เรย์) ถูกส่งผ่านไปยังตัวสร้างควรคัดลอกเพื่อป้องกันไม่ให้ผู้เรียกแก้ไขในภายหลัง
  • หากคุณจะส่งคืนคอลเลคชันของคุณให้ส่งคืนสำเนาหรือเวอร์ชันอ่านอย่างเดียว (ตัวอย่างเช่นใช้ArrayList.ReadOnlyหรือที่คล้ายกัน - คุณสามารถรวมสิ่งนี้กับจุดก่อนหน้าและจัดเก็บสำเนาแบบอ่านอย่างเดียวที่จะส่งคืนเมื่อ ผู้เรียกเข้าถึง) ส่งคืนตัวแจงนับหรือใช้วิธีการ / คุณสมบัติอื่นที่อนุญาตให้เข้าถึงคอลเลกชันแบบอ่านอย่างเดียว
  • โปรดทราบว่าคุณยังคงมีรูปลักษณ์ของคลาสที่เปลี่ยนแปลงไม่ได้หากสมาชิกคนใดคนหนึ่งของคุณไม่แน่นอน - หากเป็นกรณีนี้คุณควรคัดลอกสถานะใด ๆ ที่คุณต้องการเก็บรักษาไว้และหลีกเลี่ยงการส่งคืนอ็อบเจ็กต์ที่เปลี่ยนแปลงไม่ได้ทั้งหมดเว้นแต่คุณจะคัดลอก ก่อนที่จะคืนให้กับผู้โทร - อีกทางเลือกหนึ่งคือส่งคืนเฉพาะ "ส่วน" ที่ไม่เปลี่ยนรูปของวัตถุที่เปลี่ยนแปลงไม่ได้ - ขอบคุณ @Brian Rasmussen ที่สนับสนุนให้ฉันขยายประเด็นนี้

ฉันควรเขียน wrapper look up property ซึ่งช่วยให้คุณค้นหาได้หรือไม่ และขอบคุณสำหรับคำตอบ
Biswanath

5
ประเภทการอ้างอิงที่ไม่แน่นอนใด ๆ ที่ส่งผ่านเป็นอาร์กิวเมนต์ไปยังตัวสร้างควรถูกคัดลอก มิฉะนั้นผู้โทรจะยังคงอ้างอิงถึงสถานะ
Brian Rasmussen

@Brian Rasmussen คุณพูดถูก แต่แม้ว่าจะถูกคัดลอก แต่อาจเป็นไปได้ที่ผู้โทรทุกคนจะเข้าถึงวัตถุที่เปลี่ยนแปลงได้ขึ้นอยู่กับ acces.sors ที่ให้มา ในกรณีที่มีการส่งผ่านวัตถุที่เปลี่ยนแปลงได้คลาสจะดีที่สุดคือส่งคืนสำเนาอื่นหรือส่วนที่ไม่เปลี่ยนรูปของวัตถุเสมอ
แบลร์คอนราด

@Blair - ถ้าฉันมีพจนานุกรมในชั้นเรียนซึ่งฉันแค่ต้องการใช้มันเพื่อค้นหา คุณสมบัติแบบอ่านอย่างเดียวซึ่งการค้นหาควรใช้ได้หรือไม่?
Biswanath

@Biswanath - ใช่โดยมีข้อแม้ว่าหากค่าในพจนานุกรมไม่แน่นอนคุณจะต้องปกป้องค่าเหล่านี้ก่อนที่จะกลับมาหากคุณไม่ต้องการให้พวกมันกลายพันธุ์
Blair Conrad

18

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

คุณสามารถสร้างรายการคุณสมบัติแบบอ่านอย่างเดียวได้ดังนี้:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}

1
ฉันคิดว่า ImmutableList น่าจะดีกว่า
Kevin Wong

ใช้ ImmutableList พิจารณาปิดผนึกคลาสด้วย
kofifus

ฉันไม่เชื่อว่า ImmutableList basic rules to make a class (that contains a list) immutableเป็นแบบที่ดีสำหรับกรณีการใช้งานนี้: ความหมายของ ImmutableList ช่วยให้สามารถใช้หน่วยความจำได้อย่างมีประสิทธิภาพมากขึ้นเมื่อเพิ่มรายการลงในสำเนาของรายการ แต่ดูเหมือนว่าสำหรับฉันจะเป็นกรณีการใช้งานที่เฉพาะเจาะจงมากขึ้น
โจ

11

นอกจากนี้โปรดทราบว่า:

public readonly object[] MyObjects;

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


5

ใช้ReadOnlyCollectionชั้นเรียน ตั้งอยู่ในSystem.Collections.ObjectModelเนมสเปซ

ในสิ่งที่ส่งคืนรายการของคุณ (หรือในตัวสร้าง) ให้ตั้งค่ารายการเป็นคอลเล็กชันแบบอ่านอย่างเดียว

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}

1

อีกทางเลือกหนึ่งคือใช้รูปแบบผู้เยี่ยมชมแทนที่จะเปิดเผยคอลเล็กชันภายในใด ๆ เลย


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