เหตุใด ReadOnlyObservableCollection.CollectionChanged จึงไม่เป็นสาธารณะ


88

เหตุใดจึงReadOnlyObservableCollection.CollectionChangedได้รับการคุ้มครองและไม่เปิดเผยต่อสาธารณะ (ตามที่เกี่ยวข้องObservableCollection.CollectionChanged)

การใช้คอลเลกชันจะใช้อะไรINotifyCollectionChangedหากฉันไม่สามารถเข้าถึงCollectionChangedเหตุการณ์ได้?


1
ด้วยความอยากรู้อยากเห็นทำไมคุณถึงคาดหวังว่าคอลเล็กชันแบบอ่านอย่างเดียวจะเปลี่ยนไป ? แน่นอนว่าจะไม่อ่านอย่างเดียว?
workmad3

84
คำถามตอบโต้: เหตุใดฉันจึงไม่คาดหวังให้ ObservableCollection เปลี่ยนแปลง การสังเกตมันใช้อะไรถ้าไม่มีอะไรจะเปลี่ยนแปลง? แน่นอนว่าคอลเลกชันจะเปลี่ยนไปมากที่สุด แต่ฉันมีผู้บริโภคที่ได้รับอนุญาตให้สังเกตเท่านั้น มอง แต่สัมผัสไม่ได้ ...
Oskar

1
ฉันเพิ่งเจอปัญหานี้เช่นกัน โดยทั่วไป ObservableCollection ไม่ได้ใช้เหตุการณ์ที่เปลี่ยนแปลง INotifyCollection อย่างถูกต้อง เหตุใด C # จึงอนุญาตให้คลาส จำกัด การเข้าถึงเหตุการณ์อินเทอร์เฟซ แต่ไม่ใช่วิธีการเชื่อมต่อ
djskinner

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

9
"อ่านอย่างเดียว" ไม่ได้หมายความว่า "ไม่เปลี่ยนรูป" อย่างที่บางคนคิด หมายความว่าโค้ดที่สามารถดูคอลเล็กชันผ่านคุณสมบัติดังกล่าวเท่านั้นไม่ได้รับอนุญาตให้เปลี่ยนแปลง แน่นอนสามารถเปลี่ยนแปลงได้เมื่อโค้ดเพิ่มหรือลบสมาชิกผ่านคอลเลกชันที่อยู่เบื้องหลัง ผู้อ่านควรจะยังคงได้รับแจ้งว่ามีการเปลี่ยนแปลงเกิดขึ้นแม้ว่าจะไม่สามารถเปลี่ยนคอลเล็กชันได้เองก็ตาม พวกเขาอาจยังต้องตอบสนองต่อการเปลี่ยนแปลงตัวเอง ฉันคิดว่าไม่มีเหตุผลที่ถูกต้องในการ จำกัด คุณสมบัติ CollectionChanged เหมือนที่เคยทำในกรณีนี้
Gil

คำตอบ:



16

ฉันพบวิธีการสำหรับคุณแล้ว:

ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);

คุณต้องอ้างถึงคอลเล็กชันของคุณอย่างชัดเจนโดยอินเทอร์เฟซINotifyCollectionChanged


14
โปรดอย่าอ่านว่า ReadOnlyObservableCollection เป็นกระดาษห่อหุ้มรอบ ObservableCollection ซึ่งกำลังจะเปลี่ยนไป ผู้บริโภคของ ReadOnlyObservableCollection ได้รับอนุญาตให้สังเกตการเปลี่ยนแปลงเหล่านี้เท่านั้นไม่สามารถเปลี่ยนแปลงอะไรได้เอง
Oskar

คุณไม่ควรพึ่งพาข้อเท็จจริงนี้คอลเลกชันแบบอ่านอย่างเดียวจะไม่เปลี่ยนแปลงไม่ว่าในกรณีใด ๆ เนื่องจากเรียกว่า "อ่านอย่างเดียว" นี่คือความเข้าใจของฉัน
Restuta

4
+1 สำหรับโซลูชันการหล่อ แต่สำหรับการสังเกตคอลเลคชันแบบอ่านอย่างเดียวนั้นไม่สมเหตุสมผล: หากคุณมีสิทธิ์เข้าถึงฐานข้อมูลแบบอ่านอย่างเดียวคุณคาดว่าจะไม่มีการเปลี่ยนแปลงหรือไม่?
djskinner

17
ฉันไม่เห็นว่าความยากของที่นี่คืออะไร ReadOnlyObservableCollection เป็นคลาสที่ให้วิธีอ่านอย่างเดียวในการสังเกตคอลเล็กชัน หากชั้นเรียนของฉันมีคอลเล็กชันพูดเซสชันและฉันไม่ต้องการให้ผู้อื่นสามารถเพิ่มหรือลบเซสชันออกจากคอลเล็กชันของฉันได้ แต่ยังคงสังเกตเห็นพวกเขา ReadOnlyObservableCollection เป็นพาหนะที่สมบูรณ์แบบสำหรับสิ่งนั้น คอลเล็กชันนี้อ่านได้เฉพาะกับผู้ใช้ในชั้นเรียนของฉัน แต่ฉันมีสำเนาอ่าน / เขียนสำหรับใช้งานของฉันเอง
scwagner

1
เรียกดูรหัส. Net ของReadOnlyObservableCollectionคุณจะพบสิ่งนี้: event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged. การใช้งานอินเทอร์เฟซที่ชัดเจนของเหตุการณ์
Mike de Klerk

7

ฉันรู้ว่าโพสต์นี้เก่า แต่ผู้ใช้ควรใช้เวลาทำความเข้าใจกับรูปแบบที่ใช้ใน. NET ก่อนแสดงความคิดเห็น คอลเลคชันแบบอ่านอย่างเดียวคือกระดาษห่อหุ้มในคอลเล็กชันที่มีอยู่ซึ่งป้องกันไม่ให้ผู้บริโภคแก้ไขโดยตรงดูReadOnlyCollectionและคุณจะเห็นว่าเป็นกระดาษห่อหุ้มIList<T>ซึ่งอาจมีการเปลี่ยนแปลงหรือไม่ก็ได้ คอลเล็กชันที่ไม่เปลี่ยนรูปเป็นเรื่องที่แตกต่างออกไปและอยู่ภายใต้ไลบรารีคอลเล็กชันใหม่ที่ไม่เปลี่ยนรูป

อีกอย่างอ่านอย่างเดียวไม่เหมือนไม่เปลี่ยนรูป !!!!

ว่ากันโดยปริยายควรใช้ReadOnlyObservableCollectionINotifyCollectionChanged


5

มีแน่นอนเหตุผลที่ดีสำหรับความต้องการที่จะสมัครเป็นสมาชิกกับคอลเลกชันเปลี่ยนแปลงการแจ้งเตือนบนReadOnlyObservableCollection ดังนั้นเพื่อเป็นทางเลือกในการแคสต์คอลเลกชันของคุณเป็นINotifyCollectionChangedหากคุณบังเอิญคลาสย่อยReadOnlyObservableCollectionวิธีต่อไปนี้จะเป็นวิธีที่สะดวกกว่าในการเข้าถึงเหตุการณ์CollectionChanged :

    public class ReadOnlyObservableCollectionWithCollectionChangeNotifications<T> : ReadOnlyObservableCollection<T>
{
    public ReadOnlyObservableCollectionWithCollectionChangeNotifications(ObservableCollection<T> list)
        : base(list)
    {
    }

    event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged2
    {
        add { CollectionChanged += value; }
        remove { CollectionChanged -= value; }
    }
}

สิ่งนี้ได้ผลดีสำหรับฉันมาก่อน


5

คุณอาจลงคะแนนสำหรับรายการจุดบกพร่องบน Microsoft Connect ที่อธิบายปัญหานี้: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

อัปเดต:

พอร์ทัล Connect ถูกปิดโดย Microsoft ดังนั้นลิงก์ด้านบนใช้ไม่ได้อีกต่อไป

ไลบรารี My Win Application Framework (WAF) มีโซลูชัน: คลาสReadOnlyObservableList :

public class ReadOnlyObservableList<T> 
        : ReadOnlyObservableCollection<T>, IReadOnlyObservableList<T>
{
    public ReadOnlyObservableList(ObservableCollection<T> list)
        : base(list)
    {
    }

    public new event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add { base.CollectionChanged += value; }
        remove { base.CollectionChanged -= value; }
    }

    public new event PropertyChangedEventHandler PropertyChanged
    {
        add { base.PropertyChanged += value; }
        remove { base.PropertyChanged -= value; }
    }
}

Connect ถูกยกเลิก ฉันจะโหวตทั้งหมด
findusl

1

ตามที่ตอบไปแล้วคุณมีสองทางเลือก: คุณสามารถส่งReadOnlyObservableCollection<T>ไปยังอินเทอร์เฟซINotifyCollectionChangedเพื่อเข้าถึงCollectionChangedเหตุการณ์ที่นำไปใช้อย่างชัดเจนหรือคุณสามารถสร้างคลาส wrapper ของคุณเองที่ทำเช่นนั้นครั้งเดียวในตัวสร้างและเชื่อมโยงเหตุการณ์ของการรวมเข้าด้วยReadOnlyObservableCollection<T>กัน

ข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับสาเหตุที่ปัญหานี้ยังไม่ได้รับการแก้ไข:

ในขณะที่คุณสามารถดูจากรหัสที่มา , ReadOnlyObservableCollection<T>เป็นสาธารณะไม่ปิดผนึก (เช่นที่สืบทอด) protected virtualระดับที่เหตุการณ์ที่เกิดขึ้นมีการทำเครื่องหมาย

นั่นคืออาจมีโปรแกรมคอมไพล์ที่มีคลาสที่ได้มาReadOnlyObservableCollection<T>โดยมีนิยามเหตุการณ์ที่ถูกแทนที่ แต่protectedสามารถมองเห็นได้ โปรแกรมเหล่านั้นจะมีรหัสที่ไม่ถูกต้องเมื่อการเปิดเผยของเหตุการณ์ถูกเปลี่ยนเป็นpublicในคลาสพื้นฐานเนื่องจากไม่ได้รับอนุญาตให้ จำกัด การมองเห็นของเหตุการณ์ในคลาสที่ได้รับมา

น่าเสียดายที่การสร้างprotected virtualเหตุการณ์ในpublicภายหลังเป็นการเปลี่ยนแปลงการทำลายไบนารีและด้วยเหตุนี้จึงไม่สามารถทำได้หากไม่มีเหตุผลที่ดีมากซึ่งฉันกลัวว่า "ฉันต้องโยนวัตถุหนึ่งครั้งเพื่อแนบตัวจัดการ" ไม่ใช่

ที่มา: ความคิดเห็น GitHub โดย Nick Guererra, 19 สิงหาคม 2015


0

นี่เป็นยอดนิยมใน Google ดังนั้นฉันจึงคิดว่าฉันจะเพิ่มโซลูชันของฉันในกรณีที่คนอื่นค้นหาสิ่งนี้

จากข้อมูลด้านบน (เกี่ยวกับความจำเป็นในการส่งไปยังINotifyCollectionChanged ) ฉันได้สร้างสองวิธีการขยายเพื่อลงทะเบียนและยกเลิกการลงทะเบียน

โซลูชันของฉัน - วิธีการขยาย

public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged += handler;
}

public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged -= handler;
}

ตัวอย่าง

IThing.cs

public interface IThing
{
    string Name { get; }
    ReadOnlyObservableCollection<int> Values { get; }
}

การใช้วิธีการขยาย

public void AddThing(IThing thing)
{
    //...
    thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged);
}

public void RemoveThing(IThing thing)
{
    //...
    thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged);
}

โซลูชันของ OP

public void AddThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged -= this.HandleThingCollectionChanged;
}

ทางเลือกที่ 2

public void AddThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged -= this.HandleThingCollectionChanged;
}

0

วิธีการแก้

ReadOnlyObservableCollection.CollectionChanged ไม่ถูกเปิดเผย (ด้วยเหตุผลที่ถูกต้องที่ระบุไว้ในคำตอบอื่น ๆ ) ดังนั้นเรามาสร้างคลาส Wrapper ของเราเองที่เปิดเผย:

/// <summary>A wrapped <see cref="ReadOnlyObservableCollection{T}"/> that exposes the internal <see cref="CollectionChanged"/>"/>.</summary>
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
    public new NotifyCollectionChangedEventHandler CollectionChanged;

    public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { /* nada */ }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) => 
        CollectionChanged?.Invoke(this, args);
}

คำอธิบาย

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

นี่คือสถานการณ์ดังกล่าว:

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

หมายเหตุว่าเพื่อที่จะตัดคอลเลกชันภายในที่มีReadOnlyObservableCollectionคอลเลกชันภายในถูกบังคับให้ต้องมาจากโดยคอนสตรัคของObservableCollectionReadOnlyObservableCollection

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

ObservableReadOnlyCollectionหนังReadOnlyObservableCollection.CollectionChangedที่มีเป็นของตัวเองและก็ผ่านไปกับเหตุการณ์ที่คอลเลกชันที่มีการเปลี่ยนแปลงใด ๆ ทุกจัดการเหตุการณ์ที่แนบมา

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