Skip to content
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

Proposal: Take the burden away from developers, allow the runtime to swap out classes #9050

Open
migueldeicaza opened this issue Oct 3, 2017 · 5 comments
Milestone

Comments

@migueldeicaza
Copy link
Contributor

While we love to make fun of our Java brothers for their naming conventions, out of lack of support, our community has resorted to rolling a spectrum of dependency injection frameworks, mocking frameworks and developers find themselves over and over writing boilerplate code or consuming large libraries that use DI and other unpleasant approaches that make it difficult to follow the code.

This is an issue based on a public twitter discussion a few weeks/months ago.

The idea is that the runtime should provide a mechanism by which developers could swap out individual implementations of classes, and possibly even individual methods, either via an external configuration option, or at runtime.

This would allow developers to hardcode things that today are left to assorted meta-runtimes built as libraries, sweat and despair.

For example:

$ dotnet run -remap:System.Console=MyLibrary.dll,Miguel.Console
$ dotnet run -remap:System.Net.HttpClient=MyBetterLibrary.dll,MyBetterHttp.HttpClient

This support could also be surfaced at runtime, so for any classes that have yet to be loaded, we could perform the swapping:

using System.Net;
class Demo {
    static void Main ()
    {
        Assembly a = Assembly.LoadFrom ("MyBetterHttpClient.dll");
        var newHttpClient = a.GetType ("VerifiedHttpClient");
        RuntimeServices.Swap (typeof (HttpClient), newHttpClient);
    }
}

@davidfowl

@jkotas
Copy link
Member

jkotas commented Oct 3, 2017

cc @noahfalk

@noahfalk
Copy link
Member

noahfalk commented Oct 3, 2017

@migueldeicaza - Could you include any links you might have back to tweets or specific customer requests? Its always good to be able to validate any planning we might do against some concrete examples of customer pain. Thanks!

@migueldeicaza
Copy link
Contributor Author

@noahfalk
Copy link
Member

noahfalk commented Oct 6, 2017

Thanks! FWIW, the static version of this seems pretty close to type forwarders which the runtime already supports. I haven't looked into any of the details of our type forwarder implementation, but I'd like to believe it isn't much harder to build a type forwarding table from a config file or from hosting APIs than it is to read it from an assembly.

The dynamic runtime version looks hairier to me. The suggestion that it is done before the type is loaded by the runtime certainly simplifies the requirements, but in practice I worry that condition would rule out a bunch of the situations customers would hope to use it in. The runtime does have technology that allows a type which is already loaded/instanced to be mutated (EnC, profiler Rejit), but it imposes some restrictions on the changes that can be made. I suspect many of things customers want to do with mocking for testing could fit inside those limitations, but for other uses of DI it might not be a great fit.

In terms of user confusion, or the sense that a mysterious big library is doing magic, it doesn't fall out for me that the runtime implementing this won't be just as confusing. Presumably if we want to change the appearance of it to users that might pull in language features as well. Otherwise any managed API the runtime could expose would probably look similar to something a fancy 3rd party library could expose. In that case we may be offloading the pain from the library implementer, but to users it would be about the same.

We'd need to consider security at some point but I ignored it for now. This always opens the possibility of

RuntimeServices.Swap (typeof (SecureCryptoRandomNumberGenerator), typeof(NotSoRandomNumberGenerator));

@kzu
Copy link
Contributor

kzu commented Oct 7, 2017

I can see the value in some limited scenarios (like fixing an issue in a library you don't own and probably can't modify, say).

I disagree with this statement though:

that use DI and other unpleasant approaches that make it difficult to follow the code.

It may be a matter of taste, but I've never found DI unpleasant. I wonder how this proposal will make it easier to follow the code. Specifically, given an existing DI-friendly class like:

public class Foo
{
  IBar bar;
  public Foo(IBar bar) => this.bar = bar;

  public void DoFoo() => bar.DoBar();
}

a developer can trivially navigate to the implementation of IBar.DoBar by just hitting Ctrl+F12 in VS2017+ (Go To Implementation).

ctrlf12

The proposal would allow that (unpleasant?) code to be replaced with:

public class Foo
{
  public void DoFoo() => Bar.DoBar(); //  or possibly Bar.Instance.DoBar();
}

And since the implementation of said Bar could be replaced by anyone at any point (before the type is loaded, say), how is it making it easier to follow the code? Now doing F12 (Go To Definition), instead of Ctrl+F12 (Go To Implementation), would only take you to a sort of default implementation. You'd need to know for sure somehow (more tooling?) that nobody is changing the implementation under your feet.

Granted, you saved one keystroke (F12 vs Ctrl+F12) but at the same time you have made it impossible to know what Foo does and what dependencies it has unless you inspect its entire source code for calls to other classes (and those classes in turn, ad-infinitum) that you cannot reason about directly by seeing a constructor. That's the underlying reason most developers enjoy decoupling code in a manner that is DI-friendly, in my experience. But that's an argument for loosely coupling components and basing their interactions on implicit contracts and not internal implementation details. It's an entirely different discussion that is (as far as I can see) totally unrelated to monkey patching capabilities in the runtime.

Note that VS2017+ even handles the (more rare I'd say) multiple implementations scenario quite nicely:

ctrlf12duplicates

Given that our current tooling (in VS2017+ at least) already handles the "follow the code" part just fine, I'd frame this as a solution for the monkey patching scenarios, rather than as a perceived problem with the current frameworks and libraries developers use for other purposes.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 26, 2020
@ericstj ericstj removed the untriaged New issue has not been triaged by the area owner label Jun 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Development

No branches or pull requests

7 participants