Skip to content

Commit da82bc5

Browse files
authored
Merge pull request #813 from mihnea-radulescu/bug/issue-788-Raise-EventWith-default-constructor
Raise.EventWith default constructor (#788)
2 parents 2ce4d66 + 4bd429e commit da82bc5

File tree

2 files changed

+77
-3
lines changed

2 files changed

+77
-3
lines changed

src/NSubstitute/Core/Events/RaiseEventWrapper.cs

+18-3
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ protected EventArgs GetDefaultForEventArgType(Type type)
1212
{
1313
if (type == typeof(EventArgs)) return EventArgs.Empty;
1414

15-
var defaultConstructor = GetDefaultConstructor(type);
16-
if (defaultConstructor == null)
15+
var defaultConstructor = GetPublicDefaultConstructor(type) ?? GetInternalDefaultConstructor(type);
16+
if (defaultConstructor is null)
1717
{
1818
var message = string.Format(
1919
"Cannot create {0} for this event as it has no default constructor. " +
@@ -24,7 +24,22 @@ protected EventArgs GetDefaultForEventArgType(Type type)
2424
return (EventArgs)defaultConstructor.Invoke([]);
2525
}
2626

27-
private static ConstructorInfo? GetDefaultConstructor(Type type) => type.GetConstructor(Type.EmptyTypes);
27+
private static ConstructorInfo? GetInternalDefaultConstructor(Type type)
28+
{
29+
var nonPublicDefaultConstructor = GetNonPublicDefaultConstructor(type);
30+
var isInternalDefaultConstructor = nonPublicDefaultConstructor?.IsAssembly == true;
31+
return isInternalDefaultConstructor ? nonPublicDefaultConstructor : null;
32+
}
33+
34+
private static ConstructorInfo? GetPublicDefaultConstructor(Type type)
35+
=> GetDefaultConstructor(type, BindingFlags.Public);
36+
37+
private static ConstructorInfo? GetNonPublicDefaultConstructor(Type type)
38+
=> GetDefaultConstructor(type, BindingFlags.NonPublic);
39+
40+
private static ConstructorInfo? GetDefaultConstructor(Type type, BindingFlags bindingFlags)
41+
=> type.GetConstructor(
42+
BindingFlags.Instance | BindingFlags.ExactBinding | bindingFlags, null, Type.EmptyTypes, null);
2843

2944
protected static void RaiseEvent(RaiseEventWrapper wrapper)
3045
{

tests/NSubstitute.Acceptance.Specs/EventRaising.cs

+59
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,31 @@ public void Raise_custom_event_that_has_sender_and_args_but_does_not_inherit_fro
298298
Assert.That(eventRecorder.Sender, Is.SameAs(sender));
299299
}
300300

301+
[Test]
302+
public void MyEvent_with_CustomEventArgsWithInternalDefaultConstructor_is_raised()
303+
{
304+
// Arrange
305+
var exampleInternalMock = Substitute.For<IExampleInternal>();
306+
var consumerInternal = new ConsumerInternal(exampleInternalMock);
307+
308+
// Act
309+
exampleInternalMock.MyEvent += Raise.EventWith<CustomEventArgsWithInternalDefaultConstructor>(this, null!);
310+
311+
// Assert
312+
Assert.That(consumerInternal.SomethingWasDone);
313+
}
314+
315+
[Test]
316+
public void MyEvent_with_CustomEventArgsWithPrivateDefaultConstructor_throws_CannotCreateEventArgsException()
317+
{
318+
// Arrange
319+
var examplePrivateMock = Substitute.For<IExamplePrivate>();
320+
321+
// Act and Assert
322+
Assert.Throws<CannotCreateEventArgsException>(() =>
323+
examplePrivateMock.MyEvent += Raise.EventWith<CustomEventArgsWithPrivateDefaultConstructor>(this, null!));
324+
}
325+
301326
class RaisedEventRecorder<T>
302327
{
303328
public object Sender;
@@ -339,4 +364,38 @@ public class CustomEventArgs : EventArgs { }
339364
public class CustomEventArgsWithNoDefaultCtor(string arg) : EventArgs
340365
{
341366
}
367+
368+
public class CustomEventArgsWithInternalDefaultConstructor : EventArgs
369+
{
370+
internal CustomEventArgsWithInternalDefaultConstructor() { }
371+
}
372+
public interface IExampleInternal
373+
{
374+
public event EventHandler<CustomEventArgsWithInternalDefaultConstructor> MyEvent;
375+
}
376+
public class ConsumerInternal
377+
{
378+
public ConsumerInternal(IExampleInternal example)
379+
{
380+
example.MyEvent += OnMyEvent;
381+
}
382+
public bool SomethingWasDone { get; private set; }
383+
private void OnMyEvent(object sender, CustomEventArgsWithInternalDefaultConstructor args)
384+
{
385+
DoSomething();
386+
}
387+
private void DoSomething()
388+
{
389+
SomethingWasDone = true;
390+
}
391+
}
392+
393+
public class CustomEventArgsWithPrivateDefaultConstructor : EventArgs
394+
{
395+
private CustomEventArgsWithPrivateDefaultConstructor() { }
396+
}
397+
public interface IExamplePrivate
398+
{
399+
public event EventHandler<CustomEventArgsWithPrivateDefaultConstructor> MyEvent;
400+
}
342401
}

0 commit comments

Comments
 (0)