Closed stephentoub closed 2 years ago
Tagging subscribers to this area: @dotnet/area-system-collections See info in area-owners.md if you want to be subscribed.
Author: | stephentoub |
---|---|
Assignees: | - |
Labels: | `api-suggestion`, `area-System.Collections` |
Milestone: | 8.0.0 |
These might be our first non-abstract/virtual static interface methods in the core libraries?
Any potential risks? From a discoverability standpoint they seem pretty nice, and possibly a pattern we could be introducing in other interface types.
Any potential risks?
Not that I'm aware of. It just hasn't been expressible until relatively recently (C# 8 if memory serves).
For IReadOnlyCollection<T>
and IReadOnlyList<T>
, what's the benefit comparing to Array.Empty
?
For IReadOnlyCollection
and IReadOnlyList , what's the benefit comparing to Array.Empty?
Discoverability and consistency across the interfaces. There's no performance benefit I can think of.
For IReadOnlyCollection and IReadOnlyList, what's the benefit comparing to Array.Empty?
Discoverability and consistency across the interfaces. There's no performance benefit I can think of.
Should we be using private implementations for each these interfaces or does returning Array.Empty suffice? Assuming we do the latter, Hyrum's law dictates that users will take a dependency on the underlying type being an array, but I don't foresee this needing to change.
Should we be using private implementations for each these interfaces or does returning Array.Empty suffice?
Unless we can optimize something better than what array does, I think we should just use Array.Empty.
ReadOnlyObservableCollection seems to be a good candidate too
Unless we can optimize something better than what array does, I think we should just use Array.Empty.
This will invariably lead to people taking a dependency on these values being actual arrays. Of course, nobody should do that but it's going to happen. This could make future changes to these properties difficult from a compatibility standpoint. Maybe it's safer to have a private implementation.
For example, a reflection-based serializer might take a different path based on runtime type.
For example, a reflection-based serializer might take a different path based on runtime type.
That's a good point, actually. Any reflection-based serializer using polymorphism might provide divergent serialization contracts for empty instances.
What does using the interface members look like -- var empty = IReadOnlyList<Foo>.Empty;
?
List<Foo>.Empty
would be more discoverable, presumably. (or create a new ReadOnlyList<T>
and put it on there, similar for ReadOnlyHashSet<T>
perhaps). what am I missing?
what am I missing?
That List<Foo>.Empty
would be expected to return a List<Foo>
(which we can't do), not IReadOnlyList<Foo>
. So it would discoverably return the wrong thing.
Right, I figured the return type is something that could be navigated, once you've found the thing. But I defer to API experts.
And is there value in considering adding ReadOnlyList<T>
and ReadOnlyHashSet<T>
, including Empty following the pattern above?
And is there value in considering adding
ReadOnlyList<T>
andReadOnlyHashSet<T>
, including Empty following the pattern above?
ReadOnlyCollection<T>
is an IReadOnlyList<T>
. I don't think adding a whole new type that's a duplicate of ReadOnlyCollection<T>
adds enough values.
There might be value in a ReadOnlySet<T>
ala ReadOnlyDictionary<TKey, TValue>
, but I wouldn't add that purely for Empty
. Rather, we could choose to add such a thing on its own merit, and if we ever did, it's surface area would include an Empty
property.
Would this cause confusions, as the proposed static properties would be inherited? For example, if IReadOnlyList<T>.Empty
were introduced, IImmutableList<T>
, which inherits from IReadOnlyList<T>
, would also have an inherited static property Empty
, which unfortunately would be of type IReadOnlyList<T>
, not IImmutableList<T>
.
For other languages like VB or F#, would static interface members be difficult to consume?
add
IReadOnlySet<T>.Empty
That's already there in the list.
ReadOnlySet<T>
; if we had this, we should also a static Empty
therenamespace System.Collections.ObjectModel
{
public partial class ReadOnlyCollection<T>
{
public static ReadOnlyCollection<T> Empty { get; }
}
public partial class ReadOnlyDictionary<TKey, TValue>
{
public static ReadOnlyDictionary<TKey, TValue> Empty { get; }
}
public partial class ReadOnlyObservableCollection<T>
{
public static ReadOnlyObservableCollection<T> Empty { get; }
}
}
FWIW, I just ran into a situation today where I would have liked to have IReadOnlySet<T>.Empty
and had to roll my own. Would be a bit unfortunate if that doesn't get included here in some form.
Background and motivation
Various pieces of code today allocate new read-only collections (e.g.
ReadOnlyCollection<T>
) around empty collections. Sometimes code implements its own singleton caching, e.g.and sometimes it doesn't, e.g.
In other cases, empty could be special-cased but isn't as doing so would have required someone to add their own cache for it to have meaningful benefits).
In any case, we can make this easier and more efficient by exposing our own singletons that we and others can use.
(Replaces https://github.com/dotnet/runtime/issues/41147, which is limited to just ReadOnlyCollection.) (Related to https://github.com/dotnet/runtime/issues/38340, which asked for such Empty methods on mutable collections like
List<T>
returning empty instances of those same collections. There we said we'd consider read-only variants with a proposal; this is that proposal.)API Proposal
API Usage
Alternative Designs
No response
Risks