Runtime: Bitte fügen Sie die Schnittstelle IReadOnlySet hinzu und lassen Sie HashSet, SortedSet implementieren

Erstellt am 9. Juni 2015  ·  104Kommentare  ·  Quelle: dotnet/runtime

Original

Seit IReadOnlyList hinzugefügt wurde, hat sich die Parität zwischen Sets und Listen verringert. Es wäre toll, es wieder aufzubauen.

Es wäre eine implementierungsunabhängige Art zu sagen: "Hier ist diese schreibgeschützte Sammlung, in der Elemente einzigartig sind".

Offensichtlich wird es von vielen Menschen benötigt:

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
Einige Kerle in Kommentaren: http://blogs.msdn.com/b/bclteam/archive/2013/03/06/update-to-immutable-collections.aspx

Ich fand diese Diskussion, als ich an einem realen Problem arbeitete, bei dem ich die Schlüsselsammlung eines Wörterbuchs für schreibgeschützte Mengenoperationen verwenden wollte. Um diesen Fall zu unterstützen, ist hier die API, die ich vorschlage.

Bearbeiten

Begründung

Vorgeschlagene API

 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);
         }
     }
 }

Offene Fragen

  • Ist das Hinzufügen dieser Methoden zu Dictionary<TKey, TValue>.KeyCollection angesichts der Kosten für die Codegröße für einen häufig instanziierten generischen Typ akzeptabel? Siehe hier .
  • Sollte IReadOnlySet<T> in anderen Dictionary KeyCollection s wie SortedDictionary oder ImmutableDictionary implementiert werden?

Aktualisierung

  • Entfernt TryGetValue , da es nicht in ist ISet<T> und als solche verhindern würde möglicherweise jemals Rebasing ISet<T> umzusetzen IReadOnlySet<T> .
  • ReadOnlySet<T> Klasse hinzugefügt, die ReadOnlyCollection<T> ähnlich ist.
api-approved area-System.Collections up-for-grabs

Hilfreichster Kommentar

Vorgeschlagenes API-Design:

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);
}

Dies basiert auf der ISet<> API (außer natürlich Mutationsmethoden).

Schade, dass Comparer hier nicht reinpasst, aber dann legen weder ISet<> noch IReadOnlyDictionary<> Vergleiche offen, also ist es jetzt zu spät, um das Problem zu beheben.

Alle 104 Kommentare

Wäre es nicht schön, wenn wir nur ein Sprachkonstrukt hätten, um die Dinge unveränderlich zu machen? Dann müssten wir diese magischen Schnittstellen nicht haben.

@whoisj Welche Sprache? Die CLR hat Dutzende davon.

Selbst als potenzielles Sprachmerkmal würde es eine Metadatendarstellung erfordern. Für diesen Fall ist eine Marker-Schnittstelle (oder Verhaltensschnittstelle) so gut wie jede andere. Der Versuch, die Unveränderlichkeit eines Sammlungstyps durch einen neuen Metadateneintrag zu vermitteln, scheint nicht angemessen, da die CLR keine Annahmen darüber treffen sollte, wie eine beliebige Klasse intern funktioniert (und die CLR hat überhaupt kein Konzept von Sammlungsklassen außer für Arrays).

@whoisj Ich denke, dies wird zumindest für eine der zukünftigen C#-Versionen in Betracht gezogen. Diese Entscheidung hat jedoch keinen Einfluss auf die Notwendigkeit symmetrischer Schnittstellen über alle Sammlungen hinweg. Außerdem kann ich mir Szenarien vorstellen, in denen eine schreibgeschützte Liste veränderlicher Elemente nützlich sein könnte, zB in Spielen, bei denen sowohl Codequalität als auch Leistung wichtig sind.

Auch unveränderliche Kollektionen sind bereits verfügbar:

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

Um eine vollständig unveränderliche Sammlung zu erhalten, brauchen wir nur eine Möglichkeit, eine immutable T Sammlung zu definieren und dann damit eine Immutable...<T> Sammlung zu deklarieren.

@HaloFour wir waren schon einmal auf diesem Weg :smirk: aber ich glaube immer noch, dass die CLR eine Möglichkeit braucht, um zu sagen: "Hier ist ein Handle, liest daraus, aber alle Aktionen, die dazu führen, dass sie durchgeschrieben werden, werden fehlschlagen; oh und diese Unveränderlichkeit ist ansteckend, sodass jedes Handle, das vom unveränderlichen Handle erreicht wird, ebenfalls unveränderlich ist (einschließlich this )".

@dsaf absolut! In einer anderen Ausgabe schlug ich vor, dass wir einen beschreibbaren Anti-Begriff für readdonly haben, um die Verwendung einer readonly-Sammlung von beschreibbaren Elementen zu ermöglichen. Etwas in der Art von readonly Bag<writable Element> .

Ich schlug vor, dass jede Referenz, die mit einem & markiert ist, vom Compiler als unveränderlich behandelt wird. Ich bin immer noch der Meinung, dass es nur eine Überprüfung der Kompilierungszeit sein muss und notwendigerweise von der CLR selbst erzwungen wird, da ich hauptsächlich nach einer Überprüfung der Logik zur Kompilierung und nicht nach Laufzeitgarantien suche. Dies würde alle Referenzen abdecken, die von den Entwicklern unveränderlich sein wollten, jedoch pro Aufruf.

@whoisj Vielleicht, aber das ist ziemlich tangential und verwandelt diese Anfrage von etwas, das dsaf heute Nachmittag/PR verzweigen könnte, in etwas, das Aufwand für drei verschiedene Teams erfordert.

Sie behandeln dies auch als Compilerproblem. An diesem Punkt ist kein Compiler beteiligt (außer dem JIT-Compiler) und nur der Verifizierer kann versuchen, die Ausführung von "ungeeignetem" Code zu verhindern. Sogar die bestehenden Laufzeitmechanismen der Unveränderlichkeit, initonly Felder, können leicht umgangen werden, wenn die Verifikation übersprungen wird (oder durch Reflektion).

Ich stimme zu, dass es schön wäre, wenn die C#-Sprache und der Compiler "reine" Methoden besser unterstützen könnten. Das Attribut PureAttribute existiert bereits, wird aber sporadisch verwendet und es gibt wirklich keine Sprachunterstützung dafür. Und selbst wenn C# es durch Compilerfehler unterstützt hat (pure kann nur pure aufrufen usw.), ist es sehr leicht durch die Verwendung einer anderen Sprache zu umgehen. Aber diese Methoden müssen sich ankündigen und durchsetzen, da nach der Kompilierung in IL alle Wetten im Grunde genommen ausgeschlossen sind und keiner der Compiler die Ausführung einer vorhandenen Assembly ändern kann.

@HaloFour Messe.

Angenommen, wir haben keine allgemeine Möglichkeit, "reine" oder "konstante" Verweise zu unterstützen, dann nehme ich an, dass die vorgeschlagene Alternative die beste Alternative ist.

Wenn Sie es jetzt brauchen, hat meine Commons-Bibliothek (Commons.Collections https://github.com/yanggujun/commonsfornet/tree/master/src/Commons.Collections/Set ) die Readonly-Set-Unterstützung. Admin bitte diesen Beitrag löschen, wenn dies als Werbung gedacht ist... Mein Vorschlag ist, sich nach einer Open-Source-Implementierung umzusehen.

@yanggujun Vielen Dank für den Vorschlag, das scheint eine schöne Bibliothek zu sein, aber ich werde meine eigene rollen, um zusätzliche Abhängigkeiten zu vermeiden.

Mein Vorschlag ist, sich nach einer Open-Source-Implementierung umzusehen.

Dies ist eine Problemumgehung, grundlegende Schnittstellen wie IReadOnlySet sollten wirklich ein Teil von .NET Framework selbst sein.

Benötigt dies ein Speclet, um "bereit für die API-Überprüfung" zu werden?

Und wenn wir schon dabei sind, denken Sie darüber nach, es anders als "ReadOnly" zu benennen (siehe interessanter Beitrag: http://stackoverflow.com/questions/15262981/why-does-listt-implement-ireadonlylistt-in-net-4- 5)
"Lesbar" scheint in Ordnung zu sein.

@GiottoVerducci Nein. Ich würde es vorziehen, ein einheitliches perfekt ist. Es steht Ihnen jedoch frei, ein separates Problem zum Umbenennen vorhandener Schnittstellen anzusprechen.

Vorgeschlagenes API-Design:

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);
}

Dies basiert auf der ISet<> API (außer natürlich Mutationsmethoden).

Schade, dass Comparer hier nicht reinpasst, aber dann legen weder ISet<> noch IReadOnlyDictionary<> Vergleiche offen, also ist es jetzt zu spät, um das Problem zu beheben.

    bool Contains(T item);

Sollte dies nicht stattdessen in IReadOnlyCollection<T> da ICollection<T> Contains(T item) ?

Das Paket mit
Ich denke, dies ist ein ziemlich häufiger Anwendungsfall und sollte in Standardbibliotheken behandelt werden.

Gibt es hier noch mehr Arbeit an der API, wie das Tag andeutet? Ich würde mich freuen, etwas Zeit damit zu verbringen, wenn es hilfreich wäre und jemand aufzeigen kann, was benötigt wird.

Die vorgeschlagene API @ashmind sieht gut aus.

Kann ISet<T> dazu veranlasst werden, IReadOnlySet<T> ? Das ist nicht passiert IList<T> / IReadOnlyList<T> ?

Wenn nicht, dann denke ich, dass die anderen zu berücksichtigenden Änderungen darin bestehen, IReadOnlySet<T> zur Schnittstellenliste für alle ISet<T> Implementierungen in corefx hinzuzufügen, einschließlich HashSet<T> , SortedSet<T> und deren unveränderliche Gegenstücke in System.Collections.Immutable .

Ich muss @GiottoVerducci zustimmen. Die Verwendung eines Namens wie IReadOnlySet<T> deklariert nicht die Vertragsfunktionen, sondern die Vertragsbeschränkungen. Es ist verwirrend, denselben Vertrag dann in Kombination mit einem anderen zu verwenden, der diesen Beschränkungen widerspricht. Ich glaube, dass der Vertragsname eine positive Aussage zu dem beschreiben sollte, was der Implementierer unterstützt. Ein Name wie IReadableSet<T> ist zugegebenermaßen nicht großartig, aber er beschreibt zumindest besser, was der Implementierer tut.

@HaloFour Ich stimme im Prinzip zu, aber wir haben jetzt die gleiche Situation mit IReadOnlyList<T> . Die Beibehaltung der Konsistenz übertrumpft hier die Steigerung der Präzision, IMHO.

@drewnoakes

Ich verstehe, und Konsistenz ist wichtig. Ich denke, das beantwortet auch, warum ISet<T> IReadOnlySet<T> nicht erweitern sollte.

Die Beibehaltung der Konsistenz übertrumpft hier die Steigerung der Präzision, IMHO.

Ich denke, das beantwortet auch, warum ISet<T> IReadOnlySet<T> nicht erweitern sollte.

Ich glaube, du verfehlst den Punkt. Aus diesem Grund sollten IList<T> , ICollection<T> , IDictionary<TKey, TValue> zusätzlich zu ISet<T> auch behoben werden, um schreibgeschützte Ansichtsschnittstellen zu implementieren. Ansonsten müssen alle weiterhin verwirrt sein, wenn sie das wenig intuitive Design des BCL umgehen .

@binki

Ich bin nicht anderer Meinung. Was ich daran nicht mag, ist, dass ein Vertrag, der das Nur-Lese-Verhalten festlegt, durch einen Vertrag erweitert wird, der das Lesen-_Schreiben_-Verhalten festlegt. Die Namensgebung ist falsch und die Zusammensetzung ist falsch. Aber hier sind wir. Ich würde gerne dafür stimmen, beides zu ändern, aber ich bezweifle, dass dies auf dem Tisch liegt.

@HaloFour

Wenn Sie eine Schnittstelle in etwas hineinbekommen, ist es eine _Ansicht_ in etwas. Die Ansicht selbst _ist_ schreibgeschützt. Angenommen, Sie haben typsicheren Code geschrieben und werden das Upcasting nicht umgehen, wenn Sie etwas erhalten, das schreibgeschützt ist, ist es in jeder Hinsicht schreibgeschützt. Das ist keine Garantie dafür, dass sich die Daten nicht ändern. Es ist wie das Öffnen einer Datei schreibgeschützt. Eine schreibgeschützt geöffnete Datei kann von einem anderen Prozess mutiert werden. Oder wie der schreibgeschützte Zugriff auf Seiten einer Website, auf der ein Administrator eine Lese-Schreib-Ansicht auf die Daten hat und sie unter Ihnen ändern kann.

Ich bin mir nicht sicher, warum schreibgeschützt hier als der falsche Begriff angesehen wird. Schreibgeschützt bedeutet _nicht_ unveränderlich. Es gibt ein ganzes Nuget-Paket/eine andere API (wo das Hinzufügen/Entfernen ein neues Objekt generiert und die aktuelle Instanz garantiert niemals mutiert – also unveränderlich ist) dafür, wenn Sie dies benötigen.

Ich dachte an etwas ähnliches. "Nur lesen" in .NET ist auch eine ziemlich schwache Garantie für Felder. Bei einer Überarbeitung wäre das alles sicher sinnvoller. Im Moment lohnt es sich, pragmatisch zu sein.

Wenn also eine Methode ein IReadOnlySomething<T> akzeptiert, können Sie im Allgemeinen davon ausgehen, dass sie es nicht ändert. Es gibt keine Garantie dafür, dass die empfangende Methode die Referenz nicht hochwandelt, und es gibt auch keine Garantie dafür, dass sich die Implementierung der Schnittstelle beim Zugriff nicht intern selbst ändert.

In C++ schwächt const_cast die Garantien von const , was schade ist (besonders heutzutage mit dem mutable Modifikator), aber in der Praxis ändert es nicht die Nützlichkeit const ist ein Feature. Sie müssen nur wissen, womit Sie es zu tun haben.

@binki macht eine gute Unterscheidung. _Unveränderlich_ im Namen impliziert eine harte Garantie für Stabilität im Laufe der Zeit für alle Beteiligten.

Hat jemand eine maßgebliche Quelle, warum IList<T> IReadOnlyList<T> nicht verlängert?

@binki

Eine Schnittstelle ist keine Ansicht, sondern ein Vertrag. Dieser Vertrag erklärt die Fähigkeiten des Implementierers. Wenn der Implementierer diese Funktionen nicht tatsächlich implementiert, würde ich dies als Vertragsverletzung betrachten. Diese List<T> Klasse behauptet, dass sie "ein" IReadOnlyList<T> , aber das ist es nicht. Diese Fähigkeit fehlt ihm.

Zu diesem Thema gibt es mehrere Denkschulen. Ich gehöre eindeutig zu der Schule, in der die Schnittstellenvererbung strenger "ist-a"-Beziehungen zwischen Typen folgt. Ich unterstütze sicherlich einen differenzierteren Ansatz für die Komposition mit Schnittstellen und denke, dass List<T> und seine Verwandten wahrscheinlich von der Implementierung von 3-4 zusätzlichen Schnittstellen (Lesen, Schreiben, Anhängen usw.) profitieren könnten Der Name einer Schnittstelle sollte beschreiben, was ein Typ _kann_, nicht was er _nicht_ tun kann. Negative Fähigkeitszusicherungen machen für Verträge wenig Sinn.

@Drewnoakes

Im Moment lohnt es sich, pragmatisch zu sein.

Ich stimme zu. Wir sind, wo wir sind. Wenn IList<T> geändert werden soll, um IReadOnlyList<T> dann ist es sinnvoll, dass ISet<T> geändert wird, um IReadOnlySet<T> usw.

Ist es zu überflüssig, auch auf IReadableX<T> , IWritableX<T> usw. Schnittstellen zu drängen, um neben IReadOnlyX<T> zu leben?

Hat jemand eine maßgebliche Quelle, warum IList<T> IReadOnlyList<T> nicht erweitert?

Anscheinend wäre es eine ABI-brechende Änderung beim Laden von Assemblys, die mit älteren .net-Frameworks kompiliert wurden. Denn bei der Implementierung einer Schnittstelle generieren die meisten Compiler automatisch explizite Schnittstellenimplementierungen, wenn der Quellcode auf einer impliziten Schnittstellenimplementierung beruht, wenn Sie Ihre Klasse mit IList<T> Implementierung gegen eine BCL kompiliert haben, die nicht über IList<T> Wenn Sie IReadOnlyList<T> implementieren, erstellt der Compiler nicht automatisch die expliziten IReadOnlyList<T> Implementierungen. Wenn ich das richtig lese: http://stackoverflow.com/a/35940240/429091

@HaloFour Da List<> und HashSet<> ICollection<> und IReadOnlyCollection<> HashSet<> implementieren, haben wir bereits einen Pfad angenommen, bei dem sich IReadOnly auf den Zugriff bezieht und nicht Fähigkeit. Auf dieser Grundlage macht es absolut Sinn, IAnything IReadOnlyAnything . Ich stimme zu, dass IReadable besser ist als IReadOnly aber an diesem Punkt versteht jeder IReadOnly zu _bedeuten_ IReadable und verwendet es als solches. Tatsächlich verewige ich das absichtlich in meiner eigenen Codebasis, weil es meiner Meinung nach mehr kognitive Belastung als alles andere bedeutet, zwei Denkweisen zu haben.

Wir bleiben bei dem Namen, aber das Konzept dahinter ist mächtig genug, dass ich mir wirklich wünschte, dass alle Schnittstellen IReadOnly Zukunft erweitern könnten, genau wie wir es mit konkreten Klassen tun.

@ashmind Ich finde es perfekt, dass keine der Methoden Vergleiche verwendet. In Sets und Wörterbüchern können Vergleiche nicht einfach ausgetauscht werden, da sie die Struktur des gesamten Objekts bestimmen. Außerdem wäre es nicht sinnvoll, einen Vergleich an ein CaseInsensitiveStringCollection oder eine Sammlung zu übergeben, die einen bestimmten Vergleich impliziert.

(Im Fall einer seltsamen Sammlung, die Contains(T, IEqualityComparer<T>) effizienter implementiert als die bereits verfügbare Erweiterungsmethode , wäre es wahrscheinlich eine einmalige Klassenmethode. Es ist schwer vorstellbar, dass Contains(T, IEqualityComparer<T>) üblich ist genug, um in einer spezialisierten Benutzeroberfläche zu landen, aber selbst das kann nicht verhindert werden.)

@jnm2

Ich finde es perfekt, dass keine der Methoden Vergleiche braucht.

Nur um es klarzustellen, ich wollte sagen, dass es Vergleicher aussetzen sollte, nicht einen nehmen. Da jede Menge oder jedes Wörterbuch einen Gleichheitsalgorithmus haben muss, könnte dies auf der Schnittstelle offengelegt worden sein. Aber ich erinnere mich jetzt nicht mehr an meinen Anwendungsfall dafür - so etwas wie das Erstellen eines Sets mit demselben Vergleicher wie in einem extern bereitgestellten.

Während diese Diskussion viele interessante Punkte hervorbringt, scheint sie weit von dem einfachen und offensichtlichen Vorschlag abzuweichen, der diesen Thread gestartet hat. Und das ist entmutigend, denn ich würde wirklich gerne sehen, dass dieses Problem angegangen wird.

Wie das OP sagte, ist das Versäumnis, die Parität zwischen den Sammlungstypen aufrechtzuerhalten, wenn IReadOnlyList ohne IReadOnlySet hinzugefügt wurde, bedauerlich und viele Leute haben ihre eigenen Versionen der IReadOnlySet-Schnittstelle als Problemumgehung implementiert (mein eigenes Team hat eine ähnliche Problemumgehung). Diese Problemumgehungsschnittstellen sind nicht ideal, da die corefx-Klassen sie nicht implementieren können. Dies ist der Hauptgrund, dies im Framework bereitzustellen: Wenn ich ein HashSet habe, möchte ich es als IReadOnlySet verwenden können, ohne das bereits vorhandene Objekt zu kopieren oder zu umhüllen. Zumindest für die Performance ist dies oft wünschenswert.

Der Name der Schnittstelle sollte eindeutig IReadOnlySet sein. Konsistenz übertrumpft alle Bedenken mit den IReadOnlyXXX-Namen. Dieses Schiff ist los gefahren.

Keine der vorhandenen Schnittstellen (IReadOnlyCollection) kann geändert werden. Die Back-Compat-Anforderungen für .NET lassen solche Änderungen nicht zu. Es ist bedauerlich, dass Vergleicher nicht in den vorhandenen IReadOnlyXXX-Schnittstellen verfügbar sind (ich bin auch darauf gestoßen), aber das Schiff ist wieder gesegelt.

Aus praktischer Sicht scheint nur noch die Frage zwischen diesen beiden möglichen Definitionen der Schnittstelle zu bleiben.

Zuvor vorgeschlagen von @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);
}

Minimaler Vorschlag:

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

Persönlich bevorzuge ich diesen minimalen Vorschlag, da die anderen Methoden abgeleitet werden können; idealerweise gäbe es eine Standardimplementierung dieser als Erweiterungsmethoden über die IReadOnlySet-Schnittstelle, sodass die Implementierer von IReadOnlySet sie nicht bereitstellen müssen. Ich glaube auch, dass dieser minimale Vorschlag eher mit den anderen minimalen IReadOnlyXXX-Schnittstellen übereinstimmt.

@aaron-meyers Die einzige Sorge, die ich hätte, ist, dass IsSubsetOf und Freunde nicht _effizient_ aus Contains abgeleitet werden können. Wenn es sich beispielsweise um zwei Hash-Tabellen handelt, zwingt die Verwendung von Contains die Implementierung dazu, verschachtelte Schleifen anstelle von Hash-Abgleichen zu verwenden.

Vielleicht könnte eine neue Schnittstelle, IComparableSet<T> die Mengenoperationen enthalten.

Wir haben bereits Erweiterungsmethoden auf IEnumerable<T> für einige Mengenoperationen.

@jnm2 Die Implementierung dieser von HashSet verwendeten Methoden erfordert nur Enthält und das Aufzählen der anderen Auflistung (die IReadOnlySet durch die Vererbung von IReadOnlyCollection erhalten würde). Es erfordert jedoch zu wissen, dass der andere Satz denselben Vergleicher verwendet. Vielleicht lohnt es sich, die Comparer-Eigenschaft zu IReadOnlySet hinzuzufügen, damit diese Operationen effizient in den Erweiterungsmethoden implementiert werden können. Es ist bedauerlich, dass IReadOnlyDictionary den KeyComparer nicht ebenfalls verfügbar macht, aber es kann sich lohnen, dies zu IReadOnlySet hinzuzufügen, auch wenn es nicht ganz konsistent ist. Es gibt gute Gründe, warum es überhaupt in IReadOnlyDictionary hätte aufgenommen werden sollen, wie hier beschrieben.

Der geänderte Vorschlag wäre:

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

Alternativ könnte sich der Vergleicher auf einer separaten Schnittstelle befinden und die Implementierungen der Erweiterungsmethoden der Mengenoperationen würden nur dann die effiziente Route verwenden, wenn beide Objekte die Schnittstelle implementieren und die gleichen Vergleicher aufweisen. Der gleiche Ansatz könnte für IReadOnlyDictionary angewendet werden (eigentlich verwenden sie nur die gleiche Schnittstelle). Etwas wie ISetComparable. Oder wenn du von aber anstatt die Mengenoperatoren zu definieren, definiert es nur den Vergleich:

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

In diesem Fall geht IReadOnlySet zurück, um nur Enthält zu definieren:

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;
    }

}

Wir können diesen Ansatz verwenden.
Die Verwendung sieht so aus;

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

Viele Anforderungen erfüllt, IReadOnlySetist minimalistisch. Aber GetComparer now Methode, nicht Eigenschaft. Aber es ist ein guter Kompromiss.

    /// <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);
    }
}

Gerade diese Verträge mit Injektionshelfern auf dem Nuget veröffentlicht (https://www.nuget.org/packages/ClrCoder.Collections.ReadOnlySet).
Fühlen Sie sich frei, es zu verwenden und Probleme hier einzureichen: https://github.com/dmitriyse/ClrCoder/issues
Wenn es ein wenig populär wird (wahrscheinlich nach einigen Verfeinerungsrunden), können wir diese Verbesserung dem CoreFX-Team vorschlagen.

@terrajobst ist es in Ordnung, dass Compat für vorhandene Klassen neue Schnittstellen implementiert?

@safern es gibt einen Präzedenzfall in List<T> IReadOnly hinzugefügt bekommen.

Ist es also geplant, die nächsten .NET Framework-Releases hinzuzufügen?

Wann wird diese Arbeit landen? Irgendwelche Zeitpläne?

https://www.nuget.org/packages/System.Collections.Immutable/
Vollständiger Satz unveränderlicher Sammlungen)

OK. Das ist schon viel zu lange hier. Ich brauche es sehr und würde gerne einen Weg vorschlagen, wie ich das dann angehen kann.

Anstatt all dies zu entlarven:

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);

und die Kunden zu zwingen, diese Member zu implementieren, fügen wir einfach das hinzu, was am wichtigsten ist:

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

und nichts weiter. Weitere APIs können in Zukunft jederzeit hinzugefügt werden, wenn ein solcher Bedarf besteht. Aber es kann nicht entfernt werden, daher dieser Vorschlag.

Wir würden diese Schnittstelle dann zur Liste der Schnittstellen hinzufügen, die um ISet<T> .

Aus der Dokumentation

ISet<T> : Diese Schnittstelle bietet Methoden zum Implementieren von Sets, bei denen es sich um Sammlungen mit eindeutigen Elementen und spezifischen Operationen handelt. Das HashSetund sortiertes SetCollections implementieren diese Schnittstelle.

Aus dem Code

Die ISet<T> Schnittstelle hat bereits unsere bool Contains<T>(T item); Methode definiert über ICollection<T> . Es hat auch int Count { get; } über ICollection<T> .

Wäre also:

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

Triviale Veränderung, wenig zu besprechen, großer Nutzen.

Bitte lassen Sie es uns geschehen. Lassen Sie mich wissen, ob ein solcher Pull-Request akzeptiert und zusammengeführt wird. Dann kann ich es erstellen.

@karelz @terrajobst @safen @ianhays

Ich fand diese Diskussion, als ich an einem realen Problem arbeitete, bei dem ich die Schlüsselsammlung eines Wörterbuchs für schreibgeschützte Mengenoperationen verwenden wollte. Um diesen Fall zu unterstützen, ist hier die API, die ich vorschlage.

Begründung

Vorgeschlagene API

 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);
         }
     }
 }

Offene Fragen

  • Ist das Hinzufügen dieser Methoden zu Dictionary<TKey, TValue>.KeyCollection angesichts der Kosten für die Codegröße für einen häufig instanziierten generischen Typ akzeptabel? Siehe hier .
  • Sollte IReadOnlySet<T> in anderen Dictionary KeyCollection s wie SortedDictionary oder ImmutableDictionary implementiert werden?

Aktualisierung

  • TryGetValue da es nicht in ISet<T> und als solches verhindern würde, dass möglicherweise jemals ISet<T> umbasiert wird, um IReadOnlySet<T> zu implementieren.
  • ReadOnlySet<T> Klasse hinzugefügt, die ReadOnlyCollection<T> ähnlich ist.

@TylerBrinkley danke für das Hinzufügen eines API-Vorschlags im Thread. Würde es Ihnen etwas ausmachen, Begründungen und Anwendungsfälle hinzuzufügen? Damit ich es zur Überprüfung vormerken und die API-Experten entscheiden lassen kann?

@ TylerBrinkley vergessen Sie nicht, die EqualityComparer-Eigenschaft in das IReadOnlySet aufzunehmenSchnittstelle. Sie werden derzeit in Wörterbüchern und Sets in CoreFX vermisst, aber es ist ein Problem.

@dmitriyse was nützt eine Getter-only IEqualityComparer<T> Eigenschaft? Was würdest du damit machen?

Wörterbücher und Sets sollten ihre EqualityComparer melden, um das korrekte Klonen von Sammlungen zu ermöglichen.
Leider habe ich vergessen, wo dieses Thema diskutiert wurde.

Wenn Sie klonen, würden Sie nicht mit einem konkreten Typ arbeiten? Warum sollte die Schnittstelle IEqualityComparer<T> ?

Wenn Sie beispielsweise mit dem Gleichheitsvergleich für Zeichenfolgen und Groß-/Kleinschreibung festgelegt haben, erhalten Sie eine Ausnahme beim Erstellen eines neuen HashSets, ohne den richtigen EqualityComparer anzugeben. Es gibt Fälle, in denen Sie nicht wissen können, welcher EqualityComparer in der angegebenen Menge verwendet wird.

Es ist nicht nur das Klonen. Ich denke, das weitaus häufigere Szenario besteht darin, zwei Sätze zu vergleichen. Ich muss wissen, dass beide denselben Vergleich verwenden, um einen optimalen Vergleich mit Enthält zu implementieren. Ich denke, in diesem Thread gibt es ein Beispiel.

Das heißt, ich hätte IReadOnlySet lieber nur mit der Methode Contains als überhaupt nicht. Es wäre schön, einen Set-Vergleich generisch implementieren zu können, aber nicht so häufig, wie nur eine schreibgeschützte Referenz auf ein Set zu benötigen.

Holen Sie sich Outlook für iOS https://aka.ms/o0ukef


Von: Tyler Brinkley [email protected]
Gesendet: Donnerstag, 10. Mai 2018 06:21:52
An: dotnet/corefx
CC: Aaron Meyers; Erwähnen
Betreff: Re: [dotnet/corefx] Bitte fügen Sie die Schnittstelle IReadOnlySet hinzu und lassen Sie HashSet, SortedSet implementieren (#1973)

Wenn Sie klonen, würden Sie nicht mit einem konkreten Typ arbeiten? Warum sollte die Schnittstelle den IEqualityComparer unterstützen.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an 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 u = 0 reserviert oder den Faden stumm 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 & reserviert = 0 .

Ich stimme zu – Sie können nur feststellen, welche Arten von Duplikaten Sie in der Menge finden (Groß-/Kleinschreibung beachten, Groß-/Kleinschreibung nicht beachten usw.), indem Sie den Vergleich anzeigen.

Ich fange an zu denken, dass mein Vorschlag nicht akzeptiert würde, da das Hinzufügen all dieser Methoden zu Dictionary<TKey, TValue>.KeyCollection erhebliche Kosten für die Codegröße verursachen würde. Weitere Informationen zum Hinzufügen einer neuen API zu häufig instanziierten generischen Typen finden Sie in dieser

Ich glaube nicht, dass Sie IReadOnlySet .

SortedSet benötigt ein IComparer , während HashSet ein IEqualityComparer .

Guter Punkt, der Vergleicher könnte als Implementierungsdetail betrachtet werden, das nicht in eine allgemeine Schnittstelle gehört.

Holen Sie sich Outlook für iOS https://aka.ms/o0ukef


Von: Cory Nelson [email protected]
Gesendet: Donnerstag, 10. Mai 2018 17:04:06
An: dotnet/corefx
CC: Aaron Meyers; Erwähnen
Betreff: Re: [dotnet/corefx] Bitte fügen Sie die Schnittstelle IReadOnlySet hinzu und lassen Sie HashSet, SortedSet implementieren (#1973)

Ich glaube nicht, dass Sie in der Lage sein werden, einen Vergleich auf IReadOnlySet zu setzen.

SortedSet benötigt einen IComparer, während HashSet einen IEqualityComparer benötigt.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an 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 u = 0 reserviert oder den Faden stumm 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 & reserviert = 0 .

Das Hinzufügen von IUnorderedSet und IOrderedSet Schnittstellen mag einen gewissen Wert haben, aber ich möchte nicht, dass dies das Durchschieben von IReadOnlySet entgleisen lässt.

Ich mag den Vorschlag von @pgolebiowski , würde diese Schnittstelle jedoch noch einfacher machen.

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

Dies ist dann auch für unzählbare Mengen wie Intervalle (real) geeignet. Dazwischen und IReadOnlySet könnten Sie möglicherweise ein paar andere Schnittstellen wie ICountableSet (auch bekannt als IEnumerableSet ), IUnorderedSet und IOrderedSet .

Aber stimmen Sie zu, dass nur IReadOnlySet eine große Verbesserung gegenüber dem, was derzeit verfügbar ist, darstellen würden.

+1 für @NickRedwood

Es ist jetzt seit über 3 Jahren geöffnet. Bitte führen Sie dies durch, indem Sie den Ansatz ändern. Die Einführung der von @NickRedwood erwähnten

@TylerBrinkley @safern @karelz @terrajobst

Ich sehe nicht wirklich viel Nutzen aus einer IReadOnlySet<T> Schnittstelle mit nur einer Contains Methode. Obwohl diese Methode nützlich ist, ist sie bereits in der ICollection<T> Schnittstelle enthalten, die auch eine IsReadOnly Eigenschaft hat, die anzeigen soll, ob die Modifikationsmethoden von ICollection<T> unterstützt werden. Implementieren Sie einfach ICollection<T> und lassen Sie IsReadOnly true . Die einzige andere Funktionalität, von der erwartet wird, dass sie unterstützt, wäre eine Count Eigenschaft, die aufzählbar ist, und eine CopyTo Methode, die nicht allzu einschränkend zu sein scheint.

Ich würde mich sehr freuen, wenn das vorgeschlagene IReadOnlySet<T> basierend auf IReadOnlyCollection<T> implementiert würde - es wäre eine große Verbesserung gegenüber dem, was derzeit verfügbar ist. ( @TylerBrinkley - nehmen Sie an, Sie meinten IReadOnlyCollection<T> statt ICollection<T> .

Ich vermute, dass ich mich auf der Seite der einfacheren Interfaces irre, und wenn es eines für Set gäbe, dann wäre es ein Single-Method-Interface mit einer Contains Methode. Es hat eine gute mathematische Basis und ist sehr sauber. Beim Nachrüsten in bestehende Klassen akzeptiere ich jedoch, dass weniger nachrüsten besser ist, und wenn nur eine Schnittstelle nachgerüstet werden soll, dann sollte es IReadOnlySet<T> basierend auf IReadOnlyCollection<T> sein.

IReadOnlyCollection<> hat kein .Contains weil dies verhindern würde, dass es kovariant ist.

Guter Punkt und ich habe meinen früheren Beitrag korrigiert, um kontravariant zu sein. IReadOnlySet<T> müsste invariant sein, es sei denn, der Ansatz, der zu Beginn des Threads gepostet wurde, wird verwendet, und ich bin mir da nicht sicher.

@ TylerBrinkley persönlich bin ich kein Fan der Eigenschaft IsReadOnly , ich möchte diese Informationen lieber zur Kompilierzeit als zur Laufzeit kennen. Wenn Sie sich auf die IReadOnlyCollection<T> Schnittstelle beziehen, denke ich immer noch, dass es für den Anrufer oder den Angerufenen nützlich wäre zu wissen, dass die Suche schnell statt einer möglichen Iteration durch eine Sammlung erfolgt, obwohl ich nicht sicher bin, ob ein "Satz" impliziert dies.

Nur eine Contains Methode zu haben, definiert nicht, was ein Set tun soll, sondern nur was ein Collection tun soll. Ich meine, Contains ist nicht einmal ein Mitglied von ISet sondern von ICollection denen ISet erbt. Die anderen Mitglieder von ISet sollten definieren, was ein Set tun soll. Ich verstehe, dass die meisten Leute wahrscheinlich Sets ausschließlich für die Methode Contains verwenden, aber es gibt so viel mehr als nur das.

@TylerBrinkley Aber ist es an dieser Stelle überhaupt möglich, IReadOnlyCollection<T>.Contains(T) zu definieren? Ich nahm an, nicht. Deshalb besteht die einzige Möglichkeit darin, IReadOnlySet<T> einzuführen und bei der Einführung sicherzustellen, dass IReadOnlySet<T>.Contains(T) deklariert ist.

@jnm2 sagt:

IReadOnlyCollection<> hat kein .Contains weil dies verhindern würde, dass es kovariant ist.

Das scheint mir momentan das größte Problem zu sein. Es wäre seltsam, wenn IReadOnlySet<T> invariant wäre, wenn IReadOnlyCollection<T> kovariant ist. Es scheint, als ob viele existierende IReadOnly* Interfaces sorgfältig als out T wobei die beschreibbaren Interfaces notwendigerweise invariant sind. Die meisten von uns möchten, dass IReadOnlySet<T> zumindest eine Contains(T) Methode deklariert, und dennoch würde dies verhindern, dass sie kovariant ist.

Wenn ICollection wirklich der richtige Ort ist, um Contains(T) zu definieren, ist das dann möglich? Vielleicht IReadOnlyProbableCollection<T>.Contains(T) was invariant und von Collection<T> und HashSet<T> implementiert wäre?

Ich denke, @NickRedwoods Vorschlag von ISetCharacteristic<T> scheint vielleicht sauberer zu sein. Das hat sogar den Vorteil, dass Sie Count nicht implementieren können, was nützlich sein könnte.

Ich persönlich bin kein Fan der IsReadOnly-Eigenschaft, ich möchte diese Informationen lieber zur Kompilierzeit als zur Laufzeit kennen.

Dieser Mann spricht gut, schenk ihm mehr Wein ein.

@binki Ich stimme zu, dass es eine Contains Methode für IReadOnlySet<T> da IReadOnlyCollection<T> keine enthalten hat, aber ich denke auch, dass alle anderen nicht mutierenden ISet<T> -Methoden sollten enthalten sein.

Neben dem oben erwähnten Anwendungsfall Dictionary.KeyCollection , welche anderen Anwendungsfälle können Sie sich für das Hinzufügen dieser Schnittstelle einfallen lassen?

OK, sieht aus wie das API-Design:

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

Es ist der einzige gemeinsame Teil des Designs, dem alle zustimmen können.

ICH SAGE, JETZT BEENDEN WIR DAS DESIGN HIER. Wenn Sie es in Zukunft erweitern möchten, tun Sie dies in einer anderen Ausgabe. Dies ist die wichtigste Funktionalität, die in prod.

Wer ist für, wer ist dagegen?

Ich mag den Vorschlag von @ashmind . Es wäre großartig, wenn Dictionary.Keys diese Schnittstelle implementieren könnte, da sie technisch IReadOnlySet<TKey> .

Könnten Sie dies bitte als api-ready-for-review markieren? Ich habe gerade festgestellt, dass ich diese Schnittstelle wieder benötige und sie immer noch nicht da ist.

Warum ist das noch nicht implementiert?

[EDIT] Entschuldigung - meine ursprüngliche Antwort wurde mit einer anderen API verwechselt. Ich habe keine starke Meinung zu diesem, Text bearbeitet. Entschuldigen Sie das Durcheinander!

Warum ist das noch nicht implementiert?

Die API wurde von Gebietsbesitzern noch nicht beachtet - @safen. Ich bin mir nicht sicher, wie hoch es auf der Prioritätenliste der Sammlungen ist. Die Upvote-Zahl ist ziemlich hoch.
Die Wahrheit ist, dass wir jetzt für .NET Core 3.0 gesperrt sind, sodass es mindestens auf die nächste Version warten muss, da es für 3.0 nicht kritisch ist.

Ich bin auch überrascht, dass dies nicht out of the box geliefert wird.

Unveränderliche Datenstrukturen können eine massive Hilfe zur Verbesserung der Code-Wartbarkeit sein, aber sie sind ohne eine Schnittstelle in den Standardbibliotheken, die Semantik spezifiziert und die Zusammenarbeit verschiedener Implementierungen ermöglicht, schwer zu verwenden.

Ohne dies muss jede Bibliothek, die einen unveränderlichen Satz als Parameter/Rückgabewert verwenden möchte, entweder auf IEnumerable zurückgreifen, was weniger effizient und semantisch falsch ist, oder auf ihre eigenen Schnittstellen/Klassen in ihren Signaturen, die mit denen anderer nicht kompatibel sind.

Ich bin gespannt, ob es hierfür Problemumgehungen gibt, die in Bezug auf Contains Lookups effizient sind und die Angabe des konkreten Typs des Parameters/Rückgabewerts vermeiden. Das Beste, was ich mir spontan vorstellen könnte, wäre, IEnumerable in der Signatur zu verwenden und ReadOnlyDictionary.Keys zu übergeben, aber das scheint ein bisschen unangenehm zu sein, besonders in einer Bibliothek. Da Enumerable.Contains in Linq die Collection-Implementierung von Contains , sollte dies effizient und gleichzeitig kompatibel sein, kommuniziert jedoch nicht die Absicht, die zur Verwendung weniger performanter Implementierungen führen könnte.

@adamt06 Du suchst nach ISet<T> .

@scalablecory Sie haben Recht, mit einer unveränderlichen Implementierung, und es gibt eine: ImmutableHashSet<T>

Weiß/versteht jemand warum ICollectionerweitert IReadOnlyCollection nicht??
Ich dachte, das hängt ein wenig mit diesem Thread zusammen. Anstatt dass ich einen neuen Thread eröffne. Vielleicht verstehe ich nur etwas falsch.

Ein weiterer Gedanke, aber völlig vom Thema abgekommen, Und warum hat ReaOnlyCollection nicht:

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

Oh, ich sehe, Sie meinten, IReadOnlyCollection hat sie nicht. Das liegt daran, dass die Schnittstelle sonst nicht kovariant sein könnte .

Ich bin mir nicht sicher, aber es hat wahrscheinlich etwas mit der expliziten Schnittstellenimplementierung zu tun, die Probleme mit der Abwärtskompatibilität verursacht. Ich kann sehen, warum das Aktualisieren der vorhandenen Schnittstellen, um von anderen Schnittstellen zu erben, ein Problem darstellt, aber ich sehe nicht, warum das Erstellen einer neuen Schnittstelle IReadOnlySet<T> und die Implementierung von HashSet ein Problem wäre.

OK. Ist aber auch noch nicht zu sehen ICollectionsollte nicht:
c# ICollection<T> : IReadOnlyCollection<out T>
Warum sollte eine Read/Write-Sammlung nicht auch eine Erweiterung einer Readonly-Sammlung sein?

@generik0

OK. Aber es ist auch nicht zu sehen, dass ICollection nicht sein sollte:

ICollection<out T> : IReadOnlyCollection<out T>

Warum sollte eine Read/Write-Sammlung nicht auch eine Erweiterung einer Readonly-Sammlung sein?

Beachten Sie zunächst, dass es unmöglich ist, ICollection<out T> zu deklarieren, da ICollection<T> ein Mitglied .Add(T item) .

Zweitens, beachten Sie, dass ICollection<T> in .net-2.0 und IReadOnlyCollection<out T> in .net-4.5 angezeigt wird . Wenn Sie Code kompilieren, der ICollection<T> implementiert und dann ICollection<T> geändert wird, um eine neue Schnittstelle zu implementieren, werden alle vorhandenen kompilierten Binärdateien zur Laufzeit unterbrochen, da alles, was nur ICollection<T> implementiert, keine Funktion hätte IReadOnlyCollection<T> Slots, die (möglicherweise automatisch) vom Compiler ausgefüllt werden. Dies ist die Kompatibilität Problem , das verhinderte ICollection<T> von der Implementierung IReadOnlyCollection<T> .

Ich glaube nicht, dass dies in dieser SO-Antwort eindeutig angesprochen wird, aber es gibt ein SO dafür: https://stackoverflow.com/a/14944400 .

@binki "ICollection" an ICollection gefixt, danke für den Hinweis auf meinen Tippfehler..
DotNetStandard i net461. Und hier sollte die Veränderung sein. Netzstandard 2+

ich werde wiederholen...
Warum sollte eine Read/Write-Sammlung nicht auch eine Erweiterung einer Readonly-Sammlung sein?

Und warum sollte man eine Sammlung zB in "ToArray()" umwandeln müssen, nur um weniger sichtbar zu machen?

Und natürlich können Sie in höheren Versionen neue Schnittstellen hinzufügen, ohne dass etwas kaputt geht. ICollection hat bereits die IReadOnlyCollection-Methoden implementiert. Notieren sollte brechen,... :-/

@generik0 Sieht so aus, als würde es die Quellkompatibilität nicht beeinträchtigen, woran Sie denken [hatte nicht gedacht; es würde], aber es würde die Binärkompatibilität unterbrechen, die mit IL-Tabellen funktioniert, nicht mit C#.

Ok @jnm2 danke.
Ich werde jetzt mit meinem Off-Topic-Gerede aufhören, denke nur, dass es schlechte Architektur ist. Danke an alle fürs Zuhören/Erklären :-)

@jnm2

@generik0 Es sieht so aus, als würde es die beeinträchtigen, an die Sie denken, aber es würde die Binärkompatibilität beeinträchtigen, die mit IL-Tabellen und nicht mit C# funktioniert.

Um es nicht zu sagen (sorry), es würde auch die Quellkompatibilität beeinträchtigen, wenn Ihre Quelle explizit ICollection<T> vor .net-4.5 implementiert hätte. Die Klasse würde aufgrund eines Fehlers beim expliziten Implementieren von IReadOnlyCollection<T>.Count fehlschlagen. Aber die Binärkompatibilität ist eine größere Sache, weil sie Sie daran hindern würde, eine breite Palette von .net-Versionen anzuvisieren (Sie müssten jedoch andere Binärdateien haben, um auf ≥.net-4.5 als <.net-2 ausgeführt zu werden, und Sie würden auf beide abzielen müssen, anstatt nur auf das ältere Framework abzuzielen).

Ich frage mich, ob ICollection<T> mit der Unterstützung der Standardschnittstellenimplementierung in C#8 IReadOnlyCollection<T> für .NET Core 3.0+ implementieren könnte.

Vielleicht wird eine Erweiterung benötigt, dann ist es die gleiche Sammlung. dh wenn Sie eine ienumerable oder eine collection zu einer Readonly-Collection benötigen ... Irgendwelche Ideen?

[bearbeitet von @jnm2 Kommentar]
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 Warum nicht enumerable as IReadOnlyCollection<T> ?? throw ... oder (IReadOnlyCollection<T>)enumerable anstatt diese Methode aufzurufen?

@jnm2 omg. Du hast recht. Manchmal sieht man nicht das klare Bild und macht die Dinge zu kompliziert!!!

Ich würde trotzdem die Nebenstelle anrufen, nur um die Einfachheit zu erhalten ;-)
Danke Kumpel....

Wie ist der Stand hier? Ich würde diese Schnittstelle wirklich gerne in der Standardbibliothek haben und HashSet<> implementieren lassen.

Es scheint unsere beste Wahl zu sein, noch eine Weile zu warten, bis der Shapes-Vorschlag (hoffentlich) umgesetzt wird. Dann könnten Sie eine beliebige Gruppe von Formen erstellen, um die gewünschten Set-Typen darzustellen, und Implementierungen bereitstellen, damit vorhandene Sets wie HashSet<T> entsprechen.

Dann könnte eine Gemeinschaftsbibliothek entstehen, die genau dies tut und alle Arten von Mengen abdeckt (intensional (Prädikate) und extensional (aufgelistet), geordnet, teilweise geordnet, ungeordnet, abzählbar, abzählbar unendlich, abzählbar unendlich, möglicherweise auch mathematische Bereiche wie Rational , Natürliche Zahlen usw.) als verschiedene Formen, mit allen Vereinigungs-, Schnitt- und Kardinalitätsmethoden, die für und zwischen diesen Mengen definiert sind.

Das klingt für mich nach 5 Jahren auf der Straße. Warum sollte eine einfache Änderung, die an einem Tag implementiert werden kann, auf ein 1000-mal größeres, noch nicht spezifiziertes Feature warten, das möglicherweise nicht einmal stattfindet?

Das klingt für mich nach 5 Jahren auf der Straße. Warum sollte eine einfache Änderung, die an einem Tag implementiert werden kann, auf ein 1000-mal größeres, noch nicht spezifiziertes Feature warten, das möglicherweise nicht einmal stattfindet?

Ich antworte nur auf den Mangel an Fortschritten bei IReadOnlySet<T> - dieses Thema ist immerhin schon 4 Jahre offen.

Der Microsoft-Weg: Die einfachsten und nützlichsten Dinge brauchen Jahrzehnte. Es ist umwerfend. 5 Jahre und Zählen.

Noch lustiger ist, dass sie es hier drin haben
https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.sdk.sfc.ireadonlyset-1

@terrajobst Gedanken?

  • Wir glauben im Allgemeinen, dass dies tatsächlich ein Loch in unserer Schnittstellenhierarchie ist. Wir empfehlen, sich darauf zu konzentrieren, nur die Schnittstelle hinzuzufügen und sie in den vorhandenen Set-Implementierungen zu implementieren. Wir können ISet<T> , IReadOnlySet<T> (aus dem gleichen Grund, aus dem wir es für die anderen veränderlichen Schnittstellen nicht tun konnten). Wir können zu einem späteren Zeitpunkt ein ReadOnlySet<T> hinzufügen. Wir sollten noch einmal überprüfen, dass IReadOnlySet<T> nur ISet<T> abzüglich der veränderlichen APIs ist.
  • Wir sollten auch IReadOnlySet<T> auf ImmutableSortedSet<T> und ImmutableHashSet<T> (und ihren Erbauern) implementieren.
  • Wir sollten nach anderen Implementierungen von 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> {
     }
 }

Tu es, ich wage dich!

@terrajobst

Wir können ISet<T> , IReadOnlySet<T> (aus dem gleichen Grund, aus dem wir es für die anderen veränderlichen Schnittstellen nicht tun konnten).

Ist dies auch mit Standardschnittstellenmethoden immer noch wahr? Bedeutet das, dass https://github.com/dotnet/corefx/issues/41409 geschlossen werden sollte?

@terrajobst

Wir können ISet<T> , IReadOnlySet<T> (aus dem gleichen Grund, aus dem wir es für die anderen veränderlichen Schnittstellen nicht tun konnten).

Ist dies auch mit Standardschnittstellenmethoden immer noch wahr? Bedeutet das, dass dotnet/corefx#41409 geschlossen werden sollte?

Wir haben dies besprochen. Früher dachten wir, dass DIMs funktionieren würden , aber als wir die Lösung durchgingen, kamen wir zu dem Schluss, dass dies häufig zu einem Shard-Diamanten führen würde, was zu einer mehrdeutigen Übereinstimmung führen würde. Dies wurde jedoch kürzlich in Frage gestellt, also muss ich es aufschreiben und sicherstellen, dass es tatsächlich funktioniert oder nicht.

@terrajobst / @danmosemsft Wurde jemand damit beauftragt?

Und @terrajobst , um die Arbeit zu verdeutlichen, die wir erreichen möchten, ist:
```
Wir sollten auch IReadOnlySet implementierenauf ImmutableSortedSetund ImmutableHashSet(und ihre Erbauer)
Wir sollten nach anderen Implementierungen von ISet suchen.
````
Neben der Implementierung der oben genannten Schnittstellen auf HashSet, SortedSet.

Das scannende Wesen sucht einfach nach etwas und bringt es hoch, wenn es fragwürdig aussieht.

Falls das noch zu vergeben ist, hätte ich Interesse

@Jlalond nein, dir zugewiesen. Danke für das Angebot.

@danmosemsft @terrajobst
Nur ein Update. Ich arbeite daran, habe die Schnittstelle zu privater Kern-Lib hinzugefügt und stolpere gerade durch die Sammlungen.generische und unveränderliche Aufnahme.

Letzte Frage, wenn Sie Dan aus dem Kopf haben, muss ich dafür irgendwelche Änderungen an Mono vornehmen? Ich bin nicht aufschlussreich, wo Corefx endet und Mono beginnt. Wenn Sie also wissen, dass es mich vor unabhängiger Recherche bewahren könnte

@Jlalond Sie sollten keine Änderungen an Mono vornehmen müssen. Ein Grund für das Verschieben der Mono-Laufzeit in dieses Repository ist die nahtlose Verwendung derselben exakten Bibliotheken mit CoreCLR oder der Mono-Laufzeit. Es gibt nur einen kleinen Teil der Kernbibliothek, der divergiert:
coreclrsrcSystem.Private.CoreLib im Vergleich zu mononetcoreSystem.Private.CoreLib. (Der größte Teil der Kernbibliothek wird aus den BibliothekenSystem.Private.CoreLib geteilt). Wenn Sie das also nicht berühren, sind Sie nicht betroffen.

@danmosemsft Danke für die Klarstellung, ich hoffe, dies in Kürze zu erledigen.

Hey @danmosemsft folge dem einfach nach. CoreLib baut in den src-Assemblys Ich kann die referenzierten Änderungen sehen. Die ref-Assemblys scheinen jedoch keine Änderungen zu erkennen. Das ist alles, was mich aufhält, aber ich kann keine Informationen in den Dokumenten finden. Alle Leute oder Hinweise, die Sie mir geben können, damit ich das erledigen kann (ich meine, 5 Jahre später)

Adressiert von #32488

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen