-
Notifications
You must be signed in to change notification settings - Fork 276
Fixed bug that would cause a struct to not match and throw a runtime error. #895
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fixed bug that would cause a struct to not match and throw a runtime error. #895
Conversation
…time error. When a struct had an empty constructor that modified a field/property to a non-default value. The matcher would fail and throw a runtime error. This bug fix attempts to fix that issue by essentially zero initializing the struct the same way that default(T) does through the use of a runtime helper method. Simply, we replace Activator.CreateInstance with GetUninitializedObject. The idea came from this post https://stackoverflow.com/questions/1005336/c-sharp-using-reflection-to-create-a-struct Which contains and deprecated method as of .NET8. Microsoft has a recommended fix for that here. https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0050
Thanks for the pull request! Could you please add some unit tests? |
- Added test method `Any_on_struct_with_default_constructor_should_work` to ensure method calls with struct arguments do not throw exceptions. - Introduced `StructWithDefaultConstructor` struct: - Contains a property `Value`. - Has a default constructor initializing `Value` to 42. - Created `IWithStructWithDefaultConstructor` interface: - Declares `MethodWithStruct` that accepts `StructWithDefaultConstructor` as an argument.
I added a simple test that does fail on the main branch. Not sure there is a whole lot to test here. I did ensure that all tests pass, especially ones that take in nullable primitive types like |
Is there anything still blocking this from being merged? |
I fixed the format issue. |
Only a review I guess 😅 |
@@ -46,6 +46,15 @@ private object DefaultInstanceOfValueType(Type returnType) | |||
return BoxedDouble; | |||
} | |||
|
|||
return Activator.CreateInstance(returnType)!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm doubting if we should use Activator.CreateInstance(returnType)!;
for non-stucts, to keep things 100% the same for the previous cases.
Although all tests works, so not sure.
@nsubstitute/core-team any opinion on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a runtime error that occurred when using NSubstitute's Arg.Any<T>()
matcher with structs that have non-default constructors. The issue arose because Activator.CreateInstance
would invoke the constructor, causing the matcher to fail when comparing against the modified struct values.
- Replaces
Activator.CreateInstance
withGetUninitializedObject
to create zero-initialized structs - Adds special handling for
Nullable<T>
types to return null - Implements conditional compilation for .NET 5+ vs older frameworks
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
File | Description |
---|---|
src/NSubstitute/Core/DefaultForType.cs | Updates struct instantiation logic to use GetUninitializedObject instead of Activator.CreateInstance |
tests/NSubstitute.Acceptance.Specs/ArgumentMatching.cs | Adds regression test for struct with default constructor scenario |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
When a struct had an empty constructor that modified a field/property to a non-default value. The matcher would fail and throw a runtime error. This bug fix attempts to fix that issue by essentially zero initializing the struct the same way that
default(T)
does through the use of a runtime helper method. Simply, we replaceActivator.CreateInstance
withGetUninitializedObject
.The idea came from this post
Which contains and deprecated method as of .NET8. Microsoft has a recommended fix for that here.
This resolves the issue in #766