So if I declare a couple of methods named "Foo" and "Bar", and someone somewhere declares an interface FooBar consisting of those two method names - and bad luck, same signature - then I have to make sure I follow the semantics of that interface I might not even know about, because even if I don't declare it so, anyone could use my class and push it through the FooBar-shaped hole. That's absurd.<p>Exercise: imagine what the semantics of the following signature are: `int Read(string)`. Did everyone get the same answer? And yet, with implicit interfaces, you absolutely need everyone to settle on the same answer. Otherwise, person A could write a class with such a method with answer A in mind, person B could write a library declaring an interface with such a method with answer B in mind, and person C could use the class from person's A code and the interface from person B's code without realizing.
I always wondered how that would work in a medium or large codebase.<p>Is it easy to refactor? Refactoring can change the interface of classes, which is easier if they are explicit.<p>And my main fear: how do I know I use the right object? I would tempted to add methods all the time to satisfy the target interface instead of using another object that already satisfies that interface.<p>Not knowing explicitly whether I can use an object or not is confusing, or am I missing something obvious?<p>Edit: Last but not least, how do I know my classes implement an interface that could require 5 or 10 methods? I had to do that in Go, and counting and searching the methods was really a waste of time, so much that I had to add comments to explain the interfaces that were implemented in every class.
F# has an interesting way to achieve a similar function in Statically Resolved Type Parameters (SRTP)<p>Palatable blog post: <a href="https://www.compositional-it.com/news-blog/static-duck-typing-in-f/" rel="nofollow">https://www.compositional-it.com/news-blog/static-duck-typin...</a><p>Official docs: <a href="https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/statically-resolved-type-parameters" rel="nofollow">https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...</a>
In general, problems that some languages would solve with an interface containing 1-2 methods can be solved simply via delegates in C#. Then you can bind to any method with a compatible signature, or to an anonymous closure, or to a local function, without any fuss. Compared to the bad old days of having to write a whole anonymous class in java, it's pretty straightforward. Thankfully things like ValueTuple are built-in now alongside ref/out parameters, so producing multiple return values is no problem.<p>The downside is usually a method with a compatible signature doesn't have compatible behavior. This is part of why explicit interfaces are still valuable.
I'm a bit surprised by the comments here. Isn't this just duck typing, which has been a thing in dynamic languages for a very long time?<p>The supposed risks of breaking the contract by changing the class ring hollow to me. The implicit interface you have created is already public. So how is that any different than changing any other public API? There is no suggestion of an implicit interface over private methods.<p>I've worked in c# and typescript. I don't think this feature is particularly needed in c#, but I also don't see the issues presented by other comments as real problems.
Extension methods complicate implicit interfaces considerably.<p>This is likely one of the reason they don’t exist in C#.<p>It’s also one of the reasons GO doesn’t have extension methods.<p><a href="https://github.com/golang/go/issues/37742#issuecomment-596167605">https://github.com/golang/go/issues/37742#issuecomment-59616...</a><p>You either have to exclude extension methods from implicit interface definitions (which can feel very unnatural to consumers) or you get weird behavior with dynamic casts that is very confusing and breaks everything.<p>For that reason, its unlikely you would see this in C#.<p>VB tried to do this (implement both extension methods and dynamic interfaces), and ended up cutting dynamic interfaces because the two features don’t play well together.<p>They are an awesome feature in GO, but it’s hard to add them to C# without a whole lot of very messy design compromises that make it kind of wonky.<p>If you eliminate extension methods it’s much easier to add dynamic interfaces.
I've been experimenting with this, it makes testing trivial and removes the coupling that inevitably occurs with multi method interfaces.<p>However I think there's one missing enhancement that would turn it from esoteric and difficult to reason about to actually usable that the language will never get.<p>This is being able to indicate a method implements a delegate so that compilation errors and finding references work much more easily.<p>E.g. suppose you have:<p><pre><code> delegate Task<string> GetEntityName(int id)
public async Task<string> MyEntityNameImpl(int id)
</code></pre>
I'd love to be able to mark the method:<p><pre><code> public async Task<string> MyEntityNameImpl(int id) : GetEntityName
</code></pre>
This could just be removed on compile but it would make the tooling experience much better in my view when you control the delegate implementations and definitions.
In other words: an interface with one member is equivalent to a function reference.<p>There is an equivalence between public interface IThingDoer { int DoTheThing(int value); } and Func<int, int> doTheThing.<p>Converting from one to the other is left as an exercise to the reader.
> I though it was clever, and I was a bit enamored when I first heard it (having come from a C# background). Now, I think implicit interfaces are simply, obviously right.<p>What, no. No, implicit interfaces are not right. They are a footgun. That you provide some interface needs to be declared. I dislike unnecessary ceremony as much as anyone, but this is necessary ceremony.
Eh I don't see the need for it. Explicit is the way to do. The biggest mistake they've made in recent memory was adding default implementations of interfaces. Makes no sense.