The EventHandlerType
property of the System.Reflection.EventInfo
class is declared as returning a nullable Type?
:
EventInfo.EventHandlerType Property
public virtual Type? EventHandlerType { get; }
Surely any event must have a type -- so why is the return value nullable?
If I inspect the disassembly of an implementation, I think I see that it cannot return null:
namespace System.Reflection.TypeLoading
{
/// <summary>
/// Base class for all EventInfo objects created by a MetadataLoadContext.
/// </summary>
internal abstract partial class RoEvent : LeveledEventInfo
{
public sealed override Type EventHandlerType => _lazyEventType ?? (_lazyEventType = ComputeEventHandlerType());
protected abstract Type ComputeEventHandlerType();
private volatile Type? _lazyEventType;
And it's unlike similar properties, like FieldInfo.FieldType
and MethodInfo.ReturnType
, which return Type
not Type?
.
Let's take a look at the source for EventInfo.EventHandlerType
:
public virtual Type? EventHandlerType
{
get
{
MethodInfo m = GetAddMethod(true)!;
ReadOnlySpan<ParameterInfo> p = m.GetParametersAsSpan();
Type del = typeof(Delegate);
for (int i = 0; i < p.Length; i++)
{
Type c = p[i].ParameterType;
if (c.IsSubclassOf(del))
return c;
}
return null;
}
}
The way it works is by getting the event's add
method, enumerating its parameters, and returning the first one that inherits from Delegate
. If it can't find one, it returns null.
Now, C# will never let you declare an event with an add
method that doesn't take a single delegate parameter:
public event EventHandler? MyEvent
{
add { /* value _must_ be of type EventHandler */ }
// remove goes here
}
As you see above, the add
method takes a single parameter of the same type as the event's delegate type, named value
. That's how it always is in C#.
But maybe other languages work differently. Maybe the runtime doesn't care. Either way, from the reflection API's standpoint, anything might go. And hence, it might return null.
If you know which events you're inspecting, and you know they were all declared in C#, then you can assume they're never null.