-
Notifications
You must be signed in to change notification settings - Fork 471
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
DynamicProxy does not support reference semantics with value types (C# 7.2) on .NET Core #339
Comments
For what it's worth, here's the generated IL for each interface: .class interface public abstract auto ansi
SomeProject.Tests.IStructByRefConsumer
{
.method public hidebysig virtual newslot abstract instance void
Consume(
valuetype SomeProject.Tests.Struct& modreq ([System.Runtime.InteropServices]System.Runtime.InteropServices.InAttribute) message
) cil managed
{
.param [1]
.custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor()
= (01 00 00 00 )
// Can't find a body
} // end of method IStructByRefConsumer::Consume
} // end of class SomeProject.Tests.IStructByRefConsumer .class interface public abstract auto ansi
SomeProject.Tests.IStructByValueConsumer
{
.method public hidebysig virtual newslot abstract instance void
Consume(
valuetype SomeProject.Tests.Struct message
) cil managed
{
// Can't find a body
} // end of method IStructByValueConsumer::Consume
} // end of class SomeProject.Tests.IStructByValueConsumer |
And also, the source code for the last line of that stacktrace. |
@TAGC: I cannot reproduce this problem. All of your tests pass just fine for me with the following NuGet package versions:
Some notes:
|
Try .NET Core instead of .NET Framework. I've got a suspicion it might be a problem with corefx (or coreclr). |
Yes, the tests start failing when run under .NET Core. If I am putting facts together correctly, then the problem is caused by DynamicProxy not emitting custom modifiers in its .NET Standard 1.3 target (see the Core/buildscripts/common.props Lines 43 to 44 in c6f75ff
Core/buildscripts/common.props Lines 85 to 87 in c6f75ff
Core/src/Castle.Core/DynamicProxy/Generators/Emitters/MethodEmitter.cs Lines 179 to 202 in c6f75ff
IIRC this is because support for emitting custom modifiers is not included in that version of .NET Standard. The new C# One solution might be to have DynamicProxy support a new framework target, e.g. .NET Standard 2.0, which quite possibly supports emitting custom modifiers (but I haven't checked at this point). |
I've found that Castle.Core fails to create a proxy for interfaces like this: public interface IGenericStructByRefConsumer<T>
{
T Consume(in Struct message);
} These tests fail under both .NET Framework 4.6.1 and .NET Core 2.0: [Fact]
internal void Struct_ByRef_Generic_Moq_Test()
{
_ = Mock.Of<IGenericStructByRefConsumer<int>>();
}
[Fact]
internal void Struct_ByRef_Generic_NSubstitute_Test()
{
_ = Substitute.For<IGenericStructByRefConsumer<int>>();
} In the case of .NET Core, I get a similar sort of error to the other tests:
Under .NET Framework, I get a stacktrace like this:
|
@TAGC: .NET 4.6.1 probably prefers the .NET Standard 1.3 build to the .NET Framework builds (a problem that has cropped up since .NET 4.6.1 was retroactively changed to support .NET standard 2.0), explaining why it is affected by this as well. |
How hard would it be to fix this? I'd need to manually mock all my interfaces that use |
@TAGC I've renamed the issue title, no mocking happens here that is a mocking library concern. Thanks for the issue report, I'd usually start by asking for a test case against this library not another layer up your stack, but in this case it'll be easy to make that step. Thanks @stakx, yes it definitely sounds like this is caused by custom modifiers not emitted when using the .NET Standard 1.3 build, I disliked writing that code during the DNX port. It looks like I guess its time to also add a .NET Standard 2.0 build. However I don't think that will fix Moq (.NET Standard 1.3), NSubstitute (.NET Standard 1.3) or FakeItEasy (.NET Standard 1.6) without each project also releasing a .NET Standard 2.0 build which they do not do today, as NuGet will continue to use the .NET Standard 1.3 build. I don't think we really have any other options. |
@jonorossi: Thanks for the hint regarding Moq. I'll take care of upgrading Moq once an updated Castle.Core package is available. (I think I'll skip over .NET Standard 1.5 and 1.6 as framework targets and go straight to .NET Standard 2.0.) |
Why 2.0? If the necessary APIs are supported in 1.5, why not target the lowest possible version to support the largest number of consumers? Is there a compelling reason to target 2.0?
I don't think that's the reason; I just tried it, making sure to reference the net45 version of Castle.Core, and the problem with the generic interface still occurs. I think this is a related but different bug. |
The same question from the NSubstitute side. If you can fix the issue in .NET Standard 1.5, it worth it to do that. The Probably that isn't so critical for the testing libraries like Moq or NSubstitute, however there might be other dependencies which offer the production functionality and it might hurt them. |
Thanks @thomaslevesque and @zvirja for the feedback, exactly why I CCed. I've just been seeing a lot of other libraries skip over or drop .NET Standard 1.x, I thought it was just the done thing now with .NET Standard 2.0 out (but obviously not). Also on the 2.0 side gives us full access to the .NET Standard 2.0 APIs, but I guess we aren't ready to let go of .NET Standard 1.x yet so there isn't much point. No problem, .NET Standard 1.5 it will be and we'll drop .NET Standard 1.3 in the next major version (that change only drops .NET Framework 4.6 in favour of 4.6.1).
Thanks for the heads up, there might be a defect relating just to generics. We need to get a full set of unit tests added for these cases. If someone wants to contribute them please do, just let everyone know, I won't have time to look at it tonight. |
👍 |
@thomaslevesque Both the Moq and NSubstitute guys have commented above that they're prepared to upgrade to .NET Standard 1.5+ when Castle.Core vNext gets released, so I guess it's not necessary to keep 1.3 around for their sake (although it might be for other, non-mocking related libraries). |
Hi, any update on how this is progressing? |
@TAGC I'm not aware of anyone working on a pull request to add a .NET Standard 1.5 build defining |
I had some free time today, so I took a stab at this and submitted a PR for part of this issue. I'm however not quite sure how to resolve the issues that popped up with the Travis CI build.
There indeed seems to be a second defect here.
Update: This is likely a problem in the framework, not in DynamicProxy. See the dotnet/corefx issue linked to below. |
Many thanks @stakx, great work! I've responded on your pull request to get things going on .NET Core, I guess we'll wait to hopefully get a response from someone more versed in the CLR implementation as the work week starts. |
Thanks @stakx, I appreciate it! |
I've merged #344 which gets things going, and is enough to ship. I think it would be a great idea to add a failing unit test (marked ignored referencing the corefx issue) for the defect reported in https://github.com/dotnet/corefx/issues/29254 before we close out this defect. |
I feel that I started to be a bit confused, as previously you said that issue is at .NET Runtime side 😕 Given that you merged the PR and tests passed, does that mean that no issue is present there? If issue is still present at .NET Runtime side, could you clarify which scenarios work and which ones are affected? Thanks. Excuse me if I'm missing something obvious. |
@zvirja this issue isn't yet closed which is why my last comment is requesting we add ignored failing unit tests for the .NET runtime defect so it is clear what we expect not to work. You'll see @TAGC's #339 (comment) and https://github.com/dotnet/corefx/issues/29254 both have interfaces with a generic argument, the remaining defect is with generic types, however there might be other scenarios which we haven't yet discovered. |
@jonorossi, I'll submit a PR with the skipped failing unit test. |
I'm going to close this issue as fixed. The corefx issue is assigned to .NET Core 2.2.0, I can't see any way we could work around this defect, so subscribe to that issue to track the fix there. If the Microsoft team post a workaround we can look at doing something our side. |
@jonorossi Thanks for the explanation. Do you have also any ETA the already merged fix is released in a new version of the |
(Cross-posting some details from nsubstitute/NSubstitute#378)
C# 7.2 introduced the possibility of using reference semantics for value types - this is done by applying the
in
modifier for value-type parameters in method signatures.Castle.Core does not seem to support generating mocks for interfaces that use
in
modifiers for value types. This has been reproduced using Castle.Core indirectly through the latest stable versions of Moq and NSubstitute:Posted below is the part of the stracktrace that's common between the failing Moq and NSubstitute tests:
The text was updated successfully, but these errors were encountered: