Skip to content
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

Enable nullable reference types #242

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Source/ReactiveProperty.Core/IReactiveProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface IReactiveProperty : IReadOnlyReactiveProperty, IHasErrors, INot
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
new object Value { get; set; }
new object? Value { get; set; }
}

/// <summary>
Expand All @@ -25,6 +25,6 @@ public interface IReactiveProperty<T> : IReactiveProperty, IReadOnlyReactiveProp
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
new T Value { get; set; }
new T? Value { get; set; }
}
}
4 changes: 2 additions & 2 deletions Source/ReactiveProperty.Core/IReadOnlyReactiveProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface IReadOnlyReactiveProperty : INotifyPropertyChanged
/// Gets the value.
/// </summary>
/// <value>The value.</value>
object Value { get; }
object? Value { get; }
}

/// <summary>
Expand All @@ -23,6 +23,6 @@ public interface IReadOnlyReactiveProperty<out T> : IReadOnlyReactiveProperty, I
/// Gets the value.
/// </summary>
/// <value>The value.</value>
new T Value { get; }
new T? Value { get; }
}
}
22 changes: 11 additions & 11 deletions Source/ReactiveProperty.Core/Internals/AccessorCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace Reactive.Bindings.Internals
/// <typeparam name="TType">The type of the type.</typeparam>
internal static class AccessorCache<TType>
{
private static readonly Dictionary<string, Delegate> s_getCache = new Dictionary<string, Delegate>();
private static readonly Dictionary<string, Delegate> s_setCache = new Dictionary<string, Delegate>();
private static readonly Dictionary<string, Delegate> s_getCache = new();
private static readonly Dictionary<string, Delegate> s_setCache = new();

/// <summary>
/// Lookups the get.
Expand All @@ -24,7 +24,7 @@ internal static class AccessorCache<TType>
public static Func<TType, TProperty> LookupGet<TProperty>(Expression<Func<TType, TProperty>> propertySelector, out string propertyName)
{
propertyName = ExpressionTreeUtils.GetPropertyName(propertySelector);
Delegate accessor;
Delegate? accessor;

lock (s_getCache)
{
Expand All @@ -48,7 +48,7 @@ public static Func<TType, TProperty> LookupGet<TProperty>(Expression<Func<TType,
public static Func<TType, TProperty> LookupNestedGet<TProperty>(Expression<Func<TType, TProperty>> propertySelector, out string propertyName)
{
propertyName = ExpressionTreeUtils.GetPropertyPath(propertySelector);
Delegate accessor;
Delegate? accessor;

lock (s_getCache)
{
Expand All @@ -69,10 +69,10 @@ public static Func<TType, TProperty> LookupNestedGet<TProperty>(Expression<Func<
/// <param name="propertySelector">The property selector.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public static Action<TType, TProperty> LookupSet<TProperty>(Expression<Func<TType, TProperty>> propertySelector, out string propertyName)
public static Action<TType, TProperty?> LookupSet<TProperty>(Expression<Func<TType, TProperty?>> propertySelector, out string propertyName)
{
propertyName = ExpressionTreeUtils.GetPropertyName(propertySelector);
Delegate accessor;
Delegate? accessor;

lock (s_setCache)
{
Expand All @@ -83,10 +83,10 @@ public static Action<TType, TProperty> LookupSet<TProperty>(Expression<Func<TTyp
}
}

return (Action<TType, TProperty>)accessor;
return (Action<TType, TProperty?>)accessor;
}

private static Delegate CreateSetAccessor<TProperty>(Expression<Func<TType, TProperty>> propertySelector)
private static Delegate CreateSetAccessor<TProperty>(Expression<Func<TType, TProperty?>> propertySelector)
{
var propertyInfo = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;
var selfParameter = Expression.Parameter(typeof(TType), "self");
Expand All @@ -99,9 +99,9 @@ private static Delegate CreateSetAccessor<TProperty>(Expression<Func<TType, TPro

internal static class AccessorCache
{
private static readonly Dictionary<Type, Type> _accessorCacheTypeCache = new Dictionary<Type, Type>();
private static readonly Dictionary<Type, Dictionary<string, Delegate>> _getCache = new Dictionary<Type, Dictionary<string, Delegate>>();
private static readonly Dictionary<Type, Dictionary<string, Delegate>> _setCache = new Dictionary<Type, Dictionary<string, Delegate>>();
private static readonly Dictionary<Type, Type> _accessorCacheTypeCache = new();
private static readonly Dictionary<Type, Dictionary<string, Delegate>> _getCache = new();
private static readonly Dictionary<Type, Dictionary<string, Delegate>> _setCache = new();

private static Dictionary<string, Delegate> GetGetCacheByType(Type type)
{
Expand Down
30 changes: 14 additions & 16 deletions Source/ReactiveProperty.Core/Internals/ExpressionTreeUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,46 @@ internal static class ExpressionTreeUtils
{
public static string GetPropertyPath<TType, TProperty>(Expression<Func<TType, TProperty>> propertySelector)
{
if (!(propertySelector.Body is MemberExpression memberExpression))
{
if (!(propertySelector.Body is UnaryExpression unaryExpression)) { throw new ArgumentException(nameof(propertySelector)); }
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression == null) { throw new ArgumentException(nameof(propertySelector)); }
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xxxxxxxx

var memberExpression = GetMemberExpressionFromPropertySelector(propertySelector);
var tokens = new LinkedList<string>();
while (memberExpression != null)
{
tokens.AddFirst(memberExpression.Member.Name);
memberExpression = memberExpression.Expression as MemberExpression;
if (memberExpression is not MemberExpression nextToken) { break; }
memberExpression = nextToken;
}

return string.Join(".", tokens);
}


public static string GetPropertyName<TType, TProperty>(Expression<Func<TType, TProperty>> propertySelector)
public static string GetPropertyName<TType, TProperty>(Expression<Func<TType, TProperty>> propertySelector) =>
GetMemberExpressionFromPropertySelector(propertySelector).Member.Name;

private static MemberExpression GetMemberExpressionFromPropertySelector<TType, TProperty>(Expression<Func<TType, TProperty>> propertySelector)
{
if (!(propertySelector.Body is MemberExpression memberExpression))
if (propertySelector.Body is not MemberExpression memberExpression)
{
if (!(propertySelector.Body is UnaryExpression unaryExpression)) { throw new ArgumentException(nameof(propertySelector)); }
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression == null) { throw new ArgumentException(nameof(propertySelector)); }
if (propertySelector.Body is not UnaryExpression unaryExpression) { throw new ArgumentException(nameof(propertySelector)); }
if (unaryExpression.Operand is not MemberExpression memberExpressionForUnary) { throw new ArgumentException(nameof(propertySelector)); }
memberExpression = memberExpressionForUnary;
}

return memberExpression.Member.Name;
return memberExpression;
}

public static bool IsNestedPropertyPath<TSubject, TProperty>(Expression<Func<TSubject, TProperty>> propertySelector)
{
if (propertySelector.Body is MemberExpression member)
{
return !(member.Expression is ParameterExpression);
return member.Expression is not ParameterExpression;
};

if (propertySelector.Body is UnaryExpression unary)
{
if (unary.Operand is MemberExpression unaryMember)
{
return !(unaryMember.Expression is ParameterExpression);
return unaryMember.Expression is not ParameterExpression;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Reactive.Bindings.Internals
{
internal interface IObserverLinkedList<T>
{
void UnsubscribeNode(ObserverNode<T> node);
void UnsubscribeNode(ObserverNode<T?> node);
}

}
6 changes: 3 additions & 3 deletions Source/ReactiveProperty.Core/Internals/ObserverNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ internal sealed class ObserverNode<T> : IObserver<T>, IDisposable
private readonly IObserver<T> _observer;
private IObserverLinkedList<T> _list;

public ObserverNode<T> Previous { get; set; }
public ObserverNode<T>? Previous { get; set; }

public ObserverNode<T> Next { get; set; }
public ObserverNode<T>? Next { get; set; }

public ObserverNode(IObserverLinkedList<T> list, IObserver<T> observer)
{
Expand All @@ -38,7 +38,7 @@ public void OnCompleted()
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "<Pending>")]
public void Dispose()
{
var sourceList = Interlocked.Exchange(ref _list, null);
var sourceList = Interlocked.Exchange(ref _list, null!);
if (sourceList != null)
{
sourceList.UnsubscribeNode(this);
Expand Down
3 changes: 2 additions & 1 deletion Source/ReactiveProperty.Core/ReactiveProperty.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

<PropertyGroup>
<PackageId>ReactiveProperty.Core</PackageId>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net5.0</TargetFrameworks>
<AssemblyName>ReactiveProperty.Core</AssemblyName>
<SignAssembly>true</SignAssembly>
<Nullable>enable</Nullable>
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
<Description>ReactiveProperty.Core includes minimum core classes such as ReactivePropertySlim and ReadOnlyReactivePropertySlim.</Description>
</PropertyGroup>
Expand Down
51 changes: 29 additions & 22 deletions Source/ReactiveProperty.Core/ReactivePropertySlim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.ExceptionServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Reactive.Bindings.Internals;

Expand All @@ -18,24 +19,24 @@ public class ReactivePropertySlim<T> : IReactiveProperty<T>, IObserverLinkedList
private const int IsDisposedFlagNumber = 1 << 9; // (reserve 0 ~ 8)

// minimize field count
private T _latestValue;
private T? _latestValue;

private ReactivePropertyMode _mode; // None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2, Disposed = (1 << 9)
private readonly IEqualityComparer<T> _equalityComparer;
private ObserverNode<T> _root;
private ObserverNode<T> _last;
private ObserverNode<T?>? _root;
private ObserverNode<T?>? _last;

/// <summary>
/// Occurs when a property value changes.
/// </summary>
/// <returns></returns>
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;

/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public T Value
public T? Value
{
get
{
Expand All @@ -44,7 +45,15 @@ public T Value

set
{
if (IsDistinctUntilChanged && _equalityComparer.Equals(_latestValue, value))
static bool equals(IEqualityComparer<T> comparer, T? latestValue, T? value) => (latestValue, value) switch
{
(null, null) => true,
(null, { }) => false,
({ }, null) => false,
({ } x, { } y) => comparer.Equals(x, y),
};

if (IsDistinctUntilChanged && equals(_equalityComparer, _latestValue, Value))
{
return;
}
Expand All @@ -64,7 +73,7 @@ public T Value
/// <value><c>true</c> if this instance is disposed; otherwise, <c>false</c>.</value>
public bool IsDisposed => (int)_mode == IsDisposedFlagNumber;

object IReactiveProperty.Value
object? IReactiveProperty.Value
{
get
{
Expand All @@ -73,7 +82,7 @@ object IReactiveProperty.Value

set
{
Value = (T)value;
Value = (T?)value;
}
}

Expand Down Expand Up @@ -105,14 +114,14 @@ object IReadOnlyReactiveProperty.Value
/// <param name="initialValue">The initial value.</param>
/// <param name="mode">The mode.</param>
/// <param name="equalityComparer">The equality comparer.</param>
public ReactivePropertySlim(T initialValue = default, ReactivePropertyMode mode = ReactivePropertyMode.Default, IEqualityComparer<T> equalityComparer = null)
public ReactivePropertySlim(T? initialValue = default, ReactivePropertyMode mode = ReactivePropertyMode.Default, IEqualityComparer<T>? equalityComparer = null)
{
_latestValue = initialValue;
_mode = mode;
_equalityComparer = equalityComparer ?? EqualityComparer<T>.Default;
}

private void OnNextAndRaiseValueChanged(ref T value)
private void OnNextAndRaiseValueChanged(ref T? value)
{
// call source.OnNext
var node = _root;
Expand Down Expand Up @@ -169,7 +178,7 @@ public IDisposable Subscribe(IObserver<T> observer)
return next;
}

void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T> node)
void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T?> node)
{
if (node == _root)
{
Expand Down Expand Up @@ -231,7 +240,7 @@ public override string ToString()

IObservable<bool> IHasErrors.ObserveHasErrors => throw new NotSupportedException();

event EventHandler<DataErrorsChangedEventArgs> INotifyDataErrorInfo.ErrorsChanged
event EventHandler<DataErrorsChangedEventArgs>? INotifyDataErrorInfo.ErrorsChanged
{
add
{
Expand All @@ -241,7 +250,7 @@ event EventHandler<DataErrorsChangedEventArgs> INotifyDataErrorInfo.ErrorsChange
}
}

IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
IEnumerable INotifyDataErrorInfo.GetErrors(string? propertyName)
{
return System.Linq.Enumerable.Empty<object>();
}
Expand All @@ -251,26 +260,25 @@ IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <seealso cref="Reactive.Bindings.IReadOnlyReactiveProperty{T}"/>
/// <seealso cref="Reactive.Bindings.IObserverLinkedList{T}"/>
/// <seealso cref="System.IObserver{T}"/>
public class ReadOnlyReactivePropertySlim<T> : IReadOnlyReactiveProperty<T>, IObserverLinkedList<T>, IObserver<T>
{
private const int IsDisposedFlagNumber = 1 << 9; // (reserve 0 ~ 8)

// minimize field count
private T _latestValue;
private T? _latestValue;

private IDisposable _sourceSubscription;
private ReactivePropertyMode _mode; // None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2, Disposed = (1 << 9)
private readonly IEqualityComparer<T> _equalityComparer;
private ObserverNode<T> _root;
private ObserverNode<T> _last;
private ObserverNode<T>? _root;
private ObserverNode<T>? _last;

/// <summary>
/// Occurs when a property value changes.
/// </summary>
/// <returns></returns>
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;

/// <summary>
/// Gets the value.
Expand Down Expand Up @@ -305,7 +313,7 @@ public T Value
/// <param name="initialValue">The initial value.</param>
/// <param name="mode">The mode.</param>
/// <param name="equalityComparer">The equality comparer.</param>
public ReadOnlyReactivePropertySlim(IObservable<T> source, T initialValue = default, ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer<T> equalityComparer = null)
public ReadOnlyReactivePropertySlim(IObservable<T> source, T? initialValue = default, ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer<T>? equalityComparer = null)
{
_latestValue = initialValue;
_mode = mode;
Expand Down Expand Up @@ -355,7 +363,7 @@ public IDisposable Subscribe(IObserver<T> observer)
return next;
}

void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T> node)
void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T?> node)
{
if (node == _root)
{
Expand Down Expand Up @@ -455,7 +463,6 @@ public override string ToString()
/// <summary>
/// </summary>
/// <seealso cref="Reactive.Bindings.IReadOnlyReactiveProperty{T}"/>
/// <seealso cref="Reactive.Bindings.IObserverLinkedList{T}"/>
/// <seealso cref="System.IObserver{T}"/>
public static class ReadOnlyReactivePropertySlim
{
Expand All @@ -468,7 +475,7 @@ public static class ReadOnlyReactivePropertySlim
/// <param name="mode">The mode.</param>
/// <param name="equalityComparer">The equality comparer.</param>
/// <returns></returns>
public static ReadOnlyReactivePropertySlim<T> ToReadOnlyReactivePropertySlim<T>(this IObservable<T> source, T initialValue = default, ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer<T> equalityComparer = null)
public static ReadOnlyReactivePropertySlim<T> ToReadOnlyReactivePropertySlim<T>(this IObservable<T> source, T? initialValue = default, ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer<T>? equalityComparer = null)
{
return new ReadOnlyReactivePropertySlim<T>(source, initialValue, mode, equalityComparer);
}
Expand Down
Loading