runtime: [API Proposal]: Please allow for the replacing of method bodies at runtime.
Background and motivation
It would be nice if the framework supported a way to dynamically replace a method body of an instance of an object,at runtime. I can think of several cases where a pattern like this could be leveraged, but the primary use case for me would support test automation.
As an example, say I’d like to test a class that has multiple dependencies:
public class SystemUnderTest
{
private Checker _checker; // Initialized by constructor, omitted for brevity
private Doer _doer;
public void DoTheRightThing()
{
if(_checker.Check())
Doer.DoA();
else
Doer.DoB();
}
}
The predominant seam for isolating the SUT from the dependencies would be to litter interfaces throughout the code (IChecker
and IDoer
). This is fine in the naive case, but in larger projects this causes a proliferation of single-use interfaces. If the framework supported replacing method bodies, one could simply create a new Checker
object and replace Check()
with return true
or return false
, and replace DoA()
with an assertion to pass or fail.
This concept is not novel and already exists in other frameworks (eg Ruby). Also, proving the demand for such an extension, there are commercial offerings which use proprietary techniques to accomplish a similar goal, but these are expensive, complex, and fragile. I believe these attach to the CLR Profiler to intercept method invocation, but I’m probably wrong…
API Proposal
I would like to propse the API should support a reflection option where a method body could be replaced at the instance level of an object with a delegate of matching signature:
Func<bool> returnsTrue = () => { return true; }
var method = typeof(Checker).GetMethod(nameof(Checker.Check));
//Proposed functionality
method.ReplaceBody(instance, returnsTrue);
API Usage
Extending the testing example above, the API could be used as follows.
[TestMethod]
public void Test()
{
var checker = new Checker();
typeof(Checker).GetMethod("Check").ReplaceBody(checker, () => return false);
var doer = new Doer();
typeof(Doer).GetMethod("DoA").ReplaceBody(doer, () => Assert.Pass());
typeof(Doer).GetMethod("DoB").ReplaceBody(doer, () => Assert.Fail());
var sut = new SystemUnderTest(checker, doer);
sut.DoTheRightThing();
}
Risks
While there are risks of application instability, I’d suggest those are owned by the developer. Although my perspective is limited, I believe this would offer no greater risk than a developer writing poor code using Reflection.Emit.
The larger risk, in my mind is the security risk associated with letting users replace random methods. There are a few options here for mitigation, but I would hope that this could be mitigated by an attribute allowing a user to opt-in or opt-out of having their methods replaced. Microsoft may choose to opt-out all System assemblies by default, opting-in certain assemblies or methods they deem valuable, and consumers could opt-in their assemblies or classes at their own discretion.
Thanks for your consideration!
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 18
- Comments: 30 (7 by maintainers)
Addressing some of the comments on the thread:
Regarding delegates -I understand their use; I just don’t think it applies here. My very specific API request was to support better isolation for testing, and that can already be done by declaring an interface for every class, no matter how small. However, the cost (development, maintainability, complexity) of maintaining all those extra declarations is non-trivial. The same problem would apply with delegates. – As a contrast, the API request is a way to perform a common task, minimizing the amount of overhead or friction to developers.
The profiling API was the ‘proprietary techniques’ I cited in the original post referencing the commercial products. Yes, the profiling API does allow method interception, albeit on a class level (not instance), but I agree with the other commenter that it is prohibitively complex / cumbersome to implement. That is a very large amount of risk, complexity, and overhead to consider for common use. I would consider method interception as an alternative to this proposal, if such a feature were more easily available in the runtime.
I would hope that allowing developers to opt-in to replacement could mitigate these concerns.
On the general topic of class vs instance replacement, if we’re opening up the source to add this feature, instance based method replacement would be ideal. If that is somehow prohibitive, class based method replacement would suffice.
And finally, though I’m not sure how this is relevant, I’m a big fan of Canada. =)
Replacing non-virtual method per instance is technically impossible. The method is often inlined before getting the object. Disable inlining or even add a check will downgrade performance a lot.
I like this API! And~ Can we add some way to close and open this feature, like the: add the env or app config or compile options?
So when will this feature be on the agenda? I don’t care if you replace the assembly or some other way.
Your are mixing OOP concepts of methods (per class) and fields/properties (per instance). If something really differs at the instance granularity, C# has delegates and you are free to assign different delegates as a field or property to your checker instances.
Maybe there has some real scence to use this feature. If you are the programer who write the client code, use the xLua you can do this (the code below copy from the internet):
And this feature always uses to hotfix some little bug or dynamic change the client behavior.
The DotNet has this feature, but it is very hard to use. So if we have this API, we can do this easily. So, what reason makes us negate this API?
While maybe this API getting the application’s risks, but I think this feature needs the switch to close. So why we don’t like It?
Ho forgot the little thing, most of the U3D’s development use the C# and xLua to realization this feature.
@acaly I don’t know if you are using .NET as the programming language, but Delegate as fields and properties is basic knowledge, as we all know, you respond to the features expected by others with basic knowledge, such as XCYB. Am I right? Now you should know that the function he expects is more different.
This is not your issue. If you don’t want to reply, then don’t reply.
Also please obey Code of Conduct.
你说了句废话! 懒得翻译英文回你.
If you are mainly worried about creating too many interface implementations, would inline anonymous types help? There are proposals in C# to allow user to implement interfaces with anomymous types inside method body like in Java.
It’s not possible to replace a non-virtual method body at runtime (some methods even get inlined after JIT and no actual code generated for them dedicately, you can reassign delegates because they’re actually function pointers), you can manipulate il as a build task after compiled as you want.
For example, using Fody: https://github.com/Fody/Fody
Dear fellow Canadians, you made a meaningless remark. I don’t think you understand his scene at all. This scene is very important.