-
Notifications
You must be signed in to change notification settings - Fork 750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Final wave of #nullable in Rx. #1373
Conversation
@danielcweber Is a revisit of I think there's a good middle ground by having struct-based implementations of the various disposables, which encapsulate the // This can be inlined in operators and encapsulates the common use case (as it did originally in Rx).
internal struct SerialDisposableValue
{
private IDisposable? _value;
// Maybe a constructor if initial assignment is common.
// Maybe an additional void Initialize(IDisposable?) for the initial assignment
// iff it's decoupled from the instantiation of the struct value and iff it's clearly
// more performant than an assignment to Disposable.
public bool IsDisposed => Disposables.Disposable.GetIsDisposed(ref _current);
public IDisposable? Disposable
{
get => Disposables.Disposable.GetValue(ref _current);
// Can be optimized, we discard the result of TrySetSerial anyway.
set => Disposables.Disposable.TrySetSerial(ref _current, value);
}
// Possibly another form of assignment given that we're commonly using TrySetSerial
// and discard its return value. (4/31 use sites do use the value right now)
// bool TryAssign(IDisposable value) => ...
public void Dispose() => Disposables.Disposable.Dispose(ref _current);
}
public sealed class SerialDisposable : IDisposable
{
private SerialDisposableValue _value;
public bool IsDisposed => _value.IsDisposed;
public IDisposable Disposable
{
get => _value.Disposable;
set => _value.Disposable = value;
}
public void Dispose() => _value.Dispose();
} Similar for Current stats:
With the proper (re)encapsulation, I'd think all of these would drop to single-digit counts, or vanish entirely behind the struct wrapper. |
@bartdesmet This mention went by completely unnoticed by me, sorry. I may have a look. The reason I revisited this PR is to have a look about the current state of nullability on the public API surface. More specifically, I ran into a bug in my code because e.g. FirstOrDefault does not expose nullability on the result type. What's its current state? |
@danielcweber The nullable annotations should be in a good shape now and be consistent with [return: MaybeNull]
public static TSource SingleOrDefault<TSource>(this IObservable<TSource> source); and (see here): [return: MaybeNull]
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) A relevant PR on the dotnet/runtime side is dotnet/runtime#179. |
Also relevant: dotnet/runtime#28086 |
Sorry, meant to say FirstOrDefaultAsync, and the signature should be
shouldn't it ? |
You're right. In fact, The work has to be done in |
Can do. |
For the IQbservable stuff, the HomoIcon tool produces a lot of changes. I guess it hasn't been used for a long time but the generated files have been edited by hand. I could do so as well for the FirstOrDefaultAsync and DefaultIfEmpty methods or leave automatic generation to you. |
Would love to do the work for Ix.Async but I can't build anything. I keep getting NETSDK1004 no matter how much I restore on any sdk (3.1, 5-preview)... |
I'm fine with doing the hand-edits for now. I'm not sure when HomoIcon regressed; it may be a candidate for replacement (maybe using Roslyn source generators though it has its own challenges) and extension (by adding support to carry over nullable annotations). There is a test case as well that checks that the operator surface for both
Haven't seen that yet. Maybe @clairernovotny has some thoughts on what may be going on. |
Got it running. It requires a separate restore on the *.Ref-projects (in subfolder refs). |
The only files left with
#nullable disable
are:Map.cs
,Lookup.cs
,Grouping.cs
due to their dependence onDictionary<TKey, TValue>
which we use with a non-nullTKey
.System.Linq
) anyway.null
case check here, and clean up the use sites a bit.Disposable.Utils.cs
is a potpourri forest ofref
methods which all would needref IDisposable?
parameters because of mixed use cases, e.g. the original public disposables which supportnull
in various places (and have been annotated as such), as well as direct uses in operators where these helpers are even used in constructors, thus breaking definite assignment analysis to prove that fields are non-null
after the constructor exits (i.e. because ofref
rather thanout
with a non-null
parameter).SingleAssignment
,MultipleAssignment
, andSerial
variants of disposables, for example in a struct-based helper. That'd also make the usage more clear, rather than have an operator implementation sprinkled withDisposable.XYZ
methods. Right now, simply looking at the fields of an operator does not suffice to figure out the state transitions of disposables it contains. Furthermore, many use cases of these helpers ignore the return value.