From b966bd390c7d310590ef98baa0c9aa69440cacce Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 23 Feb 2020 15:34:04 +0100 Subject: [PATCH] Enable NRT in Avalonia.Interactivity. --- .../Avalonia.Interactivity.csproj | 6 +- .../EventSubscription.cs | 20 ++++-- src/Avalonia.Interactivity/IInteractive.cs | 2 +- src/Avalonia.Interactivity/Interactive.cs | 68 ++++++++----------- .../InteractiveExtensions.cs | 5 +- src/Avalonia.Interactivity/RoutedEvent.cs | 18 ++--- src/Avalonia.Interactivity/RoutedEventArgs.cs | 8 +-- .../RoutedEventRegistry.cs | 6 +- 8 files changed, 69 insertions(+), 64 deletions(-) diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj index 66f1e8cc26..730ca2bd6e 100644 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj @@ -1,6 +1,8 @@  netstandard2.0 + Enable + CS8600;CS8602;CS8603 @@ -9,6 +11,4 @@ - - - + \ No newline at end of file diff --git a/src/Avalonia.Interactivity/EventSubscription.cs b/src/Avalonia.Interactivity/EventSubscription.cs index e8fb1bfaf1..d363e3f6fa 100644 --- a/src/Avalonia.Interactivity/EventSubscription.cs +++ b/src/Avalonia.Interactivity/EventSubscription.cs @@ -9,12 +9,24 @@ namespace Avalonia.Interactivity internal class EventSubscription { - public HandlerInvokeSignature InvokeAdapter { get; set; } + public EventSubscription( + Delegate handler, + RoutingStrategies routes, + bool handledEventsToo, + HandlerInvokeSignature? invokeAdapter = null) + { + Handler = handler; + Routes = routes; + HandledEventsToo = handledEventsToo; + InvokeAdapter = invokeAdapter; + } - public Delegate Handler { get; set; } + public HandlerInvokeSignature? InvokeAdapter { get; } - public RoutingStrategies Routes { get; set; } + public Delegate Handler { get; } - public bool AlsoIfHandled { get; set; } + public RoutingStrategies Routes { get; } + + public bool HandledEventsToo { get; } } } diff --git a/src/Avalonia.Interactivity/IInteractive.cs b/src/Avalonia.Interactivity/IInteractive.cs index 47046b58e2..6524794733 100644 --- a/src/Avalonia.Interactivity/IInteractive.cs +++ b/src/Avalonia.Interactivity/IInteractive.cs @@ -13,7 +13,7 @@ namespace Avalonia.Interactivity /// /// Gets the interactive parent of the object for bubbling and tunneling events. /// - IInteractive InteractiveParent { get; } + IInteractive? InteractiveParent { get; } /// /// Adds a handler for the specified routed event. diff --git a/src/Avalonia.Interactivity/Interactive.cs b/src/Avalonia.Interactivity/Interactive.cs index 27ece25183..0c4649a1ca 100644 --- a/src/Avalonia.Interactivity/Interactive.cs +++ b/src/Avalonia.Interactivity/Interactive.cs @@ -15,16 +15,16 @@ namespace Avalonia.Interactivity /// public class Interactive : Layoutable, IInteractive { - private Dictionary> _eventHandlers; + private Dictionary>? _eventHandlers; private static readonly Dictionary s_invokeHandlerCache = new Dictionary(); /// /// Gets the interactive parent of the object for bubbling and tunneling events. /// - IInteractive IInteractive.InteractiveParent => ((IVisual)this).VisualParent as IInteractive; + IInteractive? IInteractive.InteractiveParent => ((IVisual)this).VisualParent as IInteractive; - private Dictionary> EventHandlers => _eventHandlers ?? (_eventHandlers = new Dictionary>()); + private Dictionary> EventHandlers => _eventHandlers ??= new Dictionary>(); /// /// Adds a handler for the specified routed event. @@ -40,16 +40,10 @@ namespace Avalonia.Interactivity RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, bool handledEventsToo = false) { - Contract.Requires(routedEvent != null); - Contract.Requires(handler != null); - - var subscription = new EventSubscription - { - Handler = handler, - Routes = routes, - AlsoIfHandled = handledEventsToo, - }; + routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); + handler = handler ?? throw new ArgumentNullException(nameof(handler)); + var subscription = new EventSubscription(handler, routes, handledEventsToo); return AddEventSubscription(routedEvent, subscription); } @@ -68,12 +62,12 @@ namespace Avalonia.Interactivity RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, bool handledEventsToo = false) where TEventArgs : RoutedEventArgs { - Contract.Requires(routedEvent != null); - Contract.Requires(handler != null); + routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); + handler = handler ?? throw new ArgumentNullException(nameof(handler)); // EventHandler delegate is not covariant, this forces us to create small wrapper // that will cast our type erased instance and invoke it. - Type eventArgsType = routedEvent.EventArgsType; + var eventArgsType = routedEvent.EventArgsType; if (!s_invokeHandlerCache.TryGetValue(eventArgsType, out var invokeAdapter)) { @@ -90,14 +84,7 @@ namespace Avalonia.Interactivity s_invokeHandlerCache.Add(eventArgsType, invokeAdapter); } - var subscription = new EventSubscription - { - InvokeAdapter = invokeAdapter, - Handler = handler, - Routes = routes, - AlsoIfHandled = handledEventsToo, - }; - + var subscription = new EventSubscription(handler, routes, handledEventsToo, invokeAdapter); return AddEventSubscription(routedEvent, subscription); } @@ -108,12 +95,11 @@ namespace Avalonia.Interactivity /// The handler. public void RemoveHandler(RoutedEvent routedEvent, Delegate handler) { - Contract.Requires(routedEvent != null); - Contract.Requires(handler != null); - - List subscriptions = null; + routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); + handler = handler ?? throw new ArgumentNullException(nameof(handler)); - if (_eventHandlers?.TryGetValue(routedEvent, out subscriptions) == true) + if (_eventHandlers is object && + _eventHandlers.TryGetValue(routedEvent, out var subscriptions) == true) { subscriptions.RemoveAll(x => x.Handler == handler); } @@ -137,9 +123,14 @@ namespace Avalonia.Interactivity /// The event args. public void RaiseEvent(RoutedEventArgs e) { - Contract.Requires(e != null); + e = e ?? throw new ArgumentNullException(nameof(e)); - e.Source = e.Source ?? this; + if (e.RoutedEvent == null) + { + throw new ArgumentException("Cannot raise an event whose RoutedEvent is null."); + } + + e.Source ??= this; if (e.RoutedEvent.RoutingStrategies == RoutingStrategies.Direct) { @@ -167,7 +158,7 @@ namespace Avalonia.Interactivity /// The event args. private void BubbleEvent(RoutedEventArgs e) { - Contract.Requires(e != null); + e = e ?? throw new ArgumentNullException(nameof(e)); e.Route = RoutingStrategies.Bubble; @@ -182,7 +173,7 @@ namespace Avalonia.Interactivity /// The event args. private void TunnelEvent(RoutedEventArgs e) { - Contract.Requires(e != null); + e = e ?? throw new ArgumentNullException(nameof(e)); e.Route = RoutingStrategies.Tunnel; @@ -197,18 +188,17 @@ namespace Avalonia.Interactivity /// The event args. private void RaiseEventImpl(RoutedEventArgs e) { - Contract.Requires(e != null); - - e.RoutedEvent.InvokeRaised(this, e); + e = e ?? throw new ArgumentNullException(nameof(e)); - List subscriptions = null; + e.RoutedEvent!.InvokeRaised(this, e); - if (_eventHandlers?.TryGetValue(e.RoutedEvent, out subscriptions) == true) + if (_eventHandlers is object && + _eventHandlers.TryGetValue(e.RoutedEvent, out var subscriptions) == true) { foreach (var sub in subscriptions.ToList()) { bool correctRoute = (e.Route & sub.Routes) != 0; - bool notFinished = !e.Handled || sub.AlsoIfHandled; + bool notFinished = !e.Handled || sub.HandledEventsToo; if (correctRoute && notFinished) { @@ -313,7 +303,7 @@ namespace Avalonia.Interactivity { _preTraverse.Execute(target, _args); - IInteractive parent = target.InteractiveParent; + var parent = target.InteractiveParent; if (parent != null) { diff --git a/src/Avalonia.Interactivity/InteractiveExtensions.cs b/src/Avalonia.Interactivity/InteractiveExtensions.cs index 07e4029240..414c408080 100644 --- a/src/Avalonia.Interactivity/InteractiveExtensions.cs +++ b/src/Avalonia.Interactivity/InteractiveExtensions.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; -using System.Linq; using System.Reactive.Linq; namespace Avalonia.Interactivity @@ -30,6 +28,9 @@ namespace Avalonia.Interactivity bool handledEventsToo = false) where TEventArgs : RoutedEventArgs { + o = o ?? throw new ArgumentNullException(nameof(o)); + routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); + return Observable.Create(x => o.AddHandler( routedEvent, (_, e) => x.OnNext(e), diff --git a/src/Avalonia.Interactivity/RoutedEvent.cs b/src/Avalonia.Interactivity/RoutedEvent.cs index 55d9e61d87..164a86fab7 100644 --- a/src/Avalonia.Interactivity/RoutedEvent.cs +++ b/src/Avalonia.Interactivity/RoutedEvent.cs @@ -25,10 +25,14 @@ namespace Avalonia.Interactivity Type eventArgsType, Type ownerType) { - Contract.Requires(name != null); - Contract.Requires(eventArgsType != null); - Contract.Requires(ownerType != null); - Contract.Requires(typeof(RoutedEventArgs).IsAssignableFrom(eventArgsType)); + name = name ?? throw new ArgumentNullException(nameof(name)); + eventArgsType = eventArgsType ?? throw new ArgumentNullException(nameof(name)); + ownerType = ownerType ?? throw new ArgumentNullException(nameof(name)); + + if (!typeof(RoutedEventArgs).IsAssignableFrom(eventArgsType)) + { + throw new InvalidCastException("eventArgsType must be derived from RoutedEventArgs."); + } EventArgsType = eventArgsType; Name = name; @@ -52,7 +56,7 @@ namespace Avalonia.Interactivity RoutingStrategies routingStrategy) where TEventArgs : RoutedEventArgs { - Contract.Requires(name != null); + name = name ?? throw new ArgumentNullException(nameof(name)); var routedEvent = new RoutedEvent(name, routingStrategy, typeof(TOwner)); RoutedEventRegistry.Instance.Register(typeof(TOwner), routedEvent); @@ -65,7 +69,7 @@ namespace Avalonia.Interactivity Type ownerType) where TEventArgs : RoutedEventArgs { - Contract.Requires(name != null); + name = name ?? throw new ArgumentNullException(nameof(name)); var routedEvent = new RoutedEvent(name, routingStrategy, ownerType); RoutedEventRegistry.Instance.Register(ownerType, routedEvent); @@ -108,8 +112,6 @@ namespace Avalonia.Interactivity public RoutedEvent(string name, RoutingStrategies routingStrategies, Type ownerType) : base(name, routingStrategies, typeof(TEventArgs), ownerType) { - Contract.Requires(name != null); - Contract.Requires(ownerType != null); } [Obsolete("Use overload taking Action.")] diff --git a/src/Avalonia.Interactivity/RoutedEventArgs.cs b/src/Avalonia.Interactivity/RoutedEventArgs.cs index 05bbf7b6a3..e00393322d 100644 --- a/src/Avalonia.Interactivity/RoutedEventArgs.cs +++ b/src/Avalonia.Interactivity/RoutedEventArgs.cs @@ -11,12 +11,12 @@ namespace Avalonia.Interactivity { } - public RoutedEventArgs(RoutedEvent routedEvent) + public RoutedEventArgs(RoutedEvent? routedEvent) { RoutedEvent = routedEvent; } - public RoutedEventArgs(RoutedEvent routedEvent, IInteractive source) + public RoutedEventArgs(RoutedEvent? routedEvent, IInteractive? source) { RoutedEvent = routedEvent; Source = source; @@ -24,10 +24,10 @@ namespace Avalonia.Interactivity public bool Handled { get; set; } - public RoutedEvent RoutedEvent { get; set; } + public RoutedEvent? RoutedEvent { get; set; } public RoutingStrategies Route { get; set; } - public IInteractive Source { get; set; } + public IInteractive? Source { get; set; } } } diff --git a/src/Avalonia.Interactivity/RoutedEventRegistry.cs b/src/Avalonia.Interactivity/RoutedEventRegistry.cs index 34c970a806..0111b115e6 100644 --- a/src/Avalonia.Interactivity/RoutedEventRegistry.cs +++ b/src/Avalonia.Interactivity/RoutedEventRegistry.cs @@ -32,8 +32,8 @@ namespace Avalonia.Interactivity /// public void Register(Type type, RoutedEvent @event) { - Contract.Requires(type != null); - Contract.Requires(@event != null); + type = type ?? throw new ArgumentNullException(nameof(type)); + @event = @event ?? throw new ArgumentNullException(nameof(@event)); if (!_registeredRoutedEvents.TryGetValue(type, out var list)) { @@ -66,7 +66,7 @@ namespace Avalonia.Interactivity /// All routed events registered with the provided type. public IReadOnlyList GetRegistered(Type type) { - Contract.Requires(type != null); + type = type ?? throw new ArgumentNullException(nameof(type)); if (_registeredRoutedEvents.TryGetValue(type, out var events)) {