-
-
Notifications
You must be signed in to change notification settings - Fork 794
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
Delegate currying refactorings #125
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
One lesser-known functionality in .NET delegates is the ability to curry the first parameter of a static function, as if it were an instance method. There are a few known cases where this is used: 1. C# takes advantage of that when it allows for delegates to be created to "instance-like" invocations of extension methods. 2. The DLR also can choose to generate backing code for lambdas this way when the lambda doesn't close over other values, because CLR execution of the Invoke(...) method is noticeably faster (probably tuning for instance member invocations such as event handlers). 3. Roslyn (next generation .NET Compiler Platform) is exploring utilizing this technique on applicable lambdas, pending further analysis on compatibility. The usage case would be on lambdas that do not require capturing context ("static lambdas") - where it would shave ~33% off of delegate invocation overhead.
First step in moving new delegate currying-aware code to be part of shared extensions.
Now that the delegate currying is in an extension method, might as well make the code similar to before to minimize the code diff.
In some versions of the Roslyn compiler, lambda backing methods can have an extra parameter to speed up CLR delegate invocations. We use the valueDel in a few ways, sometimes the sentinel value delegate (no-op) takes in no parameters. In this case, we need to update this to use the new helper method to see if the current value in valueDel accepts no parameters (rather than literally checking to see if its backing method has zero parameters)
Change a codeblock that tries to find the reflection parameter for the backing method of Action<T> by using the first parameter. In the case of curried delegates, there can be an extra parameter in front for the "logical instance". In that case, you actually can look at the last parameter instead.
Shipped! https://www.nuget.org/packages/Moq |
Awesome, thanks! ☺ From: Daniel Cazzulino [mailto:notifications@github.com] Shipped! https://www.nuget.org/packages/Moq — |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
For valid delegates, the MethodInfo returned by System.Delegate.Method does not necessarily match the signature of the delegate. For example, in .NET 3.5+, the C# compiler allows you to take extension methods such as:
And bottle them up like so (assuming Enumerable was imported via a using):
As you can see, even though the underlying Enumerable's public signature takes in a parameter, it's surprising that it can be captured by a delegate that does not accept parameters. Capturing "instance-like" invocations of extension methods is probably very uncommon for affected Moq scenarios (Callback(...), Returns(...), VerifySet(...)).
In addition, certain DLR code generation strategies also can cause a lambda to be emitted with an extra ignored "curried" parameter. As it turns out, invoking these signatures is noticeably faster (~33% time shavings off of invocation overhead). This is very likely because delegates already have to account for invoking instance methods - especially for the classic mainstream scenario of delegates invoking event handlers.
As it turns out, the next generation .NET Compiler Platform ("Roslyn") compilers are also exploring the use of delegate currying techniques.
These changes allow for Moq to be currying-aware in a backwards compatible way (with older compiler versions).
This fixes the failed Moq unit test cases when Moq/unit tests are compiled with Roslyn, as well as adds an extension method testcase to demonstrate the repro against pre-Roslyn C# compilers.