Hacker News new | ask | show | jobs
by jayd16 690 days ago
If you want to enforce things, use an interface. If you want to accept anything that fits use a delegate.

I'm not sure I understand your use case where you need to conflate the two. You want to enforce the contract but with arbitrary method names?

I suppose you could wire up something like this but it's a bit convoluted.

    interface IFoo {
     string F(String s);
    }
    
    class Bar {
     public string B(String s){
      return "";
     }
    }

    // internal class, perhaps in your test framework
    class BarContract : Bar, IFoo {
     public string F(string s) => B(s);
    }
2 comments

My aim is to use dependency injection to inject the minimal dependency and nothing more. Versus the grab bag every interface in a medium-complexity C# project eventually devolves into.

I've had this on my blogpost-to-write backlog for a year at this point but in every project I've worked on an interface eventually becomes a holding zone for related but disparate concepts. And so injecting the whole interface it becomes unclear what the dependency actually is.

E.g. you have some service that does data access for users, then someone adds some Salesforce stuff, or a notification call or whatever. Now any class consuming that service could be doing a bunch of different things.

The idea is basically single method interfaces without the overhead of writing the interface. Just being able to pass around free functions but with the superior DevX most C# tools offer.

I guess I want a more functional C# without having to learn F# which I've tried a few times and bounced off.

If anything, there is little reason to use a named delegate over the Func nowadays too. The contract in this case is implied by you explicitly calling a constructor or a factory method so a type confusion, that Go has, cannot happen.
The idea with the named delegate would be if you need some way to:

    delegate Task<string> GetUserEmail(int userId);
This provides more guidance than taking in a:

    Func<int, Task<string>> getUserEmail
If you can annotate implementations of the delegate the tooling support becomes even nicer. Not all Funcs with the same shape have the same semantics, in my ideal C#-like language.

Edit: I completely forgot the main reason which is if using a DI container it can inject the named delegate for you correctly in the constructor. Versus only being able to register a single func shape per container.