So far, when a derived interface wants to provide a default implementation for a member declared by a base interface, we have been thinking of it by analogy with overriding of virtual methods in classes, even using the `override` keyword:
Implementing on the other hand is different. You don't have to match accessibility, etc. You can implement multiple interface members with one class member. A base class can highjack an implementation from an interface:
Maybe we should try to stay true to the notion of *implementing* when it comes to default interface members. So "overriding" should instead be expressed as an explicit interface member implementation:
This seems to have more of an "interface camp" feel to it: it is more similar to what you can do with interface members today, only you can now do it in interfaces.
We like this approach! Let's investigate some of its consequences.
With the "explicit implementation" approach above, how do you *call* the implementation from another one? Well you have that problem today: you *cannot* directly call an explicit implementation today; not even from within the implementing class itself.
However, for default interface implementations that's a significant reduction in expressiveness. You'd have to do so via a non-virtual helper method. So the implementation has to anticipate needing to be reused and factor out to a helper method.
For instance, when you do an "implement interface" refactoring, what does it do? Leave out already implemented ones? implement them again with a call to `base` (if that's possible)? With a `throw`?
Won't it be common to want to reuse a default implementation, adding "just a little bit more", i.e., calling `base`? Well, that gets into an accessibility game: the accessibility for ultimate use, versus the accessibility for being non-virtually called as `base`.
If we want to do something like this, we would also consider allowing *classes* to pick which one they grab the implementation from: even though classes have no ambiguity about which is the the most *specific* override, you could potentially allow "reaching around" and grabbing an older one.
When we were on the `override` plan, we intended to allow "reabstraction", where an interface could `override` a base method to *not* have an implementation.
In this new scheme, should we allow "deimplementation" - the ability for an interface to say that an inherited member does *not* have a default implementation, contrary to what a base interface said?
The meaning would be "I declare that the default implementation is not useful (or is harmful) to classes implementing me."
It does mean you have to type the interface name every time you want to provide a new implementation for a base interface member. Should we have a shorthand for when the interface name is unambiguous and obvious? No. We haven't needed it for explicit implementation in classes, we probably don't need it now.
We currently poison with a `modreq` all the places in signatures where unaware compilers could do something unsafe (write into read-only memory) by using a `ref readonly` as if it was a mutable `ref`.
`Modreq`s aren't very discerning, so a virtual method cannot even be called by unaware compilers, even when only overriding is unsafe. We accept this degree of granularity as the best possible state of affairs.
The only role of the `modreq` is to poison unaware compilers. The actual information about read-only-ness of refs is carried in attributes. Should we reject methods that have the attributes but not the `modreq`? It makes it harder to relax it later. But it protects the contract from manual finagling. So yes, refuse to consume such methods.
It's hard for us to add a new modifier. We may have to reuse one. If we had our own, we could avoid using attributes altogether, just make it modopt or modreq depending on whether it is required for safety. Let's keep this idea around, in case we do get to have our own.
Extension methods will be allowed to take value types by `ref` or `ref readonly`. It doesn't make sense for reference types, so those are disallowed. However, what about unconstrained (or interface constrained) type parameters?
Should we allow so as to get the benefit when a type argument happens to be a value type? Probably not.
- For mutable ref it seems a little dangerous and surprising that an extension method can modify a variable of reference type
- For readonly ref, chances are it would lead to much unintended copying, as the readonly-ness would cause the value to get copied whenever you tried to do something useful with it in the body (e.g. based on an interface constraint).
`default` as an operand to unary or binary operators would sometimes work, and sometimes not, depending on whether there happens to be a best operator across all available predefined or user-defined ones for all types:
This feels arbitrary. But worse, it is actually a recipe for future compat disasters. Imagine we added a `-` operator to, say, arrays in the future. Now the second line above would break, because the `int` overload of the pre-defined `-` operator would no longer happen to be best.