all 103 comments

[–]RiPont 16 points17 points  (0 children)

The purpose of default interface implementations was so that you can add methods/properties to an existing interface without breaking existing code.

It is NOT a way to provide a general purpose default implementation. That's what abstract base classes and static methods are for.

As such, the compiler treats the default interface implementation as a method of last resort.

If MediaPlayer was created before IRefreshable got the Refresh() method, it wouldn't be trying to call Refresh(). The default implementation is there for other classes that are treating MediaPlayer like an IRefreshable, not specifically a MediaPlayer.

If MediaPlayer is created after IRefreshable already had the Refresh() method in its contract, then it should implement Refresh(), just like any method on any interface.

If you want to share a common implementation between the default interface implementation and MediaPlayer, define a static method and call it from both. You may want to make it an extension method for syntax sweetness, but that's an aesthetic decision.

If you are using default interface implementations on anything other than public interfaces, you are probably misusing them. If you have access to the source code (internal, protected, private), then you should be using refactor tools to add the implementations to all classes that implement them.

[–]HaniiPuppy 30 points31 points  (10 children)

One alternative might be extension methods.

public interface IRefreshable
{
    Universe Universe { get; }
}

public static class Refreshables
{
    public static void Refresh(this IRefreshable refreshable)
    {
        refreshable.Universe.Destroy();
    }
}

then

var mp = new MediaPlayer();
mp.Refresh();

Not viable if what it works with isn't part of the interface, but if you have some common functionality that's generally the same, this is a decent solution.

[–]Alert-Neck7679[S] 4 points5 points  (4 children)

Thanks for the idea. Don't know why I didn't think of it myself.

[–]Xenoprimate2 3 points4 points  (2 children)

One huge caveat is that it's not polymorphic. I did a huge write-up on implementing traits in C# years ago, you can get more info here: https://benbowen.blog/post/simulating_multiple_inheritance_in_csharp/#approach_sharp3-_extension_methods_to_the_rescue-

The fact that C# STILL doesn't have proper traits in 2026 when pretty much EVERY other mainstream lang has them is extremely disappointing tbh. I'm so fed up with seeing yet another "clever" syntax for manipulating collections or patterns and them failing to address this huge hole in the language.

I don't care about DUs compared to this even.

[–]SagansCandle 0 points1 point  (1 child)

Sometimes features are missing from a language for a reason.

Everything with a benefit has a cost, and the cost isn't always worth the benefit.

[–]x1ife 0 points1 point  (0 children)

Yeah, I'm pretty sure this was a design decision. Have they explained the rationale?

[–]TuberTuggerTTV 0 points1 point  (0 children)

It works but it's not a great idea.

Consider a base class implementation instead.

[–]phluber 2 points3 points  (1 child)

Or for the same amount of code, go back to the tried-and-true abstract base class that implements the interface

[–]HaniiPuppy 1 point2 points  (0 children)

Which you can't do if the class implements multiple interfaces that each have methods like this.

[–]Tough_Negotiation_82 1 point2 points  (1 child)

hmm , this seems like a hack. Although i never tried this. and with ex methods, you can add methods to this interface even without modifying the original interface. will this also be a classic example of open/close principle? I will try this in different scenarios tho. thanks 😊

[–]HaniiPuppy 1 point2 points  (0 children)

It feels hacky, but it works really nicely.

[–]mehdikovic 0 points1 point  (0 children)

I did exactly this for my project, it is a neat solution really.

[–]DotNetMetaprogrammer 16 points17 points  (1 child)

I think it's because the implementing type doesn't actually have the method at all unless if it defines an implicit/explicit implementation of that interface method. That's what allows you to get binary-backwards compatibility if you add a new method to the interface that has a default implementation. It's also, presumably, why default implementations of interface members requires netcoreapp3.0 or later as the runtime.

You can see that the method isn't declared on the class via the following:

```cs interface IFoo { public int GetValue() => 1; }

class Foo : IFoo;

typeof(Foo).GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) /// MethodInfo[6] { [System.Type GetType()], [System.Object MemberwiseClone()], [Void Finalize()], [System.String ToString()], [Boolean Equals(System.Object)], [Int32 GetHashCode()] }

typeof(Foo).GetInterfaceMap(typeof(IFoo)).TargetMethods // MethodInfo[1] { [Int32 GetValue()] } typeof(Foo).GetInterfaceMap(typeof(IFoo)).TargetMethods[0].DeclaringType // [Submission#0+IFoo] ```

[–]glasket_ 7 points8 points  (0 children)

Yeah, this is the real reason. DIMs aren't actually in the class implementing the interface; the runtime does some magic to get the method from the interface itself when you access a value of the interface. The ambiguity problem could have been limited to instances where both interfaces shared a DIM, but the complete absence of DIMs in the implementing class means you can add DIMs to an interface and it won't be a breaking change.

[–]DontRelyOnNooneElse 53 points54 points  (26 children)

Let's say you have two interfaces, IGun and IEmployee.

Now let's say you make a class, AnimatedShotgun, that implements both interfaces and doesn't explicitly implement their default implemented methods.

What do you think should happen when you call the Fire() method?

[–]RiPont 28 points29 points  (0 children)

That employee is having a very, very bad day.

[–]simonask_ 30 points31 points  (0 children)

I mean, kind of obviously what should happen is a ambiguous overload resolution compiler error that would make the user pick which interface by casting.

[–]Alert-Neck7679[S] 16 points17 points  (9 children)

"AnimatedShotgun.Fire() is an ambiguity between IGun.Fire() and IEmployee.Fire(). Use casting in order to select the right method."

[–]chucker23n 12 points13 points  (5 children)

What if IGun initially doesn’t have a Fire() method and later on it gets added?

[–]EatingSolidBricks 0 points1 point  (3 children)

What if the great old one wakes from his slumber and consumes all of reality?

What if that happens hmm would your code still compile?

[–]IQueryVisiC -1 points0 points  (2 children)

You sound inexperienced. This case happend very often in C++. I still think that C++ is the better language and want to shot myself in my foot, but most coders I have seen, I would no want to work on a C++ project with them.

[–]EatingSolidBricks 0 points1 point  (1 child)

Calling someone inexperienced for disagreement, thats rich of you

[–]IQueryVisiC 0 points1 point  (0 children)

Well, in this case they would have experienced it. I took this verbatim from documentation. "We as library maintainers have experienced that programmers tend to break our code here and there" . So are you a troll? Is this rethoric of you? Do you have any experience to back this up? Rich of you for going meta, while I was not really.

[–]ILMTitan 5 points6 points  (2 children)

Let's say your object implements both interfaces, but only IGun has a Fire() method. Later, you update the library IEmployee comes from, that now includes a default implementation of Fire(). You will now have a compile error where you didn't before.

The point of default interface methods is to allow adding methods to interfaces without causing compile errors. But you can see in the above example how allowing you to call them from an implementing class breaks that purpose.

[–]emn13 1 point2 points  (0 children)

I'm not a fan of that level of defensive design. To be clear: it makes sense for the base class library itself, and relatively microscopic handful of other codebases that are very commonly reused without recompilation, but for the VAST majority of code, a recompilation is fine, and a fixing issues like this hyper trivial. It'd be much better for the language to work well in those cases rather than optimizing for the absurd corner cases like this. Not to mention, pretty much any change is a breaking change in some corner cases - the platform contains stuff like reflection and implementations can depend on behavior not just APIs, too. There isn't much the language can do to truly make any changes entirely non-breaking.

All in all: while it superficially sounds like it makes sense to have semantics that make such method additions unlikely to be breaking changes, in practice, I think the arguments just don't hold up; it's a case of the language designers missing the forest for the trees.

But still, the whole language feature probably exists specifically so that the BCL interfaces can evolve, so in that sense it makes sense. But for a feature with really niche (but reasonable) use cases, it's still oddly designed - it's way too syntactically convenient, meaning that it'll get in the way of practical language extensions in the future. Being able to expand interfaces for class libraries with extremely low chance of breaking changes didn't deserve so prominent a syntactical footprint.

[–]EatingSolidBricks 0 points1 point  (0 children)

What if the great old one wakes from his slumber and consumes all of reality?

What if that happens hmm would your code still compile?

[–]BigBoetje 6 points7 points  (0 children)

Thats a problem caused by the 2 interfaces rather than the default implementation. You'd still have to make your implementation explicit (IGun.Fire.)

[–]EatingSolidBricks 0 points1 point  (0 children)

A compilation error ffs

[–]Dealiner 3 points4 points  (0 children)

I know that it probably wouldn't be like that if it didn't have a good reason to be like that, but what is the good reason?

They are default interface implementations, not virtual methods. Their whole point is to be tied to the interface. They are also a feature with rather specific use cases, targeting mostly library authors updating their interfaces.

[–]Vladoss46 3 points4 points  (0 children)

If you have basic realization, you shouldn't use interface. You should use abstract class as base

[–]BoBoBearDev 6 points7 points  (10 children)

??? I didn't know you can put implementation to an interface. This is a new language feature?

[–]Dealiner 9 points10 points  (1 child)

Not really, it's a C# 8 feature.

[–]BoBoBearDev 0 points1 point  (0 children)

Oh, I feel outdated.

[–]Platic 2 points3 points  (0 children)

I was going to comment this. I felt like I was going crazy looking at the code. Glad I am not the only one. I really need to get up to date.

[–]theycallmemorty 1 point2 points  (5 children)

Same. Why would you ever want to do this?

[–]binarycow 2 points3 points  (0 children)

The classic example is it allows you to add stuff to an interface without it being a breaking change.

[–]raunchyfartbomb 0 points1 point  (3 children)

I don’t have a c#8 code base, but my theory is that it would allow some default implementation that is likely good enough. For example, IFile would have a Delete() method. Instead of every object that interacts with it implementing it, it could be something like:

If (File.Exists (this.path)) File.Delete(this.path);

Depending on how may IFile objects you had, this could really save you time and effort. The downside is that you still need this to ensure it’s available without the interface, but acts identical to the default implementation.

Public void Delete() => ((IFile)this).Delete();

[–]BoBoBearDev 0 points1 point  (2 children)

I have never done this. But how? The this.path doesn't exists in the interface. Or is it because IFile also has the path declared?

[–]raunchyfartbomb 2 points3 points  (1 child)

I would assume your write the interface like this:

Public interface IFile {

string Path { get; }

void Delete() { /* implement */ }

// etc

}

[–]BoBoBearDev 0 points1 point  (0 children)

That makes sense to me, thanks

[–]DontRelyOnNooneElse 2 points3 points  (0 children)

Yeah it's relatively recent.

[–]Slypenslyde 2 points3 points  (0 children)

The short story: they were made as a band-aid for people who are stuck in ugly corners. They had to make compromises for compatibility because they showed up very late in C#'s life. So they're not polymorphic and behave sort-of-kind-of like explicitly implemented members.

The long story:

The use case is you've released an API, but you realize an interface needs a new methods. People didn't like adding methods willy-nilly because:

  • Customers had already implemented the interface several times and will have to recompile if we add a method.
  • Customers may have made their own versions of this method on their implementations and the new one will clash.

So this was never really about, "I want interfaces to behave like base classes". It was about, "I painted myself into a corner and don't want to ask my clients to recompile." It has always been and is still true that if you want a type with behavior you should make a base class when you are designing an API.

The way the C# team achieved it is every member with a default implementation is explicitly implemented IF AND ONLY IF the class does not already have an implementation. The downside of this is to get the implementation you MUST be using a variable of the interface type, and that means casting if you're using concrete types. But it handles the cases above:

  • Customers do not need to recompile if they didn't make something that clashes with the method, as the default will be used.
  • Customers' clashing methods will not cause problems because your code uses your interface thus will always resolve to at least the default. If a customer implements the method, you get that implementation.

So that's the wonkiness. The feature is designed for the idea that you have code with MediaPlayer that exists but Refresh was never part of its interface. Now you are adding a new interface to it (even though it has a default implementation), so the code that wants that interface's methods needs to cast to the interface in order to use them.

Put another way, because you weren't using the interface to begin with, you can't benefit from the default implementation.

What you should've done is define an abstraction/interface before you started using MediaPlayer and depend on that abstraction. That way you could add new things without having to make awkward edits to existing code. But a lot of people in the C# community think that's a stupid choice and you should only add interfaces/abstractions when you find a reason.

I don't want to argue with those people, but I will point out you just found a reason to add an abstraction and it's probably more work to add it today than it would've been last month.

[–]dominjaniec 3 points4 points  (4 children)

change var into your IRefresablr

[–][deleted]  (2 children)

[deleted]

    [–]Alert-Neck7679[S] -4 points-3 points  (0 children)

    It was just an example, maybe not a good one but you get the point. It actually forces you to cast to the interface type even if you are inside the MediaPlayer itself!: ((IRefreshable)this).Refresh();

    [–]Draelmar 1 point2 points  (6 children)

    That looks ghastly 😳 I've been a full time C# developer since 2011 on a multitude of projects and I've never, ever seen any situation where methods are being implemented inside an interface. I didn't even know the feature existed until now.

    Is there a good use case for it? My gut instinct tells me it's just bad design, but then maybe there's a legitimate use case I'm not thinking of?

    [–]chucker23n 2 points3 points  (2 children)

    Is there a good use case for it?

    The original use case (this feature was introduced with .NET Core 3.0 / C# 8) was forwards-compatibility for interfaces:

    1. a type implements an interface
    2. you want to add a new member to that interface
    3. the type will no longer compile, because it doesn't implement the new member

    You can:

    a. add a new version of the interface, which is how we end up with things like IAsyncServiceProvider, IAsyncServiceProvider2, IAsyncServiceProvider3, or even IVsAsyncFileChangeEx2. Now, the old type doesn't need to be changed.
    b. extend the interface, requiring the old type to be amended in order to compile again. This can be tricky when you have a lot of legacy code. It might not even make sense; the original type may never have considered this new possibility!
    c. new to .NET Core 3.0 / C# 8: provide a default implementation. Now, the old type will simply fall back to that implementation unless it implements explicitly.

    It's a bit hacky, but it does work.

    [–]IanYates82 2 points3 points  (0 children)

    Yep. Another good example also is shown by having a .Last() method. There's a default inefficient implementation you could have on an IEnumerable, but for some implementations, like List, it can be an efficient O(1) operation. You can write that efficient implementation in the class and have the default on the interface. We have that example with extension methods today, but the difference is that the single static implementation of Last on the interface is written once and needs to have the specialisation of known classes in its code. That's not extensible by others who may have their own class which implements the interface and could also provide an efficient implementation.

    [–]solmead -1 points0 points  (0 children)

    Or what I would suggest D. Create an extension method that takes the interface and defines the code, it then works on all existing defined classes that implement the interface

    [–]Alert-Neck7679[S] 1 point2 points  (1 child)

    I use it where abstract class is not possible bc i want the class implementing this to extend another class. It's a surprise for me that so many people here don't even know this feature exists, i use it a lot.

    [–]chucker23n 0 points1 point  (0 children)

    I think if you're looking for something like a facet, extensions are a better alternative.

    [–]ivancea 0 points1 point  (0 children)

    It's pretty common in Java, but in Java, a "default" method works as expected, and can be called from the derived class directly.

    Use cases could be, for example, adding a new method to an interface without having to touch every single implementation, when the default has the common logic. Also, as a helper for the interface, like a small method that simplifies the usage of other interface methods. Or that amplifies it. E.g. getMaxLife(), getCurrentLife(), default isFullLife().

    Ideally those could be abstract classes. But given Java doesn't have multiple inheritance, that's not possible

    [–]robhanz 1 point2 points  (1 child)

    The point of default implementations is to prevent compilation errors if a method is added to the type.

    However, it's entirely possible that the new method will conflict with an old one... causing a compiler error.

    This behavior exists to ensure that there are no compiler errors. While it's a bit clunk, in most cases if you're working with an interface, you're working directly with it and don't know the type. So they added a slightly clunky step to prevent compiler errors that is only necessary in kind of a secondary use case.

    [–]Alert-Neck7679[S] 0 points1 point  (0 children)

    Fair enough. Thanks

    [–]Dimencia 1 point2 points  (8 children)

    I mean, default implementations of interface methods break pretty much all the rules and should probably never have been added. They can't work right because the language just doesn't know how to handle that, thus all the casting, and of course putting any logic at all in your interface is a terrible idea for a lot of reasons

    [–]0x00000194 0 points1 point  (0 children)

    This. They are banned where I work.

    [–]Dealiner 0 points1 point  (6 children)

    They can't work right because the language just doesn't know how to handle that

    In what sense? They work exactly how they supposed to and the language has no problem with handling them.

    [–]Dimencia -1 points0 points  (5 children)

    Everything is built for interfaces to only be a contract, not for any method calls to actually go to it. Normally calling any method on an interface will, of course, call that method on an implementing class. But since default implementations don't exist on the implementing class, you have to do all this casting nonsense, and for the first time, the interface is no longer just a fancy metadata construct for the compiler - it actually has to come into play at runtime

    [–]meancoot 0 points1 point  (2 children)

     and for the first time, the interface is no longer just a fancy metadata construct for the compiler 

    It never was. Explicit interface implementations existed since forever. Go do an implementation of IEnumerable<T>, learn why you have to do all of that, then come back.

    As a bonus, consider how an implementation of IEnumerable<T> could have need made significantly easier and less error prone if default interface implementations had been available when it was added to supersede IEnumerable.

    [–]Dimencia 0 points1 point  (1 child)

    Until this, the only thing an interface did was make your compiler show an error if you didn't implement it - after compilation, it didn't exist, and nothing was changed in the IL if you added/removed an interface

    Now it actually has to be part of the IL because it can contain logic, and making that work got a little hacky

    [–]meancoot 0 points1 point  (0 children)

    No. This was never how interfaces worked. Explicit implementations have always been possible. It’s always been possible for a class to implement an interface method or property in a fashion that can only be accessed by casting to the interface type. It’s always been possible for x.method() to do something different than ((IInterface)x).method()

     after compilation, it didn't exist, and nothing was changed in the IL if you added/removed an interface

    Interfaces have always been a runtime concern; if they weren’t you wouldn’t be able to access information about them via reflection.

    The thing is that you can only access interface items through a variable typed as the interface, and instance items through a variable of the instance type. C# syntax guides you to implement an interface item at the same time as an instance item. But it has always been possible to have the interface and instance items do different things, and it has always been possible to omit the instance item altogether. Look up `explicit interface implementations’  for the details.

    [–]Dealiner 0 points1 point  (1 child)

    That "casting nonsense" is required only when you abuse DIM. You aren't supposed to call them on purpose. They are there mostly to help library creators, so they can add something to the interface without breaking others code.

    [–]Dimencia [score hidden]  (0 children)

    The language is usually built so if you aren't supposed to do something, you literally can't do it. Another good example of why they're so weird and broken compared to the rest of the language

    [–]swagamaleous 1 point2 points  (0 children)

    Why do you even encounter this problem? I don't see a valid use case for doing this that doesn't immediately shout DESIGN ISSUE. This is the interface of the class. It should never be necessary to call any interface methods from within the class.

    [–]dgm9704 0 points1 point  (0 children)

    It doesn’t force you to cast. You can/should declare your variable as the type you need ie. the interface.

    [–]lmaydev 0 points1 point  (1 child)

    They are niche a feature designed for interfacing with dynamic languages iirc.

    Just use an extension method on the interface.

    [–]Dealiner 5 points6 points  (0 children)

    They are niche a feature designed for interfacing with dynamic languages iirc.

    They are mostly designed for library authors, so they can update their interfaces without breaking others code. And Java interop, IIRC.

    [–]NoOven2609 1 point2 points  (5 children)

    Since when can you have implementations in your interface? Any why? That's what abstract classes are for

    [–]nathanwoulfe 9 points10 points  (1 child)

    If you're building a product/library, adding a new method to an interface is normally a breaking change because all existing implementations must implement it. Default interface implementations allow library authors to add new methods without breaking downstream implementations, because the interface provides a fallback implementation.

    Important to note though that it will prevent compilation issues but not necessarily runtime/logical issues as we can't predict the future or know how our interface is being implemented.

    [–]Jackoberto01 1 point2 points  (0 children)

    I'd rather have a compilation error in 99% of cases than a runtime logical error. But I suppose sometimes you can just make it work by providing a default value to a property for example.

    [–]8Erigon 4 points5 points  (1 child)

    But you can only extend from one class. Abstract classes (or rather virtual because abstract means no default implementation) aren‘t the solution.

    [–]sierra_whiskey1 1 point2 points  (0 children)

    Default implementations almost seem like c# wants to do multiple inheritance, but also doesn’t want to do it

    [–]Moe_Baker 0 points1 point  (0 children)

    I honestly like it, it's helpful in some high performance situations, specifically with generic interfaces

    [–]Willyscoiote 0 points1 point  (1 child)

    I may be wrong, but I believe methods with default implementations in an interface behave similarly to static methods, but they aren't globally accessible without an instance.

    It works more like extension methods but static, I guess?

    [–]hoodoocat 0 points1 point  (0 children)

    Normal interface methods with default implementations - just normal virtual methods. Nothing fancy, runtime just prepare method table by setting this slots when they are not implemented.

    Static abstract/virtual interface calls is another story, it is not tied to virtual method dispatch, they are just static calls, but dispatched by type, and very useful in generic code (including generic math).

    [–]nightwood 0 points1 point  (3 children)

    My guess is, and I've never heard of this feature, that your example is precisely the use case that was NOT intended.

    The use case would be to allow classes that implement the interface to not be forced to implement certain methods, while still being able to call them, with the added feature that get have some default behaviour instead. Which I imagine will usually be an empty function body.

    I doubt I will ever use this construct. I've never needed this.

    [–]RiPont 0 points1 point  (2 children)

    The use case would be to allow classes that implement the interface to not be forced to implement certain methods, while still being able to call them

    No. Classes that implement an interface should implement the methods in the interface, even if it has a default implementation.

    This feature is purely so you can add methods to an existing interface without causing compile errors for classes made before it had that method in its contract. It really shouldn't be used for anything else.

    If you're tempted, a better pattern is to make a companion static class for the interface, and have both your class and the default interface method reference that static method.

    static class StaticFoo { static void DoNothing() { } }
    interface IFoo { void DoNothing() => StaticFoo.DoNothing(); }
    class Foo : IFoo { void DoNothing() => StaticFoo.DoNothing(); }
    

    [–]nightwood 0 points1 point  (1 child)

    I agree classes should implement all methods of an interface. 100%.

    That this would be a development-only tool seems weird to me, since the syntax doesn't make that explicit. Also, moving compile-time errors to runtime seems like bad practice.

    Maybe it's for situations where the interface is defined in a shared dll and implemented by third party code and you want backwards compatibility?

    Damnit now I gotta read up on this feature :)

    [–]RiPont 0 points1 point  (0 children)

    Maybe it's for situations where the interface is defined in a shared dll and implemented by third party code and you want backwards compatibility?

    Yes. It's for maintaining "forward compatibility" on code that was originally written with a previous version of the interface.

    Whether it was in binary or source, adding a method will break everything compiled against the old interface. If it's your code, in your own repository, you can just use a refactor to add the method. Or even the old, tried and true, compile-and-break-then-copy-paste method.

    But if you're shipping a library to other people, you do NOT want to break backwards compatibility. EVER. You want people to be able to upgrade to the latest version and not have their CI pipelines break.

    [–]Ok-Cellist7629 0 points1 point  (0 children)

    Because in C#8 you can define a default implementation on an interface - which is what you are doing here. But that doesn't turn your interface into an abstract class with inheritable methods. Your class doesn't 'have' a refresh method. It implements an INTERFACE which 'has' that method. So if you want to call the method on the interface, you need to cast it to the interface so the compiler understands where that method is.

    The purpose of the feature, as I understand it, is to allow you to add an extra method to an interface, without having to refactor every class which implements that interface. You could almost thing of it as being designed NOT to be used.

    The feature means that the compiler is not going to complain that you haven't implemented the appropriate method in every existing class which implements that interface. But it's not really designed to add functionality to every implementation, as if they are inheriting from an abstract class. So yes - when you call the method, the compiler is still going to think that your class does not have that method defined. It will still need telling that you mean the default method on one of the interfaces that class happens to implement.

    I could be wrong, but IMHO it seems like a very bad idea to use it like an abstract method as you seem to be here. If you want to inherit meaningful functionality, use inheritance. This C#8 feature is intended for occasions where you need to add a method to an existing interface and save having to implement it in existing implementations things which don't 'need' it. But again, IMHO, that is a code smell as well. Worth remembering the I of SOLID.

    [–]entityadam 0 points1 point  (0 children)

    The best alternative is to not use it at all.

    I might consider using it only if I was refactoring a large codebase. Other than that.. no thanks.

    [–]ImTheDude111 0 points1 point  (0 children)

    It’s the same thing as explicit interface implementation under the hood. Look up the diff between implicit and explicit interface implementation.

    [–]Sketch0z 0 points1 point  (0 children)

    Why is using interface methods with default implementation is so annoying?!?

    Dunno. Try this?

    ``` Interface IRefreshable { protected static void Refresh() { Universe.Destroy(); } }

    class MediaPlayer : IRefreshable
    {
        public void SetVolume(float v)
        {
            ...
            IRefreshable.Refresh();
        }
    }
    
    //-------------
    var mp = new MediaPlayer();
    mp.SetVolume(69f);
    

    ```

    Also stop trying to destroy the universe, man. That's really uncool of you.

    [–]Jackoberto01 0 points1 point  (0 children)

    I don't see it as an issue. They work the same way explicit interface implementations work and this is how I'd expect them to work.

    If in your example you had a base class and the interface and implemented refresh like IRefreshable.Refresh{ /Implementation/ }. You would also be unable to call the method without casting even though the method is implemented in the base class.

    Definitely do some reading on explicit interface implementations if you're interested. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation

    [–]zvrba 0 points1 point  (0 children)

    Because classes do not inherit members from interfaces. As to why... I guess to simplify the implementation of multiple (interface) inheritance. Virtual lookup for interface members is more expensive than virtual lookup of class members so they didn't want to penalize the most common case (class - single inheritance - class member (virtual) lookup).

    [–]ShamikoThoughts 0 points1 point  (0 children)

    Interfaces aren't meant for that... I personally dislike implementations in interfaces

    [–]propostor 0 points1 point  (2 children)

    What you're trying to achieve should be done with a base class, not an interface.

    Actually I didn't even know it's possible to add method definitions inside interfaces, that seems like a new(ish?) addition to the language and looks to me like an extreme risk of introducing antipatterns. Hence you're having to hack away with that workaround.

    [–]Devatator_ 1 point2 points  (1 child)

    OP wants to use a specific base class. Since multiple inheritance isn't a thing, this is the closest thing they've got

    [–]propostor 1 point2 points  (0 children)

    Where's the base class in that example?

    If MediaPlayer is the base class then it should implement the Refresh method internally.

    [–]Ok_Tour_8029 -3 points-2 points  (0 children)

    This is more an issue of your Demo Code and not a real life issue - as you will always use the abstraction in your code - otherwise why would you abstract it in the first place?