CommunityToolkit / dotnet

.NET Community Toolkit is a collection of helpers and APIs that work for all .NET developers and are agnostic of any specific UI platform. The toolkit is maintained and published by Microsoft, and part of the .NET Foundation.
https://docs.microsoft.com/dotnet/communitytoolkit/?WT.mc_id=dotnet-0000-bramin
Other
2.99k stars 294 forks source link

Add support for Nullable structures to Span and Memory types #831

Closed AlexRadch closed 8 months ago

AlexRadch commented 8 months ago

Overview

Unfortunately, in .NET, Nullable structures currently do not have interfaces. However, the most useful methods for Span require that elements support the IEquatable and IComparable interfaces. Therefore, Span does not support Nullable structures well. At the same time, ordinary arrays perfectly support such structures.

I propose to add support for Nullable structures to spans by adding a wrapper for Nullable structures similar to the Box wrapper. This wrapper for the Nullable structure will support the IEquatable and IComparable interfaces.

I propose adding support for Nullable structures to Spans by adding a wrapper for the Nullable structure similar to the Box wrapper. This wrapper for the Nullable structure will support the IEquatable and IComparable interfaces.

Instances of our wrapper will not be created, the usual Nullable structure will always be created, but our wrapper will be needed to conveniently convert spans to Spans with our wrapper, and all span methods based on these interfaces will become available.

The same goes for the memory class. But less useful because the Memory class returns a Span, which we can already cast to a Span with the suggested wrapper.

API breakdown

public static NullableWraper<T> : IEquatable<NullableWraper<T>>, IComparable<NullableWraper<T>>
  where T as in Nullable<T>
{
   // Fields as inside Nullable<T> struct
   // Interface implementation
   // Implicit and Explicit casting methods
}

public static class SpanExtensions
{
    // Add Span casting methods
}

Usage example

int?[] array = [9, 1, null, 3, null, 5, null, 7, null];
var span = array.AsSpan();

// The following line of code is not supported by Span methods because Nullable<T> does not have an IEquatable interface.
// var index = span.IndexOf(5);

// Casting to span with Nullable proxy
var smartSpan = span.WithNullableWraper();

// It works now!!!
var index = smartSpan.IndexOf(5);

Breaking change?

No

Alternatives

I do not not.

Additional context

No response

Help us help you

Yes, I'd like to be assigned to work on this item

DaZombieKiller commented 8 months ago

Box<T> relies on runtime implementation details and involves undefined behavior, it will most likely be deprecated and removed soon. The proposed NullableWrapper<T> similarly relies on runtime implementation details so I don't think it's the right solution for this problem.

Sergio0694 commented 8 months ago

Agreed, we should not add any new APIs relying on implementation behavior of the runtime. The proposed API is doing exactly that, by relying on the internal layout of Nullable<T>, and also on the runtime actually applying the same ordering on both types. Furthermore, this limitation really only affects an extremely niche scenario, and I can't say I've ever seen anyone else bringing this up, so we'd want more use case scenarios first. Even then, this feels more like something to fix in the BCL than here.

Thank you!