Replies: 25 comments
-
Making default interface methods only be overriden explicitly would solve the scenario. However might make it annoying for new implementers of the interface and new interfaces that provided convince implementations would then be less convenient to override. |
Beta Was this translation helpful? Give feedback.
-
I don't see where this is a problem. The implementation details of an interface is not a part of the contract. The "fragility" also exists today in that you can decide to break and refactor an interface by adding new members and not realize that existing implementations already implicitly implement those members by matching the signature but not in the manner expected. You'd probably be a lot more annoyed if C# worked like VB.NET and implementing members were required to be decorated as such: Public Class C : Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class |
Beta Was this translation helpful? Give feedback.
-
I certainly care. As @benaadams suggests, requiring an @HaloFour the more general problem is that the proposal amounts to 'infecting' (for lack of a better word) the lovely and safe world of interfaces (a contract, no behaviour) with the potential for misery that is inherent in inheriting dynamic behaviour. Overriding a default implementation becomes a risky business if you don't control the interface itself. You are quite right about the existing fragility, but at the moment people have to think hard about adding to APIs because they are always a breaking change (silent or otherwise). Default implementations (without explicit override for implicit methods) is a way that something which otherwise looks 'safe' can silently go wrong. Personally I would prefer that C# didn't allow implicit implementation (with a tidy mechanism to expose the methods when casted to the sub type), but I suspect this is a minority viewpoint. |
Beta Was this translation helpful? Give feedback.
-
No class Bar : IFoo
{
public int F1(int value) => value;
public int F2(int value) => value - 1;
int IFoo.F2(int value) => F2(value); // explicit override of interface default
} If it went that way... |
Beta Was this translation helpful? Give feedback.
-
@benaadams apologies, I was perhaps not clear. |
Beta Was this translation helpful? Give feedback.
-
Tell us how you really feel? This is an academic issue. Default implementations were released with Java 3.5 years ago and this issue has not cropped up, and that language has fewer tools when it comes to how a class may implement an interface.
I'm not aware of any LDM notes that touch on this. There was a question about requiring interfaces that extend other interfaces to provide a more specific implementation if the base interface had a default implementation but the team decided against that. It's up to the implementing class to resolve ambiguities arising from member collision. |
Beta Was this translation helpful? Give feedback.
-
Sorry, I don't understand the question
I will concede that I have little experience with ('modern') Java, but all I'm saying is that currently, when you implement an interface, you know everything about it, because it is only what it says it is (it is just a contract, no behaviour). Adding dynamically overridable behaviour to interfaces means they cease to be a simple and predictable component to something that, well, isn't. The proposal undoubtedly makes interfaces more powerful, but having something less powerful is often key to reliable system design. Arguably, the proposed changes turn interfaces into a more powerful abstract class, and leaves the language without interfaces: C# is loosing a really great feature. Just because the new feature hasn't gone as wrong as it could doesn't mean the constraints of the old feature won't be missed (I wouldn't care nearly as much multi-inheritance of abstract classes was proposed!).
I looked through them again... and I can't find them either, which is a bit surprising (perhaps why I hedged at suggested they weren't a given!). There is discussion about 'non-virtual members', wherein it is implied that implicit implementation will be carried out. (I am terrible at finding things online, if you can find something that contradicts me, I would be very glad to be made aware of it!) I'm aware that the implementing class has to resolve ambiguities, but if there is no ambiguity (as in the OP) then the behaviour can still change. My concern is that adding a new method to an interface with a default implementation will (as far as I can tell, given my understanding) never cause a compiler error. This creates the illusion that it is a 'safe' thing to do. By requiring There is so much that can go wrong when an interface changes, and adding a default method is just another way to change an interface. It is my opinion that (if implicit implementations are allowed) that they should require I must apologise for how long this is... |
Beta Was this translation helpful? Give feedback.
-
If I understand this correctly, this means that |
Beta Was this translation helpful? Give feedback.
-
@alrz My understanding was that That last part (cross assembly stuff) is another kettle of fish, and has been discussed: here are some LDM notes. I think I have yet to fully absorbed the issues therewith (but I'm sure that being more explicit can't make them worse!) |
Beta Was this translation helpful? Give feedback.
-
That doesn't change here. The interface is still a contract which enforces that the members are available to the consumer. How the interface is implemented has always been an implementation detail. The implementation could be satisfied by a The complaint is that I guess the complaint is that with default interface methods the problem might arise more often as people are going to be adding new members to their interfaces with wanton abandon. |
Beta Was this translation helpful? Give feedback.
-
I don't think so. an implicit interface impl does emit |
Beta Was this translation helpful? Give feedback.
-
Hah, so it does, if they are in the same project. It's amusing that the compiler will modify the signature of the parent type even if it doesn't directly implement the interface. I swear that I've tried this before in IL with a non- |
Beta Was this translation helpful? Give feedback.
-
Just checked, if base member is not virtual by any means (and not in the same assembly), it will emit a private member (explicit impl) with |
Beta Was this translation helpful? Give feedback.
-
That it does. That's probably the behavior I saw before. It's interesting the length that the compiler will go to make implicit implementation seamless. The CLR also supports it so some extent as well as long as the members are compatible, e.g. |
Beta Was this translation helpful? Give feedback.
-
Now, as I described, with such requirements in clr, runtime has no information to make The presence of |
Beta Was this translation helpful? Give feedback.
-
@HaloFour all true, and be in no doubt that I love interfaces as they are in C# (including their constraints, the dynamic behaviour, etc. these things are all useful). Hopefully this will finally clarify my position! I was referring more to the behaviour of an interface as seen from the author of an implementing class (as opposed to a consumer). The Fragility is that the default behaviour could (though it will surely be rare and a result of poor decisions) work at odds with the code in the implementing class (there is a crazy example in the Wikipedia article the OP linked to). It is certainly an edge case, but my point is that an absolute confidence has been lost, and the interface construct is now open to all manner of abuse. Of course, all of these horrible things are already possible with class inheritance; a miserable developer like me would perhaps rather than they weren't possible (if unlikely) with interfaces. Inheritance has its place, and interfaces have their (much more important) place, and they are not the same place. The prospect of the exposed behaviours of my object as seen by colleague's code when 'projected' through a third party interface being changed by this third party does not appeal! If the interface changes then I want my compiler to tell me about it, and (though I know this isn't your question) I think requiring |
Beta Was this translation helpful? Give feedback.
-
@alrz I didn't choose my words very well... I meant rather to suggest that life would be better if we require |
Beta Was this translation helpful? Give feedback.
-
Considering that the vector for this issue has existed in the language since C# 1.0, is a known liability with implicit interface implementation and has not demonstrated an actual burden to developers I don't think that there is any value in changing the language to require an explicit override. Having interface implementation be sometimes implicit and sometimes explicit will only be confusing and can only "solve" the problem in a limited set of circumstances. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour fair enough, I think we finally understand each other (I create confusion everywhere I go); I don't necessarily disagree with anything you've said, but I stand by my preference for the reasons given above, and I do think the OP presents a valid concern that should be considered (not that I should wish to imply that it hasn't already been taken into account). |
Beta Was this translation helpful? Give feedback.
-
When we're talking about an implicit implementation in the same source as the interface is declared then there is a chance that it might be actually expected to work that way i.e. implicitly, it will help you to have source-compat when you want to group some existing members in an interface and have them silently conform to that interface. I agree that the way default interface implementations interact with implicit implementations is unfortunate but if you amend either side you're nullifying the value they are providing in the first place, like requiring |
Beta Was this translation helpful? Give feedback.
-
Quite so, but there is also a chance that it isn't meant to work like that (or even, if the method 'just about' conforms, it could readily hide an edge case), and the compiler can't know, so the spec shouldn't assume intent that hasn't been stated. (As noted prior, this is a long standing problem with implicit implementation) I would argue that the 'value' from implicit interfaces comes from the fact that the method is exposed by the class itself, not just when it is projected as an interface. Requiring |
Beta Was this translation helpful? Give feedback.
-
Yes, it could be the case at times but so far it's been a "feature" to have implicit implementations and as HaloFour said it hasn't been much of an inconvenience. Although, you could have an analyzer to require |
Beta Was this translation helpful? Give feedback.
-
In Java's case it wasn't interface implementation that was the problem. All implementation in Java is implicit. Java added the |
Beta Was this translation helpful? Give feedback.
-
Yes, I think C# learned a lesson there and introduced |
Beta Was this translation helpful? Give feedback.
-
If an automatic implicit implementation of an interface default implementation is desired in a class, we could write:
This would allow us to access all the default implementation members through the class. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Let's say I have an interface and a couple of types that implements it:
Then, when the default interface methods feature is released, I modify
IFoo
:Now according to my understanding of the Liskov Substitution Principle (LSP), by modifying
IFoo
in this way, I've created a situation whereF2
has a certain expected behaviour, whichBaz
respects. ButBar
doesn't, through no fault of its own, thus breaking the principle.This strikes me as being similar to the fragile base class problem. In that latter case, changes to a base class can cause sub classes to malfunction, as base classes are inherently tightly coupled to their derivatives. In the case of interfaces, the fragile base class problem shouldn't occur as interfaces will have no state. Instead, a change to the interface causes a method in an implementation to become a substitution, which can then break the LSP. The term "fragile substitution problem" seems a good name for this.
Clearly,
F2
contains no useful semantic information and thus it's hard to argue that it has any promise of behaviour, so whatBar
andBaz
do here is undefined by the name. However, out there in the big wide world of millions of lines of code, I'm assuming this could be a real problem at some stage.Now I'm not in anyway arguing that we should abandon default interface methods (that conversion has been well and truly had), more highlighting a potential problem it could bring for the unwary.
Is this really a problem? Should we care? I'm not sure, I just thought I'd share my thoughts on this matter.
Beta Was this translation helpful? Give feedback.
All reactions