Runtime: Silakan tambahkan antarmuka IReadOnlySet dan buat HashSet, SortedSet mengimplementasikannya

Dibuat pada 9 Jun 2015  ·  104Komentar  ·  Sumber: dotnet/runtime

Asli

Sejak IReadOnlyList ditambahkan, paritas antara set dan daftar telah menurun. Akan sangat bagus untuk membangunnya kembali.

Menggunakannya akan menjadi cara implementasi-agnostik untuk mengatakan: "ini adalah koleksi baca-saja di mana item-itemnya unik".

Jelas dibutuhkan oleh banyak orang:

SQL Server: https://msdn.microsoft.com/en-us/library/gg503096.aspx
Roslyn: https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/InternalUtilities/IReadOnlySet.cs
Beberapa Orang Dalam Komentar: http://blogs.msdn.com/b/bclteam/archive/2013/03/06/update-to-immutable-collections.aspx

Saya menemukan diskusi ini ketika mengerjakan masalah dunia nyata di mana saya ingin menggunakan kumpulan kunci kamus untuk operasi set hanya baca. Untuk mendukung kasus itu, inilah API yang saya usulkan.

Sunting

Alasan

API yang Diusulkan

 namespace System.Collections.Generic {
+    public interface IReadOnlySet<out T> : IReadOnlyCollection<T>, IEnumerable, IEnumerable<T> {
+        bool Contains(T value);
+        bool IsProperSubsetOf(IEnumerable<T> other);
+        bool IsProperSupersetOf(IEnumerable<T> other);
+        bool IsSubsetOf(IEnumerable<T> other);
+        bool IsSupersetOf(IEnumerable<T> other);
+        bool Overlaps(IEnumerable<T> other);
+        bool SetEquals(IEnumerable<T> other);
+    }
-    public class HashSet<T> : ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T> {
+    public class HashSet<T> : ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T>, IReadOnlySet<T> {
     }
-    public class SortedSet<T> : ICollection, ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T> {
+    public class SortedSet<T> : ICollection, ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T>, IReadOnlySet<T> {
     }
+    public class ReadOnlySet<T> : ICollection<T>, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlySet<T>, ISet<T> {
+        public int Count { get; }
+        public ReadOnlySet(ISet<T> set);
+        public bool Contains(T value);
+        public bool IsProperSubsetOf(IEnumerable<T> other);
+        public bool IsProperSupersetOf(IEnumerable<T> other);
+        public bool IsSubsetOf(IEnumerable<T> other);
+        public bool IsSupersetOf(IEnumerable<T> other);
+        public bool Overlaps(IEnumerable<T> other);
+        public bool SetEquals(IEnumerable<T> other);
+    }
     public class Dictionary<TKey, TValue> {
-        public sealed class KeyCollection : ICollection, ICollection<TKey>, IEnumerable, IEnumerable<TKey>, IReadOnlyCollection<TKey> {
+        public sealed class KeyCollection : ICollection, ICollection<TKey>, IEnumerable, IEnumerable<TKey>, IReadOnlyCollection<TKey>, IReadOnlySet<TKey> {
+            public bool IsProperSubsetOf(IEnumerable<TKey> other);
+            public bool IsProperSupersetOf(IEnumerable<TKey> other);
+            public bool IsSubsetOf(IEnumerable<TKey> other);
+            public bool IsSupersetOf(IEnumerable<TKey> other);
+            public bool Overlaps(IEnumerable<TKey> other);
+            public bool SetEquals(IEnumerable<TKey> other);
         }
     }
 }

Pertanyaan-pertanyaan terbuka

  • Apakah menambahkan metode ini ke Dictionary<TKey, TValue>.KeyCollection dapat diterima mengingat biaya ukuran kode untuk tipe generik yang umum dipakai? Lihat di sini .
  • Haruskah IReadOnlySet<T> diimplementasikan di Dictionary KeyCollection lain seperti SortedDictionary atau ImmutableDictionary ?

Pembaruan

  • Dihapus TryGetValue karena tidak ada di ISet<T> dan dengan demikian akan mencegah kemungkinan rebasing ISet<T> untuk mengimplementasikan IReadOnlySet<T> .
  • Menambahkan kelas ReadOnlySet<T> yang mirip dengan ReadOnlyCollection<T> .
api-approved area-System.Collections up-for-grabs

Komentar yang paling membantu

Desain API yang diusulkan:

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
    bool IsSubsetOf(IEnumerable<T> other);
    bool IsSupersetOf(IEnumerable<T> other);
    bool IsProperSupersetOf(IEnumerable<T> other);
    bool IsProperSubsetOf(IEnumerable<T> other);
    bool Overlaps(IEnumerable<T> other);
    bool SetEquals(IEnumerable<T> other);
}

Ini didasarkan pada ISet<> API (kecuali metode mutasi jelas).

Sayang sekali Comparer tidak cocok dengan ini, tetapi kemudian ISet<> maupun IReadOnlyDictionary<> mengekspos pembanding, jadi sudah terlambat untuk memperbaikinya sekarang.

Semua 104 komentar

Bukankah lebih baik jika kita hanya memiliki beberapa konstruksi bahasa untuk membuat segala sesuatunya tidak berubah? Maka kita tidak perlu memiliki antarmuka ajaib ini.

@whoisj Bahasa apa? CLR memiliki lusinan dari mereka.

Bahkan sebagai fitur bahasa potensial, itu akan membutuhkan representasi metadata. Untuk kasus ini, antarmuka penanda (atau antarmuka perilaku) sama bagusnya dengan apa pun. Mencoba menyampaikan kekekalan jenis koleksi melalui entri metadata baru tampaknya tidak tepat karena CLR tidak boleh membuat asumsi tentang bagaimana kelas arbitrer berfungsi secara internal (dan CLR tidak memiliki konsep kelas koleksi sama sekali selain untuk array).

@whoisj Saya pikir ini setidaknya dipertimbangkan untuk salah satu versi C# mendatang. Tetapi keputusan itu tidak memengaruhi kebutuhan akan antarmuka simetris di semua koleksi. Lebih jauh lagi, saya dapat membayangkan skenario di mana daftar item yang dapat diubah hanya dapat dibaca dapat berguna, misalnya dalam game yang peduli dengan kualitas dan kinerja kode.

Koleksi yang tidak dapat diubah juga sudah tersedia:

https://msdn.microsoft.com/en-us/library/system.collections.immutable (v=vs.111).aspx

Untuk mencapai koleksi yang sepenuhnya tidak dapat diubah, kita hanya perlu cara mendefinisikan immutable T dan kemudian menggunakannya untuk mendeklarasikan koleksi Immutable...<T> .

@HaloFour kita sudah pernah melalui jalan ini sebelumnya :smirk: tapi saya masih percaya CLR membutuhkan cara untuk mengatakan "inilah pegangannya, baca darinya tetapi semua tindakan yang menyebabkan segala jenis penulisan melaluinya akan gagal; oh dan kekekalan ini menular sehingga pegangan apa pun yang dicapai dari pegangan yang tidak dapat diubah juga tidak dapat diubah (termasuk this )".

@dsaf benar-benar! Dalam edisi lain saya mengusulkan agar kami memiliki anti-istilah yang dapat ditulis untuk readdonly untuk memungkinkan penggunaan koleksi readonly elemen yang dapat ditulisi. Sesuatu di sepanjang baris readonly Bag<writable Element> .

Saya menyarankan agar referensi apa pun yang ditandai dengan & diperlakukan sebagai tidak dapat diubah oleh kompiler. Saya masih merasa itu hanya perlu pemeriksaan waktu kompilasi, dan harus ditegakkan oleh CLR itu sendiri karena saya kebanyakan mencari verifikasi waktu kompilasi logika dan bukan jaminan run-time. Ini akan mencakup referensi apa pun yang diinginkan pengembang untuk tidak berubah, tetapi berdasarkan per panggilan.

@whoisj Mungkin, tapi itu cukup tangensial dan ternyata permintaan ini dari sesuatu yang dsaf dapat cabang/PR sore ini menjadi sesuatu yang melibatkan upaya di tiga tim yang berbeda.

Anda juga memperlakukan ini sebagai masalah kompiler. Pada titik ini tidak ada kompiler yang terlibat (di luar kompiler JIT) dan hanya verifikator yang dapat mencoba untuk mencegah kode "tidak benar" dijalankan. Bahkan mekanisme kekekalan runtime yang ada, bidang initonly , dapat dengan mudah dikalahkan jika verifikasi dilewati (atau melalui refleksi).

Saya setuju bahwa akan lebih baik jika bahasa C# dan kompiler dapat memiliki dukungan yang lebih baik untuk metode "murni". Atribut PureAttribute sudah ada tetapi digunakan secara sporadis dan sebenarnya tidak ada dukungan bahasa untuk itu. Dan bahkan jika C# mendukungnya melalui kesalahan kompiler (pure hanya dapat memanggil murni, dll.), itu sangat mudah dikalahkan dengan menggunakan bahasa yang berbeda. Tetapi metode ini harus mengumumkan diri mereka sendiri dan memaksakan diri karena setelah dikompilasi ke IL semua taruhan pada dasarnya tidak aktif dan tidak ada kompiler yang dapat membengkokkan bagaimana perakitan yang ada dijalankan.

@HaloFour adil.

Dengan asumsi kami tidak memiliki cara umum untuk mendukung referensi "murni" atau "const", maka saya kira yang diusulkan adalah alternatif terbaik.

Jika Anda membutuhkannya sekarang, perpustakaan Commons saya (Commons.Collections https://github.com/yanggujun/commonsfornet/tree/master/src/Commons.Collections/Set ) memiliki dukungan set readonly. Admin tolong hapus posting ini jika ini dianggap sebagai iklan... Saran saya adalah untuk mencari beberapa implementasi open source.

@yanggujun Terima kasih atas sarannya, sepertinya perpustakaan yang bagus, tapi saya akan menggulung sendiri untuk menghindari ketergantungan tambahan.

Saran saya adalah untuk mencari beberapa implementasi open source.

Ini adalah solusi, antarmuka mendasar seperti IReadOnlySet harus benar-benar menjadi bagian dari .NET Framework itu sendiri.

Apakah ini memerlukan speclet untuk menjadi "siap untuk tinjauan api"?

Dan sementara kami melakukannya, pertimbangkan untuk menamainya sesuatu yang berbeda dari "Hanya Baca" (lihat posting menarik: http://stackoverflow.com/questions/15262981/why-does-listt-implement-ireadonlylistt-in-net-4- 5)
"Dapat dibaca" tampaknya baik-baik saja.

@GiottoVerducci Tidak. Saya lebih suka mempertahankan pola penamaan yang konsisten meskipun tidak sempurna. Anda bebas mengangkat masalah terpisah untuk mengganti nama antarmuka yang ada.

Desain API yang diusulkan:

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
    bool IsSubsetOf(IEnumerable<T> other);
    bool IsSupersetOf(IEnumerable<T> other);
    bool IsProperSupersetOf(IEnumerable<T> other);
    bool IsProperSubsetOf(IEnumerable<T> other);
    bool Overlaps(IEnumerable<T> other);
    bool SetEquals(IEnumerable<T> other);
}

Ini didasarkan pada ISet<> API (kecuali metode mutasi jelas).

Sayang sekali Comparer tidak cocok dengan ini, tetapi kemudian ISet<> maupun IReadOnlyDictionary<> mengekspos pembanding, jadi sudah terlambat untuk memperbaikinya sekarang.

    bool Contains(T item);

Bukankah ini seharusnya dalam IReadOnlyCollection<T> karena ICollection<T> memiliki Contains(T item) ?

Paket koleksi yang tidak dapat diubah telah tidak terdaftar dari nuget saat masih beta.
Saya pikir ini adalah kasus penggunaan yang cukup umum dan harus ditangani di lib standar.

Apakah ada lebih banyak pekerjaan yang harus dilakukan pada API di sini, seperti yang disarankan tag? Saya senang meluangkan waktu untuk hal ini jika itu akan membantu dan seseorang dapat menunjukkan apa yang dibutuhkan.

API @ashmind yang diusulkan tampak hebat.

Bisakah ISet<T> dibuat untuk memperpanjang IReadOnlySet<T> ? Ini tidak terjadi IList<T> / IReadOnlyList<T> ?

Jika tidak, maka saya kira perubahan lain yang perlu dipertimbangkan adalah menambahkan IReadOnlySet<T> ke daftar antarmuka untuk semua ISet<T> implementasi di corefx termasuk HashSet<T> , SortedSet<T> dan mereka rekan-rekan abadi di System.Collections.Immutable .

Saya harus setuju dengan @GiottoVerducci . Menggunakan nama seperti IReadOnlySet<T> tidak mendeklarasikan kemampuan kontrak, ini mendeklarasikan batasan kontrak. Untuk kemudian menggunakan kontrak yang sama yang digabungkan dengan kontrak lain yang bertentangan dengan batasan tersebut adalah membingungkan. Saya percaya bahwa nama kontrak harus menggambarkan pernyataan positif yang berkaitan dengan apa yang didukung oleh pelaksana. Nama seperti IReadableSet<T> memang tidak bagus, tetapi setidaknya lebih baik menggambarkan apa yang dilakukan oleh pelaksana.

@HaloFour Saya setuju pada prinsipnya, tetapi kami memiliki situasi yang sama sekarang dengan IReadOnlyList<T> . Mempertahankan konsistensi mengalahkan peningkatan presisi di sini, IMHO.

@drewnoakes

Saya mengerti, dan konsistensi itu penting. Saya pikir itu juga menjawab mengapa ISet<T> tidak boleh diperpanjang IReadOnlySet<T> .

Mempertahankan konsistensi mengalahkan peningkatan presisi di sini, IMHO.

Saya pikir itu juga menjawab mengapa ISet<T> tidak boleh diperpanjang IReadOnlySet<T> .

Saya pikir Anda kehilangan intinya. Itulah alasan mengapa IList<T> , ICollection<T> , IDictionary<TKey, TValue> harus, selain ISet<T> , juga diperbaiki untuk mengimplementasikan antarmuka tampilan hanya-baca. Jika tidak, setiap orang harus terus bingung ketika mengerjakan desain BCL yang tidak intuitif .

@binki

Saya tidak setuju. Apa yang saya tidak suka tentang itu adalah memiliki kontrak yang menetapkan perilaku read-_only_ diperpanjang oleh kontrak yang menetapkan perilaku read-_write_. Penamaannya salah dan komposisinya salah. Tapi di sini kita. Saya ingin memilih untuk mengubah keduanya, tetapi saya ragu itu ada di atas meja.

@HaloFour

Saat Anda memasukkan antarmuka ke dalam sesuatu, itu adalah _view_ menjadi sesuatu. Tampilan itu sendiri _is_ hanya-baca. Dengan asumsi Anda menulis kode tipe-safe dan tidak akan melakukan upcasting, jika Anda menerima sesuatu yang hanya-baca, itu, untuk semua maksud dan tujuan, hanya-baca. Itu tidak menjamin bahwa data tidak akan berubah. Ini seperti membuka file read-only. File yang dibuka hanya-baca dapat dimutasi oleh proses lain. Atau seperti akses baca-saja ke halaman di situs web di mana administrator akan memiliki tampilan baca-tulis ke dalam data dan dapat mengubahnya dari bawah Anda.

Saya tidak yakin mengapa read-only dianggap sebagai istilah yang salah di sini. Hanya-baca tidak _not_ menyiratkan tidak dapat diubah. Ada seluruh paket nuget/API yang berbeda (di mana penambahan/penghapusan menghasilkan objek baru dan instance saat ini dijamin tidak akan pernah bermutasi—sehingga tidak dapat diubah) untuk itu jika itu yang Anda butuhkan.

Aku sedang memikirkan sesuatu yang serupa. "Hanya baca" di .NET juga merupakan jaminan yang cukup lemah untuk bidang. Mengingat do-over, saya yakin semua ini akan lebih masuk akal. Untuk saat ini ada baiknya bersikap pragmatis.

Jadi secara umum, jika suatu metode menerima IReadOnlySomething<T> Anda dapat, secara umum, menganggap bahwa itu tidak akan mengubahnya. Tidak ada jaminan bahwa metode penerima tidak akan mengubah referensi, dan tidak ada jaminan bahwa implementasi antarmuka juga tidak akan memodifikasi dirinya sendiri secara internal saat diakses.

Di C++, const_cast melemahkan jaminan const juga, yang memalukan (terutama saat ini dengan pengubah mutable ) tetapi dalam praktiknya tidak menghilangkan seberapa berguna const adalah sebagai fitur. Anda hanya perlu tahu apa yang Anda hadapi.

@binki membuat perbedaan yang bagus. _Immutable_ dalam namanya menyiratkan jaminan stabilitas yang keras dari waktu ke waktu untuk semua yang terlibat.

Adakah yang punya sumber otoritatif mengapa IList<T> tidak memperpanjang IReadOnlyList<T> ?

@binki

Antarmuka bukan tampilan, itu kontrak. Kontrak itu menyatakan kemampuan pelaksana. Jika pelaksana tidak benar-benar menerapkan kemampuan itu, saya akan menganggap itu sebagai pelanggaran kontrak. Kelas List<T> mengklaim bahwa itu "adalah-a" IReadOnlyList<T> , tetapi sebenarnya tidak. Itu tidak memiliki kemampuan itu.

Ada banyak aliran pemikiran tentang hal ini. Saya jelas milik sekolah di mana pewarisan antarmuka lebih ketat mengikuti hubungan "is-a" antar tipe. Saya tentu saja mendukung pendekatan yang lebih terperinci untuk komposisi dengan antarmuka dan berpikir bahwa List<T> dan kerabatnya mungkin dapat mengambil manfaat dari penerapan beberapa 3-4 antarmuka tambahan (baca, tulis, tambahkan, dll.) Tapi saya pasti berpikir bahwa nama antarmuka harus menjelaskan apa yang dilakukan tipe _can_, bukan apa yang _tidak bisa_ lakukan. Pernyataan kemampuan negatif tidak masuk akal untuk kontrak.

@drewnoakes

Untuk saat ini ada baiknya bersikap pragmatis.

Saya setuju. Kami berada di tempat kami berada. Jika IList<T> diubah menjadi IReadOnlyList<T> maka masuk akal jika ISet<T> diubah menjadi IReadOnlySet<T> , dll.

Apakah terlalu berlebihan untuk juga mendorong IReadableX<T> , IWritableX<T> , dll. antarmuka untuk hidup bersama IReadOnlyX<T> ?

Adakah yang punya sumber otoritatif mengapa IList<T> tidak memperpanjang IReadOnlyList<T> ?

Rupanya itu akan menjadi perubahan yang melanggar ABI saat memuat rakitan yang dikompilasi terhadap kerangka kerja .net yang lebih lama. Karena saat mengimplementasikan antarmuka, sebagian besar kompiler akan secara otomatis menghasilkan implementasi antarmuka eksplisit ketika kode sumber bergantung pada implementasi antarmuka implisit, jika Anda mengkompilasi kelas Anda mengimplementasikan IList<T> terhadap BCL yang tidak memiliki IList<T> menerapkan IReadOnlyList<T> , compiler tidak akan secara otomatis membuat eksplisit IReadOnlyList<T> implementasi. Jika saya membaca ini dengan benar: http://stackoverflow.com/a/35940240/429091

@HaloFour Sejak List<> dan HashSet<> mengimplementasikan ICollection<> dan IReadOnlyCollection<> , kami telah menerapkan jalur di mana IReadOnly merujuk ke akses dan bukan kemampuan. Berdasarkan itu, memiliki IAnything extend IReadOnlyAnything sangat masuk akal. Saya setuju bahwa IReadable lebih baik daripada IReadOnly tetapi pada titik ini semua orang mengerti IReadOnly untuk _mean_ IReadable dan menggunakannya seperti itu. Sebenarnya, saya mengabadikan itu dengan sengaja dalam basis kode saya sendiri karena memiliki dua cara berpikir tentang berbagai hal lebih merupakan beban kognitif daripada apa pun menurut saya.

Kami terjebak dengan nama, tetapi konsep di baliknya cukup kuat sehingga saya benar-benar berharap semua antarmuka dapat memperluas IReadOnly ke depan, seperti yang kami lakukan dengan kelas konkret.

@ashmind Saya pikir itu sempurna bahwa tidak ada metode yang mengambil pembanding. Dalam set dan kamus, pembanding bukanlah sesuatu yang dapat Anda tukar dengan mudah karena mereka menentukan struktur seluruh objek. Juga, tidak masuk akal untuk meneruskan pembanding ke CaseInsensitiveStringCollection atau koleksi apa pun yang menyiratkan perbandingan tertentu.

(Dalam kasus koleksi aneh yang mengimplementasikan Contains(T, IEqualityComparer<T>) lebih efisien daripada metode ekstensi yang sudah tersedia, itu mungkin akan menjadi metode kelas satu kali. Sulit membayangkan Contains(T, IEqualityComparer<T>) menjadi umum cukup untuk berakhir di antarmuka khusus, tetapi tidak ada yang menghentikan hal itu terjadi.)

@jnm2

Saya pikir itu sempurna bahwa tidak ada metode yang mengambil pembanding.

Hanya untuk memperjelas, saya ingin mengatakan bahwa itu harus mengekspos pembanding, bukan mengambilnya. Karena setiap Set atau Kamus harus memiliki beberapa algoritma kesetaraan, ini bisa saja diekspos pada antarmuka. Tapi saya tidak ingat kasus penggunaan saya untuk itu sekarang -- sesuatu seperti membuat set menggunakan pembanding yang sama seperti yang disediakan secara eksternal.

Sementara diskusi ini membawa banyak poin menarik, tampaknya menyimpang jauh dari saran sederhana dan jelas yang memulai utas ini. Dan itu mengecewakan karena saya benar-benar ingin melihat masalah ini ditangani.

Seperti yang dikatakan OP, kegagalan untuk mempertahankan paritas di antara tipe koleksi ketika IReadOnlyList ditambahkan tanpa IReadOnlySet sangat disayangkan dan banyak orang telah mengimplementasikan versi antarmuka IReadOnlySet mereka sendiri sebagai solusi (tim saya sendiri memiliki solusi serupa). Antarmuka solusi tersebut tidak ideal karena kelas corefx tidak dapat mengimplementasikannya. Ini adalah alasan utama untuk menyediakan ini dalam kerangka kerja: jika saya memiliki HashSet, saya ingin dapat menggunakannya sebagai IReadOnlySet tanpa menyalin atau membungkus objek yang sudah saya miliki. Untuk kinerja setidaknya ini sering diinginkan.

Nama antarmuka harus jelas IReadOnlySet. Konsistensi mengalahkan kekhawatiran apa pun dengan nama IReadOnlyXXX. Kapal itu telah berlayar.

Tak satu pun dari antarmuka yang ada (IReadOnlyCollection) dapat diubah. Persyaratan back-compat untuk .NET tidak mengizinkan perubahan seperti itu. Sangat disayangkan bahwa Pembanding tidak diekspos di antarmuka IReadOnlyXXX yang ada (saya juga mengalami ini) tetapi sekali lagi kapal telah berlayar.

Satu-satunya pertanyaan yang tampaknya tetap dari sudut pandang praktis adalah antara dua definisi potensial dari antarmuka ini.

Sebelumnya diusulkan oleh @ashmind :

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
    bool IsSubsetOf(IEnumerable<T> other);
    bool IsSupersetOf(IEnumerable<T> other);
    bool IsProperSupersetOf(IEnumerable<T> other);
    bool IsProperSubsetOf(IEnumerable<T> other);
    bool Overlaps(IEnumerable<T> other);
    bool SetEquals(IEnumerable<T> other);
}

Usulan minimal:

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
}

Secara pribadi saya lebih suka proposal minimal ini karena metode lain dapat diturunkan; idealnya akan ada implementasi standar dari metode tersebut sebagai metode ekstensi melalui antarmuka IReadOnlySet sehingga pelaksana IReadOnlySet tidak perlu menyediakannya. Saya juga merasa proposal minimal ini lebih sesuai dengan antarmuka IReadOnlyXXX minimal lainnya.

@aaron-meyers Satu-satunya kekhawatiran yang saya miliki adalah bahwa IsSubsetOf dan teman-teman tidak dapat diturunkan dari Contains dengan cara _efisien_. Ketika dua tabel hash, misalnya, mengandalkan Contains memaksa implementasi untuk menggunakan loop bersarang daripada pencocokan hash.

Mungkin antarmuka baru, IComparableSet<T> dapat berisi operasi yang ditetapkan.

Kami sudah memiliki metode ekstensi pada IEnumerable<T> untuk beberapa operasi yang ditetapkan.

@jnm2 Implementasi metode yang digunakan oleh HashSet ini hanya membutuhkan Berisi dan menghitung koleksi lainnya (yang akan diperoleh IReadOnlySet dengan mewarisi IReadOnlyCollection). Itu memang membutuhkan mengetahui bahwa set lain menggunakan pembanding yang sama. Mungkin ada baiknya menambahkan properti Pembanding ke IReadOnlySet sehingga operasi ini dapat diimplementasikan secara efisien dalam metode ekstensi. Sangat disayangkan bahwa IReadOnlyDictionary tidak mengekspos KeyComparer juga tetapi mungkin perlu menambahkan ini ke IReadOnlySet meskipun tidak sepenuhnya konsisten. Ada alasan bagus bahwa itu seharusnya dimasukkan di IReadOnlyDictionary sejak awal, seperti yang dibahas di sini.

Proposal yang dimodifikasi akan menjadi:

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    IEqualityComparer<T> Comparer { get; }
    bool Contains(T item);
}

Sebagai alternatif, Pembanding dapat berada pada antarmuka yang terpisah dan implementasi metode ekstensi dari operasi yang ditetapkan hanya akan menggunakan rute yang efisien jika kedua objek mengimplementasikan antarmuka dan memiliki pembanding yang sama. Pendekatan yang sama dapat diterapkan untuk IReadOnlyDictionary (bahkan, mungkin mereka hanya menggunakan antarmuka yang sama). Sesuatu seperti ISetComparable. Atau menggambar dari @drewnoakes mungkin ada IComparableSettetapi alih-alih mendefinisikan operator yang ditetapkan, itu hanya mendefinisikan pembanding:

public interface IComparableSet<T> : IReadOnlySet<T> {    
    IEqualityComparer<T> Comparer { get; }
}

Dalam hal ini IReadOnlySet kembali ke hanya mendefinisikan Berisi:

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
}

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
}
public interface IReadOnlySetEx<T> : IReadOnlySet<T> {    
    bool IsSubsetOf(IEnumerable<T> other);
    bool IsSupersetOf(IEnumerable<T> other);
    bool IsProperSupersetOf(IEnumerable<T> other);
    bool IsProperSubsetOf(IEnumerable<T> other);
    bool Overlaps(IEnumerable<T> other);
    bool SetEquals(IEnumerable<T> other);
    IEqualityComparer<T> Comparer { get; }
}
public class HashSet<T>: IReadOnlySetEx<T>, ISet<T>
{
   // Improved implementation here.
}

public static class CollectionExtensiosn
{
    public static IEqualityComparer<T> GetComparer<T>(this IReadOnlySet<T> @set)
    {
           var setEx = <strong i="5">@set</strong> as IReadOnlySetEx<T>;
           if (setEx == null)
           {
                throw new ArgumentException("set should implement IReadOnlySetEx<T> for this method.")
           }
           return setEx.Comparer;
    }

    public static bool IsSubsetOf<T>(this IReadOnlySet<T> <strong i="6">@set</strong>, IEnumerable<T> other)
    {
         var setEx = <strong i="7">@set</strong> as IReadOnlySetEx<T>;
         if (setEx != null)
         {
              return setEx.IsSubsetOf(other);
         }
         // Non optimal implementation here.
    }

    // The same approach for dictionary.
    public static IEqualityComparer<T> GetKeyComparer<T>(this IReadOnlyDictionary<T> dictionary)
    {
           var dictionaryEx = set as IReadOnlyDictionaryEx<T>;
           if (dictionaryEx == null)
           {
                throw new ArgumentException("dictionary should implement IReadDictionaryEx<T> for this method.")
           }
           return dictionaryEx.KeyComparer;
    }

}

Kita bisa menggunakan pendekatan ini.
Penggunaan akan terlihat seperti ini;

IReadOnlySet<string> mySet = new HashSet<string>();
bool test = mySet.IsSubsetOf(new []{"some", "strings", "set"}); // Extension method
var = mySet.GetComparer(); // Extension method

Banyak persyaratan terpenuhi, IReadOnlySetadalah minimalis. Tetapi metode GetComparer now, bukan properti. Tapi ini adalah pertukaran yang bagus.

    /// <summary>
    /// Readable set abstracton. Allows fast contains method, also shows that collection items are unique by some criteria.
    /// </summary>
    /// <remarks>
    /// Proposal for this abstraction is discussed here https://github.com/dotnet/corefx/issues/1973.
    /// </remarks>
    /// <typeparam name="T">The type of elements in the set.</typeparam>
    public interface IReadOnlySet<out T> : IReadOnlyCollection<T>
    {
        /// <summary>
        /// Determines whether a <see cref="T:System.Collections.Generic.HashSet`1"/> object contains the specified
        /// element.
        /// </summary>
        /// <typeparam name="TItem">The type of the provided item. This trick allows to save contravariance and save from boxing.</typeparam>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.HashSet`1"/> object contains the specified element;
        /// otherwise, false.
        /// </returns>
        /// <param name="item">The element to locate in the <see cref="T:System.Collections.Generic.HashSet`1"/> object.</param>
        bool Contains<TItem>(TItem item);
    }

namespace System.Collections.Generic
{
    /// <summary>
    /// Provides the base interface for the abstraction of sets. <br/>
    /// This is full-featured readonly interface but without contravariance. See contravariant version
    /// <see cref="IReadOnlySet{T}"/>.
    /// </summary>
    /// <typeparam name="T">The type of elements in the set.</typeparam>
    public interface IReadableSet<T> : IReadOnlySet<T>
    {
        /// <summary>
        /// Gets the <see cref="Generic.IEqualityComparer{T}"/> object that is used to determine equality for the values
        /// in the set.
        /// </summary>
        /// <returns>
        /// The <see cref="Generic.IEqualityComparer{T}"/> object that is used to determine equality for the values in the
        /// set.
        /// </returns>
        IEqualityComparer<T> Comparer { get; }

        /// <summary>
        /// Determines whether a <see cref="T:System.Collections.Generic.HashSet`1"/> object contains the specified
        /// element.
        /// </summary>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.HashSet`1"/> object contains the specified element;
        /// otherwise, false.
        /// </returns>
        /// <param name="item">The element to locate in the <see cref="T:System.Collections.Generic.HashSet`1"/> object.</param>
        bool Contains(T item);

        /// <summary>
        /// Determines whether the current set is a proper (strict) subset of a specified collection.
        /// </summary>
        /// <returns><see langword="true"/> if the current set is a proper subset of <paramref name="other"/>; otherwise, false.</returns>
        /// <param name="other">The collection to compare to the current set.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="other"/> is null.
        /// </exception>
        bool IsProperSubsetOf(IEnumerable<T> other);

        /// <summary>Determines whether the current set is a proper (strict) superset of a specified collection.</summary>
        /// <returns>true if the current set is a proper superset of <paramref name="other"/>; otherwise, false.</returns>
        /// <param name="other">The collection to compare to the current set. </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="other"/> is null.
        /// </exception>
        bool IsProperSupersetOf(IEnumerable<T> other);

        /// <summary>Determines whether a set is a subset of a specified collection.</summary>
        /// <returns>true if the current set is a subset of <paramref name="other"/>; otherwise, false.</returns>
        /// <param name="other">The collection to compare to the current set.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="other"/> is null.
        /// </exception>
        bool IsSubsetOf(IEnumerable<T> other);

        /// <summary>Determines whether the current set is a superset of a specified collection.</summary>
        /// <returns>true if the current set is a superset of <paramref name="other"/>; otherwise, false.</returns>
        /// <param name="other">The collection to compare to the current set.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="other"/> is null.
        /// </exception>
        bool IsSupersetOf(IEnumerable<T> other);

        /// <summary>Determines whether the current set overlaps with the specified collection.</summary>
        /// <returns>true if the current set and <paramref name="other"/> share at least one common element; otherwise, false.</returns>
        /// <param name="other">The collection to compare to the current set.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="other"/> is null.
        /// </exception>
        bool Overlaps(IEnumerable<T> other);

        /// <summary>Determines whether the current set and the specified collection contain the same elements.</summary>
        /// <returns>true if the current set is equal to <paramref name="other"/>; otherwise, false.</returns>
        /// <param name="other">The collection to compare to the current set.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="other"/> is null.
        /// </exception>
        bool SetEquals(IEnumerable<T> other);
    }
}

Baru saja menerbitkan kontrak ini dengan pembantu injeksi ke nuget (https://www.nuget.org/packages/ClrCoder.Collections.ReadOnlySet).
Jangan ragu untuk menggunakannya dan kirimkan masalah di sini: https://github.com/dmitriyse/ClrCoder/issues
Jika ini akan menjadi sedikit populer (mungkin setelah beberapa putaran penyempurnaan), kami dapat menyarankan peningkatan ini kepada tim CoreFX.

@terrajobst apakah boleh untuk compat untuk kelas yang ada untuk mengimplementasikan antarmuka baru?

@safern ada preseden dalam List<T> mendapatkan IReadOnly ditambahkan.

Jadi apakah direncanakan untuk menambahkan rilis kerangka .NET berikutnya?

Kapan pekerjaan ini akan mendarat? Ada garis waktu?

https://www.nuget.org/packages/System.Collections.Immutable/
Kumpulan lengkap koleksi yang tidak dapat diubah)

OKE. Ini sudah terlalu lama di sini. Saya sangat membutuhkannya dan ingin mengusulkan cara bagaimana mendekati ini.

Alih-alih mengekspos semua ini:

IEqualityComparer<T> Comparer { get; }
bool IsProperSubsetOf(IEnumerable<T> other);
bool IsProperSupersetOf(IEnumerable<T> other);
bool IsSubsetOf(IEnumerable<T> other);
bool IsSupersetOf(IEnumerable<T> other);
bool Overlaps(IEnumerable<T> other);
bool SetEquals(IEnumerable<T> other);

dan memaksa pelanggan untuk menerapkan anggota ini, mari kita tambahkan apa yang paling penting untuk dimiliki:

public interface IReadOnlySet<out T> : IReadOnlyCollection<T>
{
    bool Contains<T>(T item);
}

dan tidak ada lagi. Lebih banyak API selalu dapat ditambahkan di masa mendatang, jika ada kebutuhan seperti itu. Tapi itu tidak bisa dihilangkan, demikian proposal ini.

Kami kemudian akan menambahkan antarmuka ini ke daftar antarmuka yang diperluas oleh ISet<T> .

Dari dokumentasi

ISet<T> : Antarmuka ini menyediakan metode untuk mengimplementasikan set, yang merupakan koleksi yang memiliki elemen unik dan operasi spesifik. HashSetdan SortedSetkoleksi mengimplementasikan antarmuka ini.

Dari kode

Antarmuka ISet<T> sudah memiliki metode bool Contains<T>(T item); yang ditentukan melalui ICollection<T> . Ini juga memiliki int Count { get; } melalui ICollection<T> .

Jadi akan:

public interface ISet<T> : ICollection<T>, IEnumerable<T>, IEnumerable, IReadOnlySet<T>

Perubahan sepele, sedikit untuk dibahas, manfaat besar.

Tolong mari kita mewujudkannya. Beri tahu saya jika permintaan tarik seperti itu akan diterima dan digabungkan. Saya bisa membuatnya kemudian.

@karelz @terrajobst @safern @ianhays

Saya menemukan diskusi ini ketika mengerjakan masalah dunia nyata di mana saya ingin menggunakan kumpulan kunci kamus untuk operasi set hanya baca. Untuk mendukung kasus itu, inilah API yang saya usulkan.

Alasan

API yang Diusulkan

 namespace System.Collections.Generic {
+    public interface IReadOnlySet<out T> : IReadOnlyCollection<T>, IEnumerable, IEnumerable<T> {
+        bool Contains(T value);
+        bool IsProperSubsetOf(IEnumerable<T> other);
+        bool IsProperSupersetOf(IEnumerable<T> other);
+        bool IsSubsetOf(IEnumerable<T> other);
+        bool IsSupersetOf(IEnumerable<T> other);
+        bool Overlaps(IEnumerable<T> other);
+        bool SetEquals(IEnumerable<T> other);
+    }
-    public class HashSet<T> : ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T> {
+    public class HashSet<T> : ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T>, IReadOnlySet<T> {
     }
-    public class SortedSet<T> : ICollection, ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T> {
+    public class SortedSet<T> : ICollection, ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T>, IReadOnlySet<T> {
     }
+    public class ReadOnlySet<T> : ICollection<T>, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlySet<T>, ISet<T> {
+        public int Count { get; }
+        public ReadOnlySet(ISet<T> set);
+        public bool Contains(T value);
+        public bool IsProperSubsetOf(IEnumerable<T> other);
+        public bool IsProperSupersetOf(IEnumerable<T> other);
+        public bool IsSubsetOf(IEnumerable<T> other);
+        public bool IsSupersetOf(IEnumerable<T> other);
+        public bool Overlaps(IEnumerable<T> other);
+        public bool SetEquals(IEnumerable<T> other);
+    }
     public class Dictionary<TKey, TValue> {
-        public sealed class KeyCollection : ICollection, ICollection<TKey>, IEnumerable, IEnumerable<TKey>, IReadOnlyCollection<TKey> {
+        public sealed class KeyCollection : ICollection, ICollection<TKey>, IEnumerable, IEnumerable<TKey>, IReadOnlyCollection<TKey>, IReadOnlySet<TKey> {
+            public bool IsProperSubsetOf(IEnumerable<TKey> other);
+            public bool IsProperSupersetOf(IEnumerable<TKey> other);
+            public bool IsSubsetOf(IEnumerable<TKey> other);
+            public bool IsSupersetOf(IEnumerable<TKey> other);
+            public bool Overlaps(IEnumerable<TKey> other);
+            public bool SetEquals(IEnumerable<TKey> other);
         }
     }
 }

Pertanyaan-pertanyaan terbuka

  • Apakah menambahkan metode ini ke Dictionary<TKey, TValue>.KeyCollection dapat diterima mengingat biaya ukuran kode untuk tipe generik yang umum dipakai? Lihat di sini .
  • Haruskah IReadOnlySet<T> diimplementasikan di Dictionary KeyCollection lain seperti SortedDictionary atau ImmutableDictionary ?

Pembaruan

  • Dihapus TryGetValue karena tidak ada di ISet<T> dan dengan demikian akan mencegah kemungkinan rebasing ISet<T> untuk mengimplementasikan IReadOnlySet<T> .
  • Menambahkan kelas ReadOnlySet<T> yang mirip dengan ReadOnlyCollection<T> .

@TylerBrinkley terima kasih telah menambahkan Proposal API di utas. Maukah Anda menambahkan alasan dan kasus penggunaan? Agar saya dapat menandainya sebagai siap untuk ditinjau dan membiarkan ahli api memutuskan?

@TylerBrinkley jangan lupa untuk menyertakan properti EqualityComparer di IReadOnlySetantarmuka. Mereka saat ini tidak terjawab di Kamus dan Set di CoreFX, tapi ini masalah.

@dmitriyse apa gunanya properti getter hanya IEqualityComparer<T> ? Apa yang akan kamu lakukan dengannya?

Kamus dan Set harus melaporkan EqualityComparers mereka untuk memungkinkan kloning koleksi yang benar.
Sayangnya saya lupa di mana masalah ini dibahas.

Jika Anda melakukan kloning, bukankah Anda akan bekerja dengan tipe konkret? Mengapa antarmuka perlu mendukung IEqualityComparer<T> ?

Misalnya jika Anda telah menyetel dengan string dan pembanding kesetaraan ketidakpekaan huruf besar/kecil, Anda akan menerima pengecualian saat membuat HashSet baru tanpa menentukan EqualityComparer yang benar. Ada kasus ketika Anda tidak dapat mengetahui apa yang digunakan EqualityComparer di set yang ditentukan.

Ini bukan hanya kloning. Saya pikir skenario yang jauh lebih umum adalah membandingkan dua set -- Saya perlu tahu bahwa keduanya menggunakan pembanding yang sama untuk menerapkan perbandingan optimal menggunakan Berisi. Saya pikir ada contoh di utas ini.

Yang mengatakan, saya lebih suka memiliki IReadOnlySet hanya dengan metode Berisi daripada tidak sama sekali. Akan menyenangkan untuk dapat mengimplementasikan perbandingan Set secara umum tetapi tidak biasa seperti hanya membutuhkan referensi baca-saja ke Set.

Dapatkan Outlook untuk iOS https://aka.ms/o0ukef


Dari: pemberitahuan Tyler [email protected]
Dikirim: Kamis, 10 Mei 2018 6:21:52
Kepada: dotnet/corefx
Cc: Aaron Meyers; Menyebutkan
Perihal: Re: [dotnet/corefx] Silakan tambahkan antarmuka IReadOnlySet dan buat HashSet, SortedSet mengimplementasikannya (#1973)

Jika Anda melakukan kloning, bukankah Anda akan bekerja dengan tipe konkret? Mengapa antarmuka perlu mendukung IEqualityComparer.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fdotnet%2Fcorefx%2Fissues%2F1973%23issuecomment-388051258&data=02 % 7C01% 7C% 7Cc45ea16cd3034ddd69d808d5b678ff33% 7C84df9e7fe9f640afb435aaaaaaaaaaaa% 7C1% 7C0% 7C636615553141417289 & SDATA = xRI27JtyaAwnZ2anY05oTlxmPY5AaGVl% 2BRdXK2uR0% 2F8% 3D & dicadangkan = 0 , atau mematikan benang https://eur01.safelinks.protection.outlook.com/?url=https%3A% 2F% 2Fgithub.com% 2Fnotifications% 2Funsubscribe-auth% 2FAMuQLmqboBWyHweWHSUoE1YM2OrfHZZxks5txD7wgaJpZM4E9KK- & data = 02% 7C01% 7C% 7Cc45ea16cd3034ddd69d808d5b678ff33% 7C84df9e7fe9f640afb435aaaaaaaaaaaa% 7C1% 7C0% 7C636615553141417289 & SDATA = hLtAXEyFNVEgWike6tMwAfUVC% 2BucyjXUDwoLOLDV5gk% 3D & dilindungi = 0 .

Saya setuju—satu-satunya cara Anda dapat mengetahui jenis duplikat apa yang mungkin Anda temukan di set (peka huruf besar-kecil, tidak peka huruf besar-kecil, dll) adalah dengan memaparkan pembanding.

Saya mulai berpikir bahwa proposal saya tidak akan diterima karena menambahkan semua metode tersebut ke Dictionary<TKey, TValue>.KeyCollection akan memiliki biaya ukuran kode yang signifikan. Lihat diskusi ini tentang menambahkan API baru ke tipe generik yang umum dipakai.

Saya tidak berpikir Anda akan dapat menempatkan pembanding pada IReadOnlySet .

SortedSet membutuhkan IComparer , sedangkan HashSet membutuhkan IEqualityComparer .

Poin bagus, pembanding dapat dianggap sebagai detail implementasi yang tidak termasuk dalam antarmuka umum.

Dapatkan Outlook untuk iOS https://aka.ms/o0ukef


Dari: Cory Nelson [email protected]
Dikirim: Kamis, 10 Mei 2018 17:04:06
Kepada: dotnet/corefx
Cc: Aaron Meyers; Menyebutkan
Perihal: Re: [dotnet/corefx] Silakan tambahkan antarmuka IReadOnlySet dan buat HashSet, SortedSet mengimplementasikannya (#1973)

Saya tidak berpikir Anda akan dapat menempatkan pembanding di IReadOnlySet.

SortedSet mengambil IComparer, sementara HashSet mengambil IEqualityComparer.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub https://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fdotnet%2Fcorefx%2Fissues%2F1973%23issuecomment-388221165&data=02 % 7C01% 7C% 7C0ef6d84125be4c450fdc08d5b6d2b70a% 7C84df9e7fe9f640afb435aaaaaaaaaaaa% 7C1% 7C0% 7C636615938478979295 & SDATA = PHkDQPiJBEIks8yNyIA7vKODM% 2BkMFI4PRX4lUUBu% 2Bi0% 3D & dicadangkan = 0 , atau mematikan benang https://eur02.safelinks.protection.outlook.com/?url=https%3A% 2F% 2Fgithub.com% 2Fnotifications% 2Funsubscribe-auth% 2FAMuQLu5JGLcqrpMyGWLygbCsaSQSXFgNks5txNV2gaJpZM4E9KK- & data = 02% 7C01% 7C% 7C0ef6d84125be4c450fdc08d5b6d2b70a% 7C84df9e7fe9f640afb435aaaaaaaaaaaa% 7C1% 7C0% 7C636615938478979295 & SDATA = 9pnuMULuDu9HWb7un% 2FWYq6iYdjTKFsjN7nKiToaeHkk% 3D & dilindungi = 0 .

Mungkin ada beberapa nilai dalam menambahkan antarmuka IUnorderedSet dan IOrderedSet , tetapi saya tidak ingin itu menggagalkan mendorong IReadOnlySet melalui.

Saya suka saran @pgolebiowski tetapi akan membuat antarmuka itu lebih mendasar.

c# public interface ISetCharacteristic<in T> { bool Contains(T item); }

Ini kemudian cocok untuk set yang tidak terhitung seperti Interval (nyata) juga. Antara itu dan IReadOnlySet Anda berpotensi masuk ke beberapa antarmuka lain seperti ICountableSet (alias IEnumerableSet ), IUnorderedSet dan IOrderedSet .

Tetapi setuju bahwa hanya IReadOnlySet akan menjadi peningkatan besar atas apa yang saat ini tersedia.

+1 untuk @NickRedwood

Sudah terbuka selama lebih dari 3 tahun sekarang. Tolong wujudkan ini dengan mengubah pendekatan. Memperkenalkan perubahan yang disebutkan oleh @NickRedwood memungkinkan semua peningkatan lain yang Anda diskusikan di utas ini. Semua pendekatan setuju tentang hal sepele ini. Jadi mari kita tambahkan satu hal sepele yang mendasar ini dan kemudian buat masalah lain untuk peningkatan potensial yang semuanya opsional.

@TylerBrinkley @safern @karelz @terrajobst

Saya tidak benar-benar melihat banyak manfaat dari antarmuka IReadOnlySet<T> hanya dengan metode Contains . Meskipun metode itu berguna, metode ini sudah disertakan dalam antarmuka ICollection<T> yang juga memiliki properti IsReadOnly dimaksudkan untuk menunjukkan apakah metode modifikasi ICollection<T> didukung. Cukup terapkan ICollection<T> dan buat IsReadOnly return true . Satu-satunya fungsi lain yang diharapkan untuk didukung adalah properti Count , dapat dihitung, dan metode CopyTo yang tampaknya tidak terlalu membatasi.

Saya akan sangat senang jika IReadOnlySet<T> diusulkan diimplementasikan berdasarkan IReadOnlyCollection<T> - ini akan menjadi peningkatan besar pada apa yang tersedia saat ini. ( @TylerBrinkley - anggap maksud Anda IReadOnlyCollection<T> daripada ICollection<T> .

Saya kira saya berbuat salah di sisi antarmuka yang lebih mendasar, dan jika ada satu untuk Set maka itu akan menjadi antarmuka metode tunggal dengan metode Contains . Ini memiliki dasar matematika yang baik dan sangat bersih. Namun ketika memasang kembali ke kelas yang ada, saya menerima bahwa memasang lebih sedikit lebih baik, dan jika hanya ada satu antarmuka yang dipasang kembali maka IReadOnlySet<T> berdasarkan IReadOnlyCollection<T> seharusnya.

IReadOnlyCollection<> tidak memiliki .Contains karena itu akan mencegahnya menjadi kovarian.

Poin bagus dan saya telah mengoreksi posting saya sebelumnya menjadi kontravarian. IReadOnlySet<T> harus invarian kecuali pendekatan yang diposting di awal utas digunakan, dan saya tidak yakin tentang itu.

@TylerBrinkley secara pribadi saya bukan penggemar properti IsReadOnly , saya lebih suka informasi ini diketahui pada waktu kompilasi daripada run-time. Jika Anda merujuk ke antarmuka IReadOnlyCollection<T> maka saya masih berpikir akan berguna bagi penelepon atau yang dipanggil untuk mengetahui bahwa pencarian akan cepat alih-alih potensi iterasi meskipun koleksi, meskipun saya tidak yakin apakah sebuah "set" menyiratkan bahwa.

Hanya memiliki metode Contains tidak menentukan apa yang harus dilakukan oleh Set , hanya apa yang harus dilakukan oleh Collection . Maksud saya Contains bahkan bukan anggota ISet tetapi ICollection yang ISet mewarisi dari. Anggota lain dari ISet harus diminta untuk menentukan apa yang harus dilakukan oleh Set . Saya mengerti kebanyakan orang mungkin menggunakan set secara eksklusif untuk metode Contains tetapi ada lebih banyak set dari itu.

@TylerBrinkley Tetapi apakah mungkin untuk mendefinisikan IReadOnlyCollection<T>.Contains(T) pada saat ini? Saya berasumsi tidak. Itulah mengapa satu-satunya pilihan adalah memperkenalkan IReadOnlySet<T> dan, ketika diperkenalkan, pastikan bahwa IReadOnlySet<T>.Contains(T) dideklarasikan di dalamnya.

@jnm2 berkata:

IReadOnlyCollection<> tidak memiliki .Contains karena itu akan mencegahnya menjadi kovarian.

Ini tampaknya menjadi masalah terbesar bagi saya saat ini. Akan aneh jika IReadOnlySet<T> menjadi invarian ketika IReadOnlyCollection<T> adalah kovarian. Sepertinya banyak antarmuka IReadOnly* ada dirancang dengan hati-hati untuk menjadi out T dengan antarmuka yang dapat ditulis yang selalu invarian. Sebagian besar dari kita ingin IReadOnlySet<T> setidaknya mendeklarasikan metode Contains(T) namun itu akan mencegahnya menjadi kovarian.

Jika ICollection benar-benar tempat yang tepat untuk mendefinisikan Contains(T) , apakah itu mungkin? Mungkin IReadOnlyProbableCollection<T>.Contains(T) yang akan menjadi invarian dan diimplementasikan oleh Collection<T> dan HashSet<T> ?

Saya pikir saran @NickRedwood tentang ISetCharacteristic<T> tampaknya lebih bersih mungkin. Itu bahkan memiliki manfaat memungkinkan Anda untuk tidak mengimplementasikan Count yang bisa berguna.

secara pribadi saya bukan penggemar properti IsReadOnly, saya lebih suka informasi ini diketahui pada waktu kompilasi daripada waktu proses.

Orang itu berbicara dengan baik, tuangkan dia lebih banyak anggur.

@binki Saya setuju perlu ada Contains metode pada IReadOnlySet<T> karena IReadOnlyCollection<T> tidak menyertakan satu, namun saya juga berpikir semua non-mutasi lainnya ISet<T> metode harus disertakan.

Selain use case Dictionary.KeyCollection saya sebutkan di atas, use case apa lagi yang dapat Anda buat untuk menambahkan antarmuka ini?

Oke, sepertinya desain API adalah:

public interface IReadOnlySet<T> : IReadOnlyCollection<T> {    
    bool Contains(T item);
}

Ini adalah satu-satunya bagian umum dari desain yang tampaknya dapat disetujui oleh semua orang.

AKU MENGATAKAN BAHWA SEKARANG KITA MENGAKHIRI DESAIN DI SINI. Jika Anda ingin memperpanjangnya di masa mendatang, lakukan dalam edisi yang berbeda. Ini adalah fungsi terpenting yang harus ada di prod.

Untuk siapa, siapa lawan?

Saya suka saran @ashmind . Akan luar biasa jika Dictionary.Keys dapat mengimplementasikan antarmuka ini karena secara teknis IReadOnlySet<TKey> .

Bisakah Anda menandai ini sebagai api-ready-for-review ? Saya baru saja menemukan diri saya membutuhkan antarmuka ini lagi dan masih belum ada.

Mengapa ini belum diterapkan?

[EDIT] Permintaan maaf - balasan asli saya bercampur dengan API lain. Saya tidak memiliki pendapat yang kuat tentang yang satu ini, teks diedit. Maaf untuk kebingungan!

Mengapa ini belum diterapkan?

API belum mendapat perhatian dari pemilik area - @safern. Saya tidak yakin seberapa tinggi daftar prioritas Koleksi. Angka upvote cukup tinggi.
Yang benar adalah bahwa kami sekarang mengunci .NET Core 3.0, jadi itu harus menunggu rilis berikutnya minimal karena tidak penting untuk 3.0.

Saya juga terkejut bahwa ini tidak disediakan di luar kotak.

Struktur data yang tidak dapat diubah dapat menjadi bantuan besar untuk meningkatkan pemeliharaan kode, tetapi sulit digunakan tanpa antarmuka di pustaka standar yang menentukan semantik dan memungkinkan implementasi yang berbeda untuk bekerja bersama.

Tanpa ini, setiap perpustakaan yang bermaksud menggunakan set yang tidak dapat diubah sebagai parameter/nilai pengembalian perlu menggunakan IEnumerable yang kurang efisien dan secara semantik salah atau menggunakan antarmuka/kelas mereka sendiri dalam tanda tangan mereka yang tidak kompatibel dengan milik orang lain.

Saya ingin tahu apakah ada solusi untuk ini yang efisien dalam hal pencarian Contains dan hindari menentukan tipe konkret dari parameter/nilai pengembalian. Yang terbaik yang bisa saya pikirkan dari atas kepala saya adalah menggunakan IEnumerable di tanda tangan dan lulus ReadOnlyDictionary.Keys, tapi itu tampaknya agak buruk, terutama di perpustakaan. Karena Enumerable.Contains di Linq akan menggunakan implementasi koleksi Contains , ini harus efisien sementara kompatibel, tetapi tidak mengomunikasikan maksud yang dapat menyebabkan penggunaan implementasi yang kurang berkinerja.

@adamt06 Anda sedang mencari ISet<T> .

@scalablecory Anda benar, dengan implementasi yang tidak dapat diubah, dan ada satu: ImmutableHashSet<T>

Adakah yang tahu/mengerti mengapa ICollectiontidak memperpanjang IReadOnlyCollection??
Saya pikir ini sedikit terkait dengan utas ini. Alih-alih saya membuka utas baru. Mungkin saya hanya salah paham tentang sesuatu.

Pikiran lain, tetapi benar-benar di luar topik, Dan mengapa ReaOnlyCollection tidak memiliki:

c# bool Contains(T item); void CopyTo(T[] array, int arrayIndex);

Oh, saya mengerti maksud Anda IReadOnlyCollection tidak memilikinya. Itu karena jika tidak, antarmuka tidak dapat menjadi kovarian .

Saya tidak yakin, tetapi mungkin ada hubungannya dengan implementasi antarmuka eksplisit yang menyebabkan masalah dengan kompatibilitas mundur. Saya dapat melihat mengapa memperbarui antarmuka yang ada untuk mewarisi dari antarmuka lain menjadi masalah, tetapi saya tidak melihat mengapa membuat antarmuka baru IReadOnlySet<T> dan mengimplementasikan HashSet akan menjadi masalah.

OKE. Tapi masih juga tidak melihat ICollectionseharusnya tidak:
c# ICollection<T> : IReadOnlyCollection<out T>
Mengapa koleksi baca/tulis tidak juga menjadi perpanjangan dari koleksi hanya baca?

@generik0

OKE. Tapi masih juga tidak melihat ICollection tidak boleh:

ICollection<out T> : IReadOnlyCollection<out T>

Mengapa koleksi baca/tulis tidak juga menjadi perpanjangan dari koleksi hanya baca?

Pertama, harap dicatat bahwa tidak mungkin mendeklarasikan ICollection<out T> karena ICollection<T> memiliki anggota .Add(T item) .

Kedua, perhatikan bahwa ICollection<T> muncul di .net-2.0 dan IReadOnlyCollection<out T> muncul di .net-4.5 . Jika Anda mengkompilasi kode sebagai implementasi ICollection<T> dan kemudian ICollection<T> diubah untuk mengimplementasikan antarmuka baru, semua binari terkompilasi yang ada akan rusak saat runtime karena apa pun yang hanya mengimplementasikan ICollection<T> tidak akan memilikinya Slot IReadOnlyCollection<T> diisi (mungkin secara otomatis) oleh kompilator. Ini adalah masalah kompatibilitas yang mencegah ICollection<T> mengimplementasikan IReadOnlyCollection<T> .

Saya tidak berpikir ini ditangani dengan jelas oleh jawaban SO ini, tetapi ada SO untuk itu: https://stackoverflow.com/a/14944400 .

@binki " " diperbaiki ke ICollection, terima kasih telah menunjukkan kesalahan ketik saya..
DotNetStandard dan net461. Dan disinilah seharusnya perubahan itu terjadi. standar bersih 2+

saya akan ulangi...
Mengapa koleksi baca/tulis tidak juga menjadi perpanjangan dari koleksi hanya baca?

Dan mengapa seseorang perlu melemparkan koleksi ke misalnya "ToArray()" hanya untuk mengekspos lebih sedikit jika itu?

Dan tentu saja Anda dapat menambahkan antarmuka baru di versi yang lebih tinggi tanpa merusak apa pun. ICollection sudah menerapkan metode IReadOnlyCollection. Catatan harus pecah,... :-/

@generik0 Sepertinya itu tidak akan merusak kompatibilitas sumber yang Anda pikirkan [tidak berpikir; itu akan], tetapi itu akan merusak kompatibilitas biner yang berfungsi dengan tabel IL, bukan C#.

Oke @jnm2 terima kasih.
Saya akan menghentikan kata-kata kasar saya di luar topik sekarang, anggap saja itu arsitektur yang buruk. Terima kasih semuanya untuk mendengarkan / menjelaskan :-)

@jnm2

@generik0 Sepertinya itu tidak akan merusak kompatibilitas sumber yang Anda pikirkan, tetapi itu akan merusak kompatibilitas biner yang berfungsi dengan tabel IL, bukan C#.

Untuk nitpick (maaf), itu juga akan merusak kompatibilitas sumber jika sumber Anda secara eksplisit menerapkan ICollection<T> dari sebelum .net-4.5. Kelas akan mulai gagal dikompilasi karena kegagalan untuk mengimplementasikan IReadOnlyCollection<T>.Count secara eksplisit. Tetapi kompatibilitas biner adalah masalah yang lebih besar karena itu akan mencegah Anda menargetkan berbagai versi .net (Anda harus memiliki biner yang berbeda untuk berjalan di ≥.net-4.5 daripada <.net-2 terlepas dan Anda akan harus menargetkan keduanya alih-alih hanya menargetkan kerangka kerja yang lebih lama).

Saya bertanya-tanya apakah dengan penambahan dukungan implementasi antarmuka default di C#8 apakah ICollection<T> dapat mengimplementasikan IReadOnlyCollection<T> untuk .NET Core 3.0+.

Mungkin diperlukan ekstensi, maka minimal koleksi yang sama. yaitu jika Anda perlu mendapatkan koleksi yang sangat banyak atau ikoleksi ke reaadonlycollection ... Ada pemikiran?

[diedit oleh @jnm2 komentar]
c# public static class CollectionExtensions { public static IReadOnlyCollection<T> CastAsReadOnlyCollection<T>(this IEnumerable<T> collection) { if((collection is IReadOnlyCollection<T> readOnlyCollection )) { return readOnlyCollection ; } throw new ArgumentException($"{collection.GetType()} not supported as readonly collection"); } }

@generik0 Mengapa tidak menggunakan enumerable as IReadOnlyCollection<T> ?? throw ... atau (IReadOnlyCollection<T>)enumerable daripada memanggil metode itu?

@jnm2 om . Kamu benar. Terkadang Anda tidak melihat gambaran yang jelas dan hal-hal yang terlalu rumit!!!

Saya masih akan memanggil ekstensi, hanya untuk mendapatkan kesederhanaan ;-)
Terima kasih sobat....

Apa statusnya di sini? Saya sangat menyukai antarmuka ini di lib standar dan HashSet<> mengimplementasikannya.

Sepertinya taruhan terbaik kami mungkin menunggu beberapa saat lagi untuk proposal Shapes untuk (semoga) diimplementasikan. Kemudian Anda dapat membuat grup bentuk apa pun untuk mewakili tipe Set yang Anda inginkan, dan memberikan implementasi sehingga set yang ada seperti HashSet<T> sesuai dengannya.

Sebuah perpustakaan komunitas untuk melakukan hal ini kemudian dapat muncul, mencakup semua berbagai jenis himpunan (intensional (predikat) dan ekstensional (terdaftar), dipesan, sebagian dipesan, tidak berurutan, dapat dihitung, terhitung tak terbatas, tak terbatas tak terhitung, mungkin domain matematika juga seperti Rasional , Bilangan asli dll) sebagai bentuk yang berbeda, dengan semua metode penyatuan, persimpangan, kardinalitas yang ditentukan untuk dan di antara himpunan ini.

Itu terdengar bagi saya seperti 5 tahun ke depan. Mengapa perubahan sederhana yang dapat diimplementasikan dalam sehari harus menunggu fitur 1000x lebih besar yang belum ditentukan yang bahkan mungkin tidak terjadi?

Itu terdengar bagi saya seperti 5 tahun ke depan. Mengapa perubahan sederhana yang dapat diimplementasikan dalam sehari harus menunggu fitur 1000x lebih besar yang belum ditentukan yang bahkan mungkin tidak terjadi?

Saya hanya menanggapi kurangnya kemajuan pada IReadOnlySet<T> - masalah ini telah dibuka selama 4 tahun.

Cara Microsoft: Hal-hal yang paling sederhana dan berguna membutuhkan waktu puluhan tahun. Ini pikiran bertiup. 5 tahun dan terus bertambah.

Yang lebih lucu adalah mereka memilikinya di sini
https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.sdk.sfc.ireadonlyset-1

@terrajobst pikiran?

  • Kami umumnya percaya bahwa ini memang lubang dalam hierarki antarmuka kami. Kami menyarankan untuk fokus hanya menambahkan antarmuka dan mengimplementasikannya pada implementasi set yang ada. Kami tidak dapat membuat ISet<T> extend IReadOnlySet<T> (untuk alasan yang sama yang tidak dapat kami lakukan untuk antarmuka yang dapat diubah lainnya). Kita dapat menambahkan ReadOnlySet<T> pada tahap selanjutnya. Kita harus memeriksa ulang bahwa IReadOnlySet<T> hanya ISet<T> dikurangi API yang dapat diubah.
  • Kita juga harus mengimplementasikan IReadOnlySet<T> pada ImmutableSortedSet<T> dan ImmutableHashSet<T> (dan pembuatnya)
  • Kita harus memindai implementasi lain dari ISet<T> .
 namespace System.Collections.Generic {
+    public interface IReadOnlySet<out T> : IReadOnlyCollection<T>, IEnumerable, IEnumerable<T> {
+        bool Contains(T value);
+        bool IsProperSubsetOf(IEnumerable<T> other);
+        bool IsProperSupersetOf(IEnumerable<T> other);
+        bool IsSubsetOf(IEnumerable<T> other);
+        bool IsSupersetOf(IEnumerable<T> other);
+        bool Overlaps(IEnumerable<T> other);
+        bool SetEquals(IEnumerable<T> other);
+    }
-    public class HashSet<T> : ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T> {
+    public class HashSet<T> : ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T>, IReadOnlySet<T> {
     }
-    public class SortedSet<T> : ICollection, ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T> {
+    public class SortedSet<T> : ICollection, ICollection<T>, IDeserializationCallback, IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, ISerializable, ISet<T>, IReadOnlySet<T> {
     }
 }

Lakukan, saya berani Anda!

@terrajobst

Kami tidak dapat membuat ISet<T> extend IReadOnlySet<T> (untuk alasan yang sama yang tidak dapat kami lakukan untuk antarmuka yang dapat diubah lainnya).

Apakah ini masih berlaku bahkan dengan metode antarmuka default? Apakah itu berarti https://github.com/dotnet/corefx/issues/41409 harus ditutup?

@terrajobst

Kami tidak dapat membuat ISet<T> extend IReadOnlySet<T> (untuk alasan yang sama yang tidak dapat kami lakukan untuk antarmuka yang dapat diubah lainnya).

Apakah ini masih berlaku bahkan dengan metode antarmuka default? Apakah itu berarti dotnet/corefx#41409 harus ditutup?

Kami membahas ini. Kami dulu berpikir bahwa DIM akan berfungsi, tetapi ketika kami menjalankan solusinya, kami menyimpulkan bahwa itu akan menghasilkan pecahan berlian yang akan menghasilkan kecocokan yang ambigu. Namun, ini baru-baru ini ditantang jadi saya pikir saya harus menuliskannya dan memastikan itu benar-benar berfungsi atau tidak.

@terrajobst / @danmosemsft Adakah yang ditugaskan untuk ini?

Dan, @terrajobst untuk memperjelas pekerjaan yang ingin kami capai adalah:
```
Kita juga harus mengimplementasikan IReadOnlySetdi ImmutableSortedSetdan ImmutableHashSet(dan pembangun mereka)
Kita harus memindai implementasi lain dari ISet.
````
Serta mengimplementasikan antarmuka di atas pada HashSet, SortedSet.

Pemindaian hanya mencari apa saja dan memunculkannya jika terlihat meragukan.

Jika ini masih untuk diperebutkan, saya akan tertarik

@Jlalond tidak, ditugaskan untuk Anda. Terima kasih atas tawarannya.

@danmosemsft @terrajobst
Hanya pembaruan. Saya sedang mengerjakan ini, menambahkan antarmuka ke inti pribadi Lib, hanya tersandung melalui koleksi collections.generic dan abadi mengambil ini.

Pertanyaan terakhir jika Anda tahu dari kepala Anda Dan, apakah saya perlu membuat perubahan pada Mono untuk ini? Saya tidak mengerti di mana corefx berakhir dan mono dimulai. Jadi jika Anda tahu itu bisa menyelamatkan saya dari beberapa penelitian independen

@Jlalond Anda tidak perlu melakukan perubahan pada Mono. Sebagian alasan untuk memindahkan runtime Mono ke dalam repo ini adalah untuk membuatnya lancar menggunakan pustaka yang sama persis dengan runtime CoreCLR atau Mono. Hanya ada sebagian kecil dari pustaka inti yang menyimpang:
coreclrsrcSystem.Private.CoreLib vs mononetcoreSystem.Private.CoreLib. (Sebagian besar perpustakaan inti dibagikan dari perpustakaanSystem.Private.CoreLib). Jadi kecuali Anda menyentuh itu - Anda tidak terpengaruh.

@danmosemsft Terima kasih atas klarifikasinya, saya harap ini segera selesai.

Hei @danmosemsft ikuti saja ini. CoreLib sedang membangun di rakitan src saya bisa melihat perubahan yang direferensikan. Namun majelis ref tampaknya tidak mendeteksi perubahan apa pun. Ini semua yang menahan saya, tetapi saya tidak dapat menemukan info apa pun di dokumen. Setiap orang atau petunjuk yang dapat Anda berikan kepada saya sehingga saya dapat menyelesaikan ini (maksud saya, 5 tahun kemudian)

Ditujukan oleh #32488

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

aggieben picture aggieben  ·  3Komentar

sahithreddyk picture sahithreddyk  ·  3Komentar

matty-hall picture matty-hall  ·  3Komentar

EgorBo picture EgorBo  ·  3Komentar

v0l picture v0l  ·  3Komentar