diff --git a/src/NSubstitute/Core/Events/RaiseEventWrapper.cs b/src/NSubstitute/Core/Events/RaiseEventWrapper.cs index efe56e1c..bd3aa8da 100644 --- a/src/NSubstitute/Core/Events/RaiseEventWrapper.cs +++ b/src/NSubstitute/Core/Events/RaiseEventWrapper.cs @@ -12,8 +12,8 @@ protected EventArgs GetDefaultForEventArgType(Type type) { if (type == typeof(EventArgs)) return EventArgs.Empty; - var defaultConstructor = GetDefaultConstructor(type); - if (defaultConstructor == null) + var defaultConstructor = GetPublicDefaultConstructor(type) ?? GetInternalDefaultConstructor(type); + if (defaultConstructor is null) { var message = string.Format( "Cannot create {0} for this event as it has no default constructor. " + @@ -24,7 +24,22 @@ protected EventArgs GetDefaultForEventArgType(Type type) return (EventArgs)defaultConstructor.Invoke([]); } - private static ConstructorInfo? GetDefaultConstructor(Type type) => type.GetConstructor(Type.EmptyTypes); + private static ConstructorInfo? GetInternalDefaultConstructor(Type type) + { + var nonPublicDefaultConstructor = GetNonPublicDefaultConstructor(type); + var isInternalDefaultConstructor = nonPublicDefaultConstructor?.IsAssembly == true; + return isInternalDefaultConstructor ? nonPublicDefaultConstructor : null; + } + + private static ConstructorInfo? GetPublicDefaultConstructor(Type type) + => GetDefaultConstructor(type, BindingFlags.Public); + + private static ConstructorInfo? GetNonPublicDefaultConstructor(Type type) + => GetDefaultConstructor(type, BindingFlags.NonPublic); + + private static ConstructorInfo? GetDefaultConstructor(Type type, BindingFlags bindingFlags) + => type.GetConstructor( + BindingFlags.Instance | BindingFlags.ExactBinding | bindingFlags, null, Type.EmptyTypes, null); protected static void RaiseEvent(RaiseEventWrapper wrapper) { diff --git a/tests/NSubstitute.Acceptance.Specs/EventRaising.cs b/tests/NSubstitute.Acceptance.Specs/EventRaising.cs index bc2c0d8e..d4eee84a 100644 --- a/tests/NSubstitute.Acceptance.Specs/EventRaising.cs +++ b/tests/NSubstitute.Acceptance.Specs/EventRaising.cs @@ -298,6 +298,31 @@ public void Raise_custom_event_that_has_sender_and_args_but_does_not_inherit_fro Assert.That(eventRecorder.Sender, Is.SameAs(sender)); } + [Test] + public void MyEvent_with_CustomEventArgsWithInternalDefaultConstructor_is_raised() + { + // Arrange + var exampleInternalMock = Substitute.For(); + var consumerInternal = new ConsumerInternal(exampleInternalMock); + + // Act + exampleInternalMock.MyEvent += Raise.EventWith(this, null!); + + // Assert + Assert.That(consumerInternal.SomethingWasDone); + } + + [Test] + public void MyEvent_with_CustomEventArgsWithPrivateDefaultConstructor_throws_CannotCreateEventArgsException() + { + // Arrange + var examplePrivateMock = Substitute.For(); + + // Act and Assert + Assert.Throws(() => + examplePrivateMock.MyEvent += Raise.EventWith(this, null!)); + } + class RaisedEventRecorder { public object Sender; @@ -339,4 +364,38 @@ public class CustomEventArgs : EventArgs { } public class CustomEventArgsWithNoDefaultCtor(string arg) : EventArgs { } + + public class CustomEventArgsWithInternalDefaultConstructor : EventArgs + { + internal CustomEventArgsWithInternalDefaultConstructor() { } + } + public interface IExampleInternal + { + public event EventHandler MyEvent; + } + public class ConsumerInternal + { + public ConsumerInternal(IExampleInternal example) + { + example.MyEvent += OnMyEvent; + } + public bool SomethingWasDone { get; private set; } + private void OnMyEvent(object sender, CustomEventArgsWithInternalDefaultConstructor args) + { + DoSomething(); + } + private void DoSomething() + { + SomethingWasDone = true; + } + } + + public class CustomEventArgsWithPrivateDefaultConstructor : EventArgs + { + private CustomEventArgsWithPrivateDefaultConstructor() { } + } + public interface IExamplePrivate + { + public event EventHandler MyEvent; + } } \ No newline at end of file