Runtime: Please add interface IReadOnlySet and make HashSet, SortedSet implement it

Created on 9 Jun 2015  ·  104Comments  ·  Source: dotnet/runtime

Original

Since IReadOnlyList was added the parity between sets and lists has declined. It would be great to re-establish it.

Using it would be an implementation-agnostic way of saying: "here is this read-only collection where items are unique".

Clearly it's needed by many people:

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
Some Guy In Comments: http://blogs.msdn.com/b/bclteam/archive/2013/03/06/update-to-immutable-collections.aspx

I found this discussion when working on a real world problem where I wanted to use the key collection of a dictionary for read only set operations. In order to support that case here's the API I propose.

Edit

Rationale

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

Open Questions

  • Is adding these methods to Dictionary<TKey, TValue>.KeyCollection acceptable given the code size cost for a commonly instantiated generic type? See here.
  • Should IReadOnlySet<T> be implemented in other Dictionary KeyCollections such as SortedDictionary or ImmutableDictionary?

Updates

  • Removed TryGetValue as it's not in ISet<T> and as such would prevent potentially ever rebasing ISet<T> to implement IReadOnlySet<T>.
  • Added ReadOnlySet<T> class which is similar to ReadOnlyCollection<T>.
api-approved area-System.Collections up-for-grabs

Most helpful comment

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

This is based on ISet<> API (except mutation methods obviously).

It's a pity Comparer does not fit in this, but then neither ISet<> nor IReadOnlyDictionary<> expose comparers, so it's too late to fix now.

All 104 comments

Wouldn't be nice if we just had some language construct to make things immutable? Then we would not have to have these magic interfaces.

@whoisj Which language? The CLR has dozens of them.

Even as a potential language feature it would require a metadata representation. For this case a marker interface (or behavioral interface) is as good as any. Trying to convey the immutability of a collection type through a new metadata entry doesn't seem appropriate since the CLR shouldn't be making assumptions as to how an arbitrary class functions internally (and the CLR has no concept of collection classes at all aside for arrays).

@whoisj I think this is at least considered for one of the future C# versions. But that decision doesn't affect the need for symmetrical interfaces across all collections. Furthermore I can imagine scenarios where a readonly list of mutable items could be useful, e.g. in games that care about both code quality and performance.

Also immutable collections are already available:

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

To achieve a fully immutable collection we just need a way of defining an immutable T and then use it to declare an Immutable...<T> collection.

@HaloFour we've been down this road before :smirk: but I still believe the CLR needs a way to say "here's a handle, read from it but all actions that cause any kind of write through it will fail; oh and this immutability is contagious so any handle reached from the immutable handle is also immutable (including this)".

@dsaf absolutely! In another issue I proposed that we have a writable anti-term for readdonly to enable the use of readonly collection of writeble elements. Something along the lines of readonly Bag<writable Element>.

I suggested that any reference marked with a & be treated as immutable by the compiler. I still feel it only need be a compile time check, and necessarily enforced by the CLR itself as I'm mostly seeking compile time verification of logic and not run-time guarantees. This would cover any reference which developers wanted to be immutable, but on a per call basis.

@whoisj Perhaps, but that's pretty tangential and it turns this request from something dsaf could branch/PR this afternoon into something that involves effort across three different teams.

You're also treating this as a compiler concern. At this point there isn't a compiler involved (beyond the JIT compiler) and only the verifier can attempt to prevent "improper" code from executing. Even the existing runtime mechanisms of immutability, initonly fields, can be easily defeated if verification is skipped (or via reflection).

I do agree that it would be nice if the C# language and compiler can have better support for "pure" methods. The attribute PureAttribute already exists but it's used sporadically and there really isn't any language support for it. And even if C# did support it through compiler errors (pure can only call pure, etc.), it's very easily defeated by using a different language. But these methods have to announce themselves and enforce themselves as once compiled to IL all bets are basically off and none of the compilers can bend how an existing assembly executes.

@HaloFour fair.

Assuming we don't have a general way to support "pure" or "const" references, then I suppose the proposed is the best alternative.

If you need it now, my Commons library (Commons.Collections https://github.com/yanggujun/commonsfornet/tree/master/src/Commons.Collections/Set ) has the readonly set support. Admin please delete this post if this is thought as an advert... My suggestion is to look around for some open source implementation.

@yanggujun Thank you for suggestion, that seems like a nice library, but I will roll my own to avoid extra dependencies.

My suggestion is to look around for some open source implementation.

This is a work-around, fundamental interfaces like IReadOnlySet should really be a part of .NET Framework itself.

Does this need a speclet to become "ready for api review"?

And while we're at it, consider naming it something different than "ReadOnly" (see interesting post: http://stackoverflow.com/questions/15262981/why-does-listt-implement-ireadonlylistt-in-net-4-5)
"Readable" seems fine.

@GiottoVerducci No. I would prefer to keep a consistent naming pattern even if it is imperfect. You are free to raise a separate issue to rename existing interfaces though.

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

This is based on ISet<> API (except mutation methods obviously).

It's a pity Comparer does not fit in this, but then neither ISet<> nor IReadOnlyDictionary<> expose comparers, so it's too late to fix now.

    bool Contains(T item);

Shouldn’t this be in IReadOnlyCollection<T> instead since ICollection<T> has Contains(T item)?

The immutable collections package has been unlisted from nuget while still beta.
I think this is a pretty common use case and should be handled in standard libs.

Is there more work to do on the API here, as the tag suggests? I'm happy to spend some time on this if it'd be helpful and someone can point out what's needed.

The API @ashmind proposed looks great.

Can ISet<T> be made to extend IReadOnlySet<T>? This didn't happen IList<T>/IReadOnlyList<T>?

If not, then I suppose the other changes to consider are adding IReadOnlySet<T> to the interface list for all ISet<T> implementations in corefx including HashSet<T>, SortedSet<T> and their immutable counterparts in System.Collections.Immutable.

I have to agree with @GiottoVerducci . Using a name like IReadOnlySet<T> doesn't declare the contracts capabilities, it declares the contracts limitations. To then use that same contract combined with another that contradicts those limitations is confusing. I believe that the contract name should describe a positive assertion pertaining to what the implementer supports. A name like IReadableSet<T> isn't great, admittedly, but it at least better describes what the implementer does.

@HaloFour I agree in principle, but we have the same situation now with IReadOnlyList<T>. Maintaining consistency trumps the increase in precision here, IMHO.

@drewnoakes

I understand, and consistency is important. I think that also answers why ISet<T> shouldn't extend IReadOnlySet<T>, though.

Maintaining consistency trumps the increase in precision here, IMHO.

I think that also answers why ISet<T> shouldn't extend IReadOnlySet<T>, though.

I think you’re missing the point. That’s the reason that IList<T>, ICollection<T>, IDictionary<TKey, TValue> should, in addition to ISet<T>, also be fixed to implement read-only view interfaces. Otherwise everyone has to continue to be confused when working around the unintuitive design of the BCL.

@binki

I don't disagree. What I don't like about that is having a contract that stipulates read-_only_ behavior being extended by a contract that stipulates read-_write_ behavior. The naming is wrong and the composition is wrong. But here we are. I'd love to vote to change both, but I doubt such is on the table.

@HaloFour

When you get an interface into something, it’s a _view_ into something. The view itself _is_ read-only. Assuming you wrote type-safe code and won’t go around upcasting, if you receive something that is read-only, it is, for all intents and purposes, read-only. That’s no guarantee that the data won’t change. It’s just like opening a file read-only. A file opened read-only can be mutated by another process. Or like read-only access to pages on a website where an administrator would have a read-write view into the data and can change it out from under you.

I’m not sure why read-only is considered the wrong term here. Read-only does _not_ imply immutable. There’s a whole nuget package/different API (where adding/removing generates a new object and the current instance is guaranteed to never mutate—thus being immutable) for that if that’s what you require.

I was thinking something similar. "Read only" in .NET is a pretty weak guarantee for fields too. Given a do-over, I'm sure all this would make more sense. For now it's worth being pragmatic.

So in general, if a method accepts an IReadOnlySomething<T> you can, in general, assume that it won't modify it. There's no guarantee the receiving method won't upcast the reference, and there's no guarantee that the interface's implementation won't internally modify itself when accessed either.

In C++, const_cast weakens the guarantees of const too, which is a shame (esp nowadays with the mutable modifier) but in practice it doesn't take away from how useful const is as an feature. You just have to know what you're dealing with.

@binki makes a good distinction. _Immutable_ in the name implies a hard guarantee of stability over time for all involved.

Does anyone have an authoritative source as to why IList<T> doesn't extend IReadOnlyList<T>?

@binki

An interface isn't a view, it's a contract. That contract declares the capabilities of the implementer. If the implementer doesn't actually implement those capabilities I would consider that a contract violation. That List<T> class claims that it "is-a" IReadOnlyList<T>, but it's not. It lacks that capability.

There are multiple schools of thought on this subject. I clearly belong to the school where interface inheritance more strictly follows "is-a" relationships between types. I certainly support a more granular approach to composition with interfaces and think that List<T> and its kin could probably benefit from implementing some 3-4 additional interfaces (read, write, append, etc.) But I certainly think that the name of an interface should describe what a type _can_ do, not what it _can't_ do. Negative capability assertions don't make much sense for contracts.

@drewnoakes

For now it's worth being pragmatic.

I agree. We are where we are. If IList<T> were to be changed to extend IReadOnlyList<T> then it makes sense for ISet<T> to be changed to extend IReadOnlySet<T>, etc.

Is it being too redundant to also push for IReadableX<T>, IWritableX<T>, etc. interfaces to live alongside IReadOnlyX<T>?

Does anyone have an authoritative source as to why IList<T> doesn't extend IReadOnlyList<T>?

Apparently it would be an ABI-breaking change when loading assemblies that were compiled against older .net frameworks. Because when implementing an interface, most compilers will automatically generate explicit interface implementations when the source code relies on implicit interface implementation, if you compiled your class implementing IList<T> against a BCL that doesn’t have IList<T> implementing IReadOnlyList<T>, the compiler won’t automatically create the explicit IReadOnlyList<T> implementations. If I’m reading this right: http://stackoverflow.com/a/35940240/429091

@HaloFour Since List<> and HashSet<> implement ICollection<> and IReadOnlyCollection<>, we've already embraced a path where IReadOnly refers to access and not capability. Based on that, having IAnything extend IReadOnlyAnything makes perfect sense. I agree that IReadable is better than IReadOnly but at this point everyone understands IReadOnly to _mean_ IReadable and uses it as such. In fact, I'm perpetuating that intentionally in my own codebase because having two ways of thinking about things is more cognitive load than anything in my opinion.

We're stuck with the name, but the concept behind it is powerful enough that I truly wish it was possible for all interfaces to extend IReadOnly going forward, just like we do with concrete classes.

@ashmind I think it's perfect that none of the methods take comparers. In sets and dictionaries, comparers aren't something you can easy swap out because they determine the structure of the entire object. Also, it wouldn't make sense to pass a comparer to a CaseInsensitiveStringCollection or any collection that implies a certain comparison.

(In the case of a weird collection that does implement Contains(T, IEqualityComparer<T>) more efficiently than the extension method that's already available, it would probably be a one-off class method. It's hard to imagine Contains(T, IEqualityComparer<T>) being common enough to end up in a specialized interface, but there's nothing stopping even that from happening.)

@jnm2

I think it's perfect that none of the methods take comparers.

Just to clarify, I wanted to say that it should expose comparer, not take one. Since every Set or Dictionary must have some equality algorithm, this could have been exposed on the interface. But I don't remember my use case for that now -- something like creating a set using the same comparer as in an externally provided one.

While this discussion brings up lots of interesting points, it seems to be straying far from the simple and obvious suggestion that started this thread. And that is discouraging because I would really like to see this issue addressed.

As the OP said, the failure to maintain parity among collection types when IReadOnlyList was added without IReadOnlySet is unfortunate and many people have implemented their own versions of IReadOnlySet interface as workarounds (my own team has a similar workaround). Those workaround interfaces are not ideal because the corefx classes can't implement them. This is the key reason for providing this in the framework: if I have a HashSet I would like to be able to use it as an IReadOnlySet without copying or wrapping the object I already have. For performance at least this is often desirable.

The name of the interface should clearly be IReadOnlySet. Consistency trumps any concerns with the IReadOnlyXXX names. That ship has sailed.

None of the existing interfaces (IReadOnlyCollection) can be changed. The back-compat requirements for .NET don't allow changes like that. It is unfortunate that Comparers aren't exposed in the existing IReadOnlyXXX interfaces (I've run into this as well) but again the ship has sailed.

The only question that seems to remain from a practical standpoint is between these two potential definitions of the interface.

Previously proposed by @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);
}

Minimal proposal:

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

Personally I prefer this minimal proposal since the other methods can be derived; ideally there would be a standard implementation of those as extension methods over the IReadOnlySet interface so the implementors of IReadOnlySet don't need to provide them. I also feel this minimal proposal is more in line with the other minimal IReadOnlyXXX interfaces.

@aaron-meyers The only concern I would have is that IsSubsetOf and friends cannot be derived from Contains in an _efficient_ manner. When it's two hash tables, for instance, relying on Contains forces the implementation to use nested loops rather than hash matching.

Perhaps a new interface, IComparableSet<T> could contain the set operations.

We already have extension methods on IEnumerable<T> for a few set operations.

@jnm2 The implementation of these methods used by HashSet only requires Contains and enumerating the other collection (which IReadOnlySet would get by inheriting IReadOnlyCollection). It does require though knowing that the other set uses the same comparer. Perhaps it is worth adding the Comparer property to IReadOnlySet so that these operations can be implemented efficiently in the extension methods. It is unfortunate that IReadOnlyDictionary doesn't expose the KeyComparer as well but it may be worth adding this to IReadOnlySet even though it isn't entirely consistent. There are good reasons that it should have been included on IReadOnlyDictionary in the first place, as covered here.

The modified proposal would be:

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

Alternately, the Comparer could be on a separate interface and the extension method implementations of the set operations would only use the efficient route if both objects implement the interface and have the same comparers. The same approach could be applied for IReadOnlyDictionary (in fact, perhaps they just use the same interface). Something like ISetComparable. Or drawing from @drewnoakes there could be an IComparableSet but instead of defining the set operators, it just defines the comparer:

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

In this case IReadOnlySet goes back to just defining Contains:

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 = @set 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> @set, IEnumerable<T> other)
    {
         var setEx = @set 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;
    }

}

We can use this approach.
Usage will looks like this;

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

Many requirements satisfied, IReadOnlySet is minimalistic. But GetComparer now method, not property. But it's a good trade off.

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

Just published this contracts with injection helpers to the nuget (https://www.nuget.org/packages/ClrCoder.Collections.ReadOnlySet).
Feel free to use it and submit issues here: https://github.com/dmitriyse/ClrCoder/issues
If it will becomes a little bit popular (probably after some rounds of refining) we can suggest this improvement to the CoreFX team.

@terrajobst is it okay for compat for existing classes to implement new interfaces?

@safern there is precedent in List<T> getting IReadOnly added.

So is it planned to add in next .NET framework releases?

When is this work going to land? Any timelines?

https://www.nuget.org/packages/System.Collections.Immutable/
Full set of immutable collections )

OK. This has been here for far too long. I need it very much and would like to propose a way for how to approach this then.

Instead of exposing all of these:

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

and forcing the customers to implement these members, let's just add what is most crucial to have:

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

and nothing more. More API can always be added in the future, if there is such a need. But it cannot be removed, thus this proposal.

We would then add this interface to the list of interfaces extended by ISet<T>.

From the documentation

ISet<T>: This interface provides methods for implementing sets, which are collections that have unique elements and specific operations. The HashSet and SortedSet collections implement this interface.

From the code

The ISet<T> interface already has our bool Contains<T>(T item); method defined via ICollection<T>. It also has int Count { get; } via ICollection<T>.

So would be:

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

Trivial change, little to discuss, huge benefit.

Please let's make it happen. Let me know if such a pull request would be accepted and merged. I can create it then.

@karelz @terrajobst @safern @ianhays

I found this discussion when working on a real world problem where I wanted to use the key collection of a dictionary for read only set operations. In order to support that case here's the API I propose.

Rationale

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

Open Questions

  • Is adding these methods to Dictionary<TKey, TValue>.KeyCollection acceptable given the code size cost for a commonly instantiated generic type? See here.
  • Should IReadOnlySet<T> be implemented in other Dictionary KeyCollections such as SortedDictionary or ImmutableDictionary?

Updates

  • Removed TryGetValue as it's not in ISet<T> and as such would prevent potentially ever rebasing ISet<T> to implement IReadOnlySet<T>.
  • Added ReadOnlySet<T> class which is similar to ReadOnlyCollection<T>.

@TylerBrinkley thanks for adding an API Proposal in the thread. Would you mind adding rationale and use cases? So that I can mark it ready for review and let the api experts decide?

@TylerBrinkley do no forget to include EqualityComparer property in the IReadOnlySet interface. They are currently missed in Dictionaries ans Sets in the CoreFX, but it's an issue.

@dmitriyse what use would a getter only IEqualityComparer<T> property be? What would you do with it?

Dictionaries and Sets should report their EqualityComparers to allow correct collection cloning.
Unfortunately I forgot where this issue was discussed.

If you're doing cloning wouldn't you be working with a concrete type? Why would the interface need to support the IEqualityComparer<T>?

For example if you have set with the strings and case insensitivity equality comparer, you will receive an exception on creating of a new HashSet without specifying correct EqualityComparer. There are cases when you can't know what EqualityComparer is used in the specified set.

It's not just cloning. I think the far more common scenario is comparing two sets -- I need to know that they both use the same comparer in order to implement an optimal comparison using Contains. I think there's an example in this thread.

That said, I'd rather have IReadOnlySet with just the Contains method than not at all. It would be nice to be able to implement Set comparison generically but not as common as just needing a read-only reference to a Set.

Get Outlook for iOShttps://aka.ms/o0ukef


From: Tyler Brinkley notifications@github.com
Sent: Thursday, May 10, 2018 6:21:52 AM
To: dotnet/corefx
Cc: Aaron Meyers; Mention
Subject: Re: [dotnet/corefx] Please add interface IReadOnlySet and make HashSet, SortedSet implement it (#1973)

If you're doing cloning wouldn't you be working with a concrete type? Why would the interface need to support the IEqualityComparer.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://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&reserved=0, or mute the threadhttps://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&reserved=0.

I agree—the only way you can tell what types of duplicates you might find in the set (case sensitive, case insensitive, etc) is by exposing the comparer.

I'm beginning to think that my proposal would not be accepted as adding all those methods to the Dictionary<TKey, TValue>.KeyCollection would have a significant code size cost. See this discussion regarding adding new API to commonly instantiated generic types.

I don't think you're going to be able to put a comparer on IReadOnlySet.

SortedSet takes an IComparer, while HashSet takes an IEqualityComparer.

Good point, the comparer could be considered an implementation detail that doesn't belong on a general interface.

Get Outlook for iOShttps://aka.ms/o0ukef


From: Cory Nelson notifications@github.com
Sent: Thursday, May 10, 2018 5:04:06 PM
To: dotnet/corefx
Cc: Aaron Meyers; Mention
Subject: Re: [dotnet/corefx] Please add interface IReadOnlySet and make HashSet, SortedSet implement it (#1973)

I don't think you're going to be able to put a comparer on IReadOnlySet.

SortedSet takes an IComparer, while HashSet takes an IEqualityComparer.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://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&reserved=0, or mute the threadhttps://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&reserved=0.

There may be some value in adding IUnorderedSet and IOrderedSet interfaces, but I wouldn't want that to derail pushing IReadOnlySet through.

I like @pgolebiowski 's suggestion but would make that interface even more basic.

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

This is then suitable for uncountable sets such as Intervals (real) as well. Between that and IReadOnlySet you could potentially fit in a few other interfaces such as ICountableSet (aka IEnumerableSet), IUnorderedSet and IOrderedSet.

But agree that just IReadOnlySet would be a large improvement over what's currently available.

+1 for @NickRedwood

It's been open for over 3 years now. Please make this happen by changing the approach. Introducing the change mentioned by @NickRedwood allows for all the other enhancements that you are discussing in this thread. All the approaches agree about this single trivial thing. So let's add this single trivial thing which is fundamental and then create another issue for potential enhancements that are all optional.

@TylerBrinkley @safern @karelz @terrajobst

I don't really see that much benefit to an IReadOnlySet<T> interface with only a Contains method. While that method is useful it's already included in the ICollection<T> interface which also has an IsReadOnly property intended to indicate whether the modification methods of ICollection<T> are supported. Just implement ICollection<T> and make IsReadOnly return true. The only other functionality it would be expected to support would be a Count property, being enumerable, and a CopyTo method which doesn't seem too limiting.

I would be very happy for the proposed IReadOnlySet<T> to be implemented based on IReadOnlyCollection<T> - it would be a large improvement on what's currently available. (@TylerBrinkley - assume you meant IReadOnlyCollection<T> rather than ICollection<T>.

I guess I err on the side of more basic interfaces, and if there was one for Set then it would be a single-method-interface with a Contains method. It has a good mathematical basis and is very clean. However when retrofitting to existing classes I accept that retrofitting less is better, and if there was to be only one interface retrofitted then IReadOnlySet<T> based on IReadOnlyCollection<T> should be it.

IReadOnlyCollection<> doesn't have .Contains because that would prevent it from being covariant.

Good point and I've corrected my earlier post to be contravariant. IReadOnlySet<T> would need to be invariant unless the approach posted early in the thread is used, and I'm unsure about that one.

@TylerBrinkley personally I'm not a fan of the IsReadOnly property, I would much rather have this information known at compile-time instead of run-time. If you were referring to the IReadOnlyCollection<T> interface then I still think it would be useful for the caller or callee to know that the lookup will be fast instead of a potential iteration though a collection, though I'm not certain if a "set" implies that.

Having just a Contains method doesn't define what a Set should do, just what a Collection should do. I mean Contains is not even a member of ISet but of ICollection which ISet inherits from. The other members of ISet should be required to define what a Set should do. I understand most people probably use sets exclusively for it's Contains method but there's so much more to sets than just that.

@TylerBrinkley But is it even possible to define IReadOnlyCollection<T>.Contains(T) at this point? I assumed not. That’s why the only option is to introduce IReadOnlySet<T> and, when introduced, ensure that IReadOnlySet<T>.Contains(T) is declared on it.

@jnm2 says:

IReadOnlyCollection<> doesn't have .Contains because that would prevent it from being covariant.

This seems to be the biggest issue to me right now. It would be odd for IReadOnlySet<T> to be invariant when IReadOnlyCollection<T> is covariant. It seems like many existing IReadOnly* interfaces were carefully deisgn to be out T with the writable interfaces being necessarily invariant. Most of us would want IReadOnlySet<T> to at least declare a Contains(T) method and yet that would prevent it from being covariant.

If ICollection really is the right place for Contains(T) to be defined, is that possible? Maybe IReadOnlyProbableCollection<T>.Contains(T) which would be invariant and implemented by Collection<T> and HashSet<T>?

I think @NickRedwood’s suggestion of ISetCharacteristic<T> seems cleaner maybe. That even has the benefit of allowing you not to implement Count which could be useful.

personally I'm not a fan of the IsReadOnly property, I would much rather have this information known at compile-time instead of run-time.

That man speaks well, pour him more wine.

@binki I agree there needs to be a Contains method on IReadOnlySet<T> since IReadOnlyCollection<T> did not include one, however I also think all the other non-mutating ISet<T> methods should be included.

Besides the Dictionary.KeyCollection use case I mentioned above what other use cases can you come up with for adding this interface?

OK, looks like the API design is:

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

It is the only common part of the design that everyone seems to be able to agree to.

I SAY THAT NOW WE END THE DESIGN HERE. If you want to extend it in the future, do it in a different issue. This is the most important functionality that should be in prod.

Who is for, who is against?

I like @ashmind 's suggestion. It would be awesome if Dictionary.Keys can implement this interface as it is technically IReadOnlySet<TKey>.

Could you please mark this as api-ready-for-review? I've just found myself needing this interface again and it is still not there.

Why isn't this implemented yet?

[EDIT] Apologies - my original reply was mix up with another API. I don't have strong opinion about this one, text edited. Sorry for confusion!

Why isn't this implemented yet?

The API didn't get attention by area owners yet - @safern. I am not sure how high it is on Collections priority list. The upvote number is fairly high.
The truth is that we are now locking down for .NET Core 3.0, so it will have to wait for next release at minimum as it is not critical for 3.0.

I'm surprised too that this is not provided out of the box.

Immutable data structures can be a massive help towards improving code maintainability, but they are hard to use without an interface in the standard libraries that specifies semantics and allows different implementations to work together.

Without this, each library meaning to use an immutable set as a parameter/return value needs to resort to either using IEnumerable which is less efficient and semantically incorrect or using their own interfaces/classes in their signatures which is incompatible with anyone else's.

I'm curious if there are any workarounds for this that are efficient in terms of Contains lookups and avoid specifying the concrete type of the parameter/return value. The best I could think of off the top of my head would be to use IEnumerable in the signature and pass ReadOnlyDictionary.Keys, but that seems a bit nasty, especially in a library. Since Enumerable.Contains in Linq will use the collection implementation of Contains, this should be efficient while compatible, but does not communicate the intent which may lead to using less performant implementations.

@adamt06 You're looking for ISet<T>.

@scalablecory You are right, with an immutable implementation, and there is one: ImmutableHashSet<T>

Does anyone know/understand why ICollection does not extend IReadOnlyCollection ??
I thought this was a little related to this thread. Instead of me opening a new thread. Maybe I am just misunderstanding something.

Another thought, but completely off topic, And why doesn't ReaOnlyCollection have:

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

Oh, I see you meant IReadOnlyCollection doesn't have them. It's because otherwise the interface could not be covariant.

I'm not sure, but it's probably something to do with explicit interface implementation causing problems with backwards compatibility. I can see why updating the existing interfaces to inherit from other interfaces from being a problem, but I don't see why creating a new interface IReadOnlySet<T> and having HashSet implement it would be an issue.

OK. But is still also do not see ICollection should not be:
c# ICollection<T> : IReadOnlyCollection<out T>
Why should a read/write collection not also be an extension of a readonly collection?

@generik0

OK. But is still also do not see ICollection should not be:

ICollection<out T> : IReadOnlyCollection<out T>

Why should a read/write collection not also be an extension of a readonly collection?

First, please note that it is impossible to declare ICollection<out T> because ICollection<T> has a member .Add(T item).

Second, note that ICollection<T> shows up in .net-2.0 and IReadOnlyCollection<out T> shows up in .net-4.5. If you compile code as implementing ICollection<T> and then ICollection<T> is changed to implement a new interface, any existing compiled binaries will break at runtime because anything only implementing ICollection<T> would not have its IReadOnlyCollection<T> slots filled in (possibly automatically) by the compiler. This is the compatibility issue that prevented ICollection<T> from implementing IReadOnlyCollection<T>.

I don’t think this is clearly addressed by this SO answer, but there is an SO for it: https://stackoverflow.com/a/14944400 .

@binki "ICollection" fixed to ICollection, thanks for pointing out my typo..
DotNetStandard i net461. And this is where the change should be. netstandard 2+

I will repeat...
Why should a read/write collection not also be an extension of a readonly collection?

And why should one need to cast a collection to e.g. "ToArray()" just to expose less if it?

And ofcause you can add new interfaces in higher versions without breaking things. ICollection already has teh IReadOnlyCollection methods implemented. Noting should break,... :-/

@generik0 Looks like it wouldn't break source compatibility which is what you're thinking of [wasn't thinking; it would], but it would break binary compatibility which works with IL tables, not C#.

Ok @jnm2 thanks.
I will stop my off topic rant now, just think it is bad architecture. Thanks everyone for listening / explaining:-)

@jnm2

@generik0 Looks like it wouldn't break source compatibility which is what you're thinking of, but it would break binary compatibility which works with IL tables, not C#.

To nitpick (sorry), it would also break source compatibility if your source explicitly implemented ICollection<T> from prior to .net-4.5. The class would start failing to compile due to a failure to explicitly implement IReadOnlyCollection<T>.Count. But the binary compatibility is a bigger deal because it would prevent you from targeting a wide range of .net versions (you’d have to have different binaries for running on ≥.net-4.5 than <.net-2 regardless and you’d have to target both instead of just targeting the older framework).

I'm wondering if with the addition of default interface implementation support in C#8 if ICollection<T> could implement IReadOnlyCollection<T> for .NET Core 3.0+.

Maybe an extension is needed, then it is atleat the same collection. i.e. if you need to get an ienumerable or icollection to a reaadonlycollection ... Any thoughts?

[edited by @jnm2 comment]
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 Why not use enumerable as IReadOnlyCollection<T> ?? throw ... or (IReadOnlyCollection<T>)enumerable instead of calling that method?

@jnm2 omg. You are right. Sometimes you don’t see the clear picture and overcomplicate things!!!

I would still call the extension, just to get the simplicity ;-)
Thanks mate....

What's the status here? I'd really like this interface in standard lib and have HashSet<> implement it.

It seems like our best bet may be to wait a while longer for the Shapes proposal to be (hopefully) implemented. Then you'd be able to build whatever group of shapes to represent Set types that you wanted, and provide implementations so that existing sets like HashSet<T> conform to them.

A community library to do exactly this could then emerge, covering all the various types of sets (intensional (predicates) and extensional (listed), ordered, partially ordered, unordered, countable, countably infinite, uncountable infinite, possibly mathematic domains too like Rational, Natural numbers etc) as different shapes, with all the union, intersection, cardinality methods defined for and between these sets.

That's sounds to me like 5 years down the road. Why should a simple change that can be implemented in a day wait for some 1000x larger not-yet-specified feature that might not even happen?

That's sounds to me like 5 years down the road. Why should a simple change that can be implemented in a day wait for some 1000x larger not-yet-specified feature that might not even happen?

I'm just responding to the lack of progress on IReadOnlySet<T> - this issue has already been open 4 years after all.

The Microsoft way: The most simple and useful things take decades. It's mind blowing. 5 years and counting.

What's even more funny is that they have it in here
https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.sdk.sfc.ireadonlyset-1

@terrajobst thoughts?

  • We generally believe that this indeed a hole in our interface hierarchy. We suggest to focus on just adding the interface and implementing it on the existing set implementations. We can't make ISet<T> extend IReadOnlySet<T> (for the same reason that we couldn't do for the other mutable interfaces). We can add an ReadOnlySet<T> at a later stage. We should double check that IReadOnlySet<T> is just ISet<T> minus the mutable APIs.
  • We should also implement IReadOnlySet<T> on ImmutableSortedSet<T> and ImmutableHashSet<T> (and their builders)
  • We should scan for other implementations of 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> {
     }
 }

Do it, I dare you!

@terrajobst

We can't make ISet<T> extend IReadOnlySet<T> (for the same reason that we couldn't do for the other mutable interfaces).

Is this still true even with default interface methods? Does that mean https://github.com/dotnet/corefx/issues/41409 should be closed?

@terrajobst

We can't make ISet<T> extend IReadOnlySet<T> (for the same reason that we couldn't do for the other mutable interfaces).

Is this still true even with default interface methods? Does that mean dotnet/corefx#41409 should be closed?

We discussed this. We used to think that that DIMs would work, but when we walked the solution we concluded that it would result commonly in a shard diamond which would result in an ambiguous match. However, this was recently challenged so I think I have to write it down and make sure it's actually working or not working.

@terrajobst / @danmosemsft Has Anyone been assigned to this?

And, @terrajobst to clarify the work we want to achieve is:
```
We should also implement IReadOnlySet on ImmutableSortedSet and ImmutableHashSet (and their builders)
We should scan for other implementations of ISet.
````
As well as implementing the above interfaces on HashSet, SortedSet.

The scanning being just look for anything and bring it up if it looks questionable.

If this is still up for grabs I'd be interested

@Jlalond nope, assigned to you. Thanks for the offer.

@danmosemsft @terrajobst
Just an update. I'm working on this, added the interface to private core Lib, just stumbling my way through having collections.generic and immutable pick up on this.

Last question if you know off your head Dan, do I need to make any changes to Mono for this? I'm not insightful on where corefx ends and mono starts. So if you know it could save me from some independent research

@Jlalond you should not need to make changes to Mono. Part of the reason for moving the Mono runtime into this repo is to make it seamless to use the same exact libraries with either CoreCLR or the Mono runtime. There is only a small part of the core library that diverges:
coreclrsrcSystem.Private.CoreLib vs mononetcoreSystem.Private.CoreLib. (Most of the core library is shared out of librariesSystem.Private.CoreLib). So unless you touch that - you are not affected.

@danmosemsft Thanks for the clarification, I hope to have this done shortly.

Hey @danmosemsft just follow up on this. CoreLib is building in the src assemblies I can see the referenced changes. However the ref assemblies seem to not be detecting any changes. This is all that's holding me up, but I can't find any info in the docs. Any people or pointers you can give me so I can get this done (I mean, 5 years later)

Addressed by #32488

Was this page helpful?
0 / 5 - 0 ratings