Make all event types nullable (#25752)
authorStephen Toub <stoub@microsoft.com>
Wed, 17 Jul 2019 20:38:09 +0000 (16:38 -0400)
committerStephen Toub <stoub@microsoft.com>
Thu, 18 Jul 2019 18:25:53 +0000 (14:25 -0400)
Late-breaking design change: all events should be declared with nullable delegate types.

13 files changed:
src/System.Private.CoreLib/shared/System/AppContext.cs
src/System.Private.CoreLib/shared/System/AppDomain.cs
src/System.Private.CoreLib/shared/System/Diagnostics/Contracts/Contracts.cs
src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
src/System.Private.CoreLib/shared/System/Exception.cs
src/System.Private.CoreLib/shared/System/Progress.cs
src/System.Private.CoreLib/shared/System/Reflection/Assembly.cs
src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ContractHelper.cs
src/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs
src/System.Private.CoreLib/shared/System/Runtime/Serialization/ISafeSerializationData.cs
src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskScheduler.cs
src/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs

index 605326c..62e9dbd 100644 (file)
@@ -63,12 +63,12 @@ namespace System
         }
 
 #pragma warning disable CS0067 // events raised by the VM
-        public static event UnhandledExceptionEventHandler UnhandledException; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public static event UnhandledExceptionEventHandler? UnhandledException;
 
-        public static event System.EventHandler<FirstChanceExceptionEventArgs> FirstChanceException; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public static event System.EventHandler<FirstChanceExceptionEventArgs>? FirstChanceException;
 #pragma warning restore CS0067
 
-        public static event System.EventHandler ProcessExit; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public static event System.EventHandler? ProcessExit;
 
         internal static void OnProcessExit()
         {
index 6795e55..addef77 100644 (file)
@@ -2,7 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-#pragma warning disable CS0414 // events are assigned but not used
+#pragma warning disable CS0067 // events are declared but not used
 
 using System.Diagnostics;
 using System.IO;
@@ -41,7 +41,7 @@ namespace System
 
         public PermissionSet PermissionSet => new PermissionSet(PermissionState.Unrestricted);
 
-        public event UnhandledExceptionEventHandler UnhandledException // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event UnhandledExceptionEventHandler? UnhandledException
         {
             add { AppContext.UnhandledException += value; }
             remove { AppContext.UnhandledException -= value; }
@@ -67,15 +67,15 @@ namespace System
 
         public bool IsHomogenous => true;
 
-        public event EventHandler DomainUnload = null!; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler? DomainUnload;
 
-        public event EventHandler<FirstChanceExceptionEventArgs> FirstChanceException // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler<FirstChanceExceptionEventArgs>? FirstChanceException
         {
             add { AppContext.FirstChanceException += value; }
             remove { AppContext.FirstChanceException -= value; }
         }
 
-        public event EventHandler ProcessExit // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler? ProcessExit
         {
             add { AppContext.ProcessExit += value; }
             remove { AppContext.ProcessExit -= value; }
@@ -232,27 +232,27 @@ namespace System
 
         public Assembly[] GetAssemblies() => AssemblyLoadContext.GetLoadedAssemblies();
 
-        public event AssemblyLoadEventHandler AssemblyLoad // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event AssemblyLoadEventHandler? AssemblyLoad
         {
             add { AssemblyLoadContext.AssemblyLoad += value; }
             remove { AssemblyLoadContext.AssemblyLoad -= value; }
         }
 
-        public event ResolveEventHandler AssemblyResolve // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event ResolveEventHandler? AssemblyResolve
         {
             add { AssemblyLoadContext.AssemblyResolve += value; }
             remove { AssemblyLoadContext.AssemblyResolve -= value; }
         }
 
-        public event ResolveEventHandler ReflectionOnlyAssemblyResolve = null!; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event ResolveEventHandler? ReflectionOnlyAssemblyResolve;
 
-        public event ResolveEventHandler TypeResolve // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event ResolveEventHandler? TypeResolve
         {
             add { AssemblyLoadContext.TypeResolve += value; }
             remove { AssemblyLoadContext.TypeResolve -= value; }
         }
 
-        public event ResolveEventHandler ResourceResolve // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event ResolveEventHandler? ResourceResolve
         {
             add { AssemblyLoadContext.ResourceResolve += value; }
             remove { AssemblyLoadContext.ResourceResolve -= value; }
index df338d1..30d5b9e 100644 (file)
@@ -703,7 +703,7 @@ namespace System.Diagnostics.Contracts
         /// full trust, because it will inform you of bugs in the appdomain and because the event handler
         /// could allow you to continue execution.
         /// </summary>
-        public static event EventHandler<ContractFailedEventArgs> ContractFailed // TODO-NULLABLE: Should all events use nullable delegate types?
+        public static event EventHandler<ContractFailedEventArgs>? ContractFailed
         {
             add
             {
index 58e74a1..93687a6 100644 (file)
@@ -479,10 +479,13 @@ namespace System.Diagnostics.Tracing
         /// <summary>
         /// Fires when a Command (e.g. Enable) comes from a an EventListener.  
         /// </summary>
-        public event EventHandler<EventCommandEventArgs> EventCommandExecuted // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler<EventCommandEventArgs>? EventCommandExecuted
         {
             add
             {
+                if (value == null)
+                    return;
+
                 m_eventCommandExecuted += value;
 
                 // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
@@ -4042,7 +4045,7 @@ namespace System.Diagnostics.Tracing
         /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback' 
         /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
         /// </summary>
-        public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler<EventSourceCreatedEventArgs>? EventSourceCreated
         {
             add
             {
@@ -4060,7 +4063,7 @@ namespace System.Diagnostics.Tracing
         /// This event is raised whenever an event has been written by a EventSource for which 
         /// the EventListener has enabled events.  
         /// </summary>
-        public event EventHandler<EventWrittenEventArgs> EventWritten = null!; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler<EventWrittenEventArgs>? EventWritten;
 
         static EventListener()
         {
@@ -4251,7 +4254,7 @@ namespace System.Diagnostics.Tracing
         /// <param name="eventData"></param>
         internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
         {
-            EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
+            EventHandler<EventWrittenEventArgs>? callBack = this.EventWritten;
             if (callBack != null)
             {
                 callBack(this, eventData);
@@ -4483,7 +4486,7 @@ namespace System.Diagnostics.Tracing
             }
         }
 
-        private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
+        private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs>? callback)
         {
             lock (EventListenersLock)
             {
@@ -4507,36 +4510,40 @@ namespace System.Diagnostics.Tracing
                         s_Listeners = this;
                     }
 
-                    // Find all existing eventSources call OnEventSourceCreated to 'catchup'
-                    // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback) 
-                    // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
-                    // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
-                    // is created.
-                    WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
+                    if (callback != null)
+                    {
+                        // Find all existing eventSources call OnEventSourceCreated to 'catchup'
+                        // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback) 
+                        // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
+                        // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
+                        // is created.
+                        WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
 
 #if DEBUG
-                    bool previousValue = s_ConnectingEventSourcesAndListener;
-                    s_ConnectingEventSourcesAndListener = true;
-                    try
-                    {
-#endif
-                        for (int i = 0; i < eventSourcesSnapshot.Length; i++)
+                        bool previousValue = s_ConnectingEventSourcesAndListener;
+                        s_ConnectingEventSourcesAndListener = true;
+                        try
                         {
-                            WeakReference eventSourceRef = eventSourcesSnapshot[i];
-                            if (eventSourceRef.Target is EventSource eventSource)
+#endif
+                            for (int i = 0; i < eventSourcesSnapshot.Length; i++)
                             {
-                                EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
-                                args.EventSource = eventSource;
-                                callback(this, args);
+                                WeakReference eventSourceRef = eventSourcesSnapshot[i];
+                                if (eventSourceRef.Target is EventSource eventSource)
+                                {
+                                    EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
+                                    args.EventSource = eventSource;
+                                    callback(this, args);
+                                }
                             }
-                        }
 #if DEBUG
-                    }
-                    finally
-                    {
-                        s_ConnectingEventSourcesAndListener = previousValue;
-                    }
+                        }
+                        finally
+                        {
+                            s_ConnectingEventSourcesAndListener = previousValue;
+                        }
 #endif
+                    }
+
                     Validate();
                 }
                 finally
index 80dec57..a5b92f5 100644 (file)
@@ -166,7 +166,7 @@ namespace System
             return s;
         }
 
-        protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState // TODO-NULLABLE: Should all events use nullable delegate types?
+        protected event EventHandler<SafeSerializationEventArgs>? SerializeObjectState
         {
             add { throw new PlatformNotSupportedException(SR.PlatformNotSupported_SecureBinarySerialization); }
             remove { throw new PlatformNotSupportedException(SR.PlatformNotSupported_SecureBinarySerialization); }
index 36095c9..d6a2146 100644 (file)
@@ -56,7 +56,7 @@ namespace System
         /// Handlers registered with this event will be invoked on the 
         /// <see cref="System.Threading.SynchronizationContext"/> captured when the instance was constructed.
         /// </remarks>
-        public event EventHandler<T> ProgressChanged = null!; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event EventHandler<T>? ProgressChanged;
 
         /// <summary>Reports a progress change.</summary>
         /// <param name="value">The value of the updated progress.</param>
@@ -66,7 +66,7 @@ namespace System
             // Inside the callback, we'll need to check again, in case 
             // an event handler is removed between now and then.
             Action<T>? handler = _handler;
-            EventHandler<T> changedEvent = ProgressChanged;
+            EventHandler<T>? changedEvent = ProgressChanged;
             if (handler != null || changedEvent != null)
             {
                 // Post the processing to the sync context.
@@ -86,7 +86,7 @@ namespace System
             T value = (T)state!;
 
             Action<T>? handler = _handler;
-            EventHandler<T> changedEvent = ProgressChanged;
+            EventHandler<T>? changedEvent = ProgressChanged;
 
             if (handler != null) handler(value);
             if (changedEvent != null) changedEvent(this, value);
index 0b7a189..b1e3e32 100644 (file)
@@ -117,7 +117,7 @@ namespace System.Reflection
             return Activator.CreateInstance(t, bindingAttr, binder, args, culture, activationAttributes);
         }
 
-        public virtual event ModuleResolveEventHandler ModuleResolve { add { throw NotImplemented.ByDesign; } remove { throw NotImplemented.ByDesign; } } // TODO-NULLABLE: Should all events use nullable delegate types?
+        public virtual event ModuleResolveEventHandler? ModuleResolve { add { throw NotImplemented.ByDesign; } remove { throw NotImplemented.ByDesign; } }
 
         public virtual Module ManifestModule { get { throw NotImplemented.ByDesign; } }
         public virtual Module? GetModule(string name) { throw NotImplemented.ByDesign; }
index e654458..10aa45b 100644 (file)
@@ -17,7 +17,7 @@ namespace System.Runtime.CompilerServices
         /// event handlers sets the Cancel flag in the ContractFailedEventArgs, then the Contract class will
         /// not pop up an assert dialog box or trigger escalation policy.
         /// </summary>
-        internal static event EventHandler<ContractFailedEventArgs> InternalContractFailed; // TODO-NULLABLE: Should all events use nullable delegate types?
+        internal static event EventHandler<ContractFailedEventArgs>? InternalContractFailed;
 
         /// <summary>
         /// Rewriter will call this method on a contract failure to allow listeners to be notified.
@@ -42,7 +42,7 @@ namespace System.Runtime.CompilerServices
             try
             {
                 displayMessage = GetDisplayMessage(failureKind, userMessage, conditionText);
-                EventHandler<ContractFailedEventArgs> contractFailedEventLocal = InternalContractFailed;
+                EventHandler<ContractFailedEventArgs>? contractFailedEventLocal = InternalContractFailed;
                 if (contractFailedEventLocal != null)
                 {
                     eventArgs = new ContractFailedEventArgs(failureKind, displayMessage, conditionText, innerException);
index 1c19e09..84d81fe 100644 (file)
@@ -38,11 +38,11 @@ namespace System.Runtime.Loader
         // synchronization primitive to protect against usage of this instance while unloading
         private readonly object _unloadLock;
 
-        private event Func<Assembly, string, IntPtr> _resolvingUnmanagedDll = null!;
+        private event Func<Assembly, string, IntPtr>? _resolvingUnmanagedDll;
 
-        private event Func<AssemblyLoadContext, AssemblyName, Assembly> _resolving = null!;
+        private event Func<AssemblyLoadContext, AssemblyName, Assembly>? _resolving;
 
-        private event Action<AssemblyLoadContext> _unloading = null!;
+        private event Action<AssemblyLoadContext>? _unloading;
 
         private readonly string? _name;
 
@@ -168,7 +168,7 @@ namespace System.Runtime.Loader
         //
         // Inputs: Invoking assembly, and library name to resolve
         // Returns: A handle to the loaded native library
-        public event Func<Assembly, string, IntPtr> ResolvingUnmanagedDll // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event Func<Assembly, string, IntPtr>? ResolvingUnmanagedDll
         {
             add
             {
@@ -186,7 +186,7 @@ namespace System.Runtime.Loader
         //
         // Inputs: The AssemblyLoadContext and AssemblyName to be loaded
         // Returns: The Loaded assembly object.
-        public event Func<AssemblyLoadContext, AssemblyName, Assembly?> Resolving // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event Func<AssemblyLoadContext, AssemblyName, Assembly?>? Resolving
         {
             add
             {
@@ -198,7 +198,7 @@ namespace System.Runtime.Loader
             }
         }
 
-        public event Action<AssemblyLoadContext> Unloading // TODO-NULLABLE: Should all events use nullable delegate types?
+        public event Action<AssemblyLoadContext>? Unloading
         {
             add
             {
@@ -212,17 +212,17 @@ namespace System.Runtime.Loader
 
 #region AppDomainEvents
         // Occurs when an Assembly is loaded
-        internal static event AssemblyLoadEventHandler AssemblyLoad; // TODO-NULLABLE: Should all events use nullable delegate types?
+        internal static event AssemblyLoadEventHandler? AssemblyLoad;
 
         // Occurs when resolution of type fails
-        internal static event ResolveEventHandler TypeResolve; // TODO-NULLABLE: Should all events use nullable delegate types?
+        internal static event ResolveEventHandler? TypeResolve;
 
         // Occurs when resolution of resource fails
-        internal static event ResolveEventHandler ResourceResolve; // TODO-NULLABLE: Should all events use nullable delegate types?
+        internal static event ResolveEventHandler? ResourceResolve;
 
         // Occurs when resolution of assembly fails
         // This event is fired after resolve events of AssemblyLoadContext fails
-        internal static event ResolveEventHandler AssemblyResolve; // TODO-NULLABLE: Should all events use nullable delegate types?
+        internal static event ResolveEventHandler? AssemblyResolve;
 #endregion
 
         public static AssemblyLoadContext Default => DefaultAssemblyLoadContext.s_loadContext;
index 5089d13..e1ccc11 100644 (file)
@@ -139,7 +139,7 @@ namespace System.Runtime.Serialization
     //     
     //    2. Add a protected SerializeObjectState event, which passes through to the SafeSerializationManager:
     //  
-    //       protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState
+    //       protected event EventHandler<SafeSerializationEventArgs>? SerializeObjectState
     //       {
     //           add { _safeSerializationManager.SerializeObjectState += value; }
     //           remove { _safeSerializationManager.SerializeObjectState -= value; }
index 4c7a3c4..d85f8d7 100644 (file)
@@ -445,7 +445,7 @@ namespace System.Threading.Tasks
         /// Each handler is passed a <see cref="T:System.Threading.Tasks.UnobservedTaskExceptionEventArgs"/>
         /// instance, which may be used to examine the exception and to mark it as observed.
         /// </remarks>
-        public static event EventHandler<UnobservedTaskExceptionEventArgs> UnobservedTaskException; // TODO-NULLABLE: Should all events use nullable delegate types?
+        public static event EventHandler<UnobservedTaskExceptionEventArgs>? UnobservedTaskException;
 
         ////////////////////////////////////////////////////////////
         //
index e55d466..15ceb0b 100644 (file)
@@ -21,7 +21,7 @@ namespace System.Reflection
         internal RuntimeAssembly() { throw new NotSupportedException(); }
 
         #region private data members
-        private event ModuleResolveEventHandler _ModuleResolve;
+        private event ModuleResolveEventHandler? _ModuleResolve;
         private string? m_fullname;
         private object? m_syncRoot;   // Used to keep collectible types alive and as the syncroot for reflection.emit
 #pragma warning disable 169
@@ -54,7 +54,7 @@ namespace System.Reflection
             }
         }
 
-        public override event ModuleResolveEventHandler ModuleResolve // TODO-NULLABLE: Should all events use nullable delegate types?
+        public override event ModuleResolveEventHandler? ModuleResolve
         {
             add
             {
@@ -602,7 +602,7 @@ namespace System.Reflection
         // This method is called by the VM.
         private RuntimeModule? OnModuleResolveEvent(string moduleName)
         {
-            ModuleResolveEventHandler moduleResolve = _ModuleResolve;
+            ModuleResolveEventHandler? moduleResolve = _ModuleResolve;
             if (moduleResolve == null)
                 return null;
 
index f17b044..fb6fe60 100644 (file)
@@ -114,7 +114,7 @@ namespace System.Runtime.Loader
         {
             Assembly? resolvedAssembly = null;
 
-            Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = _resolving;
+            Func<AssemblyLoadContext, AssemblyName, Assembly>? assemblyResolveHandler = _resolving;
 
             if (assemblyResolveHandler != null)
             {
@@ -209,7 +209,7 @@ namespace System.Runtime.Loader
         {
             IntPtr resolvedDll = IntPtr.Zero;
 
-            Func<Assembly, string, IntPtr> dllResolveHandler = _resolvingUnmanagedDll;
+            Func<Assembly, string, IntPtr>? dllResolveHandler = _resolvingUnmanagedDll;
 
             if (dllResolveHandler != null)
             {
@@ -319,7 +319,7 @@ namespace System.Runtime.Loader
             return InvokeResolveEvent(AssemblyResolve, assembly, assemblyFullName);
         }
 
-        private static RuntimeAssembly? InvokeResolveEvent(ResolveEventHandler eventHandler, RuntimeAssembly assembly, string name)
+        private static RuntimeAssembly? InvokeResolveEvent(ResolveEventHandler? eventHandler, RuntimeAssembly assembly, string name)
         {
             if (eventHandler == null)
                 return null;