Added: C# Language Design Notes for Jun 14, 2017 #693
Replies: 18 comments
-
This is fine to disambiguate between different interfaces carrying default implementations with the same signature, however I don't like the idea of skipping to a grandparent's base in a class hierarchy; it may lead to some valuable state to not get set. public class Foo
{
protected virtual void M()
{
//maybe something, maybe no-op
}
}
public class Bar : Foo
{
protected override void M()
{
base.M();
//does stuff
}
}
public class Baz : Bar
{
protected override void M()
{
base.M();
//does its own stuff
}
}
If you are set on this, then maybe, just maybe only allow it when the current assembly "owns" that much of the class hierarchy, because the dev may know what they're doing in that case, but please don't allow skipping base overrides cross-assembly. |
Beta Was this translation helpful? Give feedback.
-
As long as specifying the interface even in unambiguous contexts is also legal. That would significantly reduce the risk of throws at runtime when a conflicting implementation is introduced later and alleviate HaloFour's concern of the BCL team thinking the feature may be too risky to use effectively. |
Beta Was this translation helpful? Give feedback.
-
@Joe4evr The runtime already allows skipping base overrides (+it allows skipping the base call entirely). It also allows non-virtual calls to any implementation of the method in the class hierarchy from outside users. I have seen some cases where |
Beta Was this translation helpful? Give feedback.
-
I know that much (though just earlier today I talked someone into writing an analyzer to advise users to call base when the base method is annotated as such). Eric Lippert shows a specific case where a middle-class is updated with an override and the child class (in a different assembly) is not recompiled to take it into account. As the post notes, that behavior is specifically by-design. In every other case, C# does not allow one to skip to a grandparent's method when an override already exists on the parent, even if the CLR allows it. I simply motion to keep this rule intact, at the very least when crossing assembly boundaries, because the middle-class will most likely have very good reason to contain that override and the child override most likely has no good reason to skip over it intentionally. |
Beta Was this translation helpful? Give feedback.
-
@Joe4evr just a FYI, I've already written an analyzer to see if a developer is calling a base class method, you can find it here: https://github.com/JasonBock/CompilerAPIBook/tree/master/Chapter%202/MustInvoke. Note that it doesn't check to see if the method is reachable or not, but feel free to use what is there for your own purposes :). |
Beta Was this translation helpful? Give feedback.
-
My analyzer (the one @Joe4evr linked to) checks for controlflow and can be configured through attributes. 👍 |
Beta Was this translation helpful? Give feedback.
-
As for the declaration proposals, I prefer 1 over 2, but like status quo the most indeed. |
Beta Was this translation helpful? Give feedback.
-
re
It's not like we're going to default all the things (in that case we're probably using a class), but even so, it's good to signal that a method is " |
Beta Was this translation helpful? Give feedback.
-
I may have forgotten a few meeting notes, but so far, I've only seen syntax for default implementations as they apply to methods. What about properties? public interface IFoo
{
string FooString { get; set; } //required part of the contract, or just default implementation?
} A syntax modifier would almost certainly have to be used to disambiguate: public interface IFoo
{
//using 'default' because that is what was discussed
default string FooString { get; set; } //hey, now we know for sure!
} It does make me lean towards seeing the |
Beta Was this translation helpful? Give feedback.
-
It'd be impossible for an interface to have a default auto-implemented properties. That would require the interface to support fields, which they won't. |
Beta Was this translation helpful? Give feedback.
-
I realize this comment is coming a bit late but... After a while, my opinion is this default interface implementation is getting quite complex in terms of how many logical options we have related to code reuse. There is inheritance from an abstract class, there's interface with extension method, there's interface with default implementation, there's going to be interface with extension anything, there's going to be shapes, and also extension anything over shapes. Now we have 3 logical concepts that are quite related but not same: Abstract classes, Interfaces and Shapes with specific distinctions and things to remember where they behave like the other one. Sometimes we have interfaces that are 'contracts' but sometimes they have implementation so they are like abstract classes. Same with shapes. Also, interfaces can't have fields and that can be a problem in certain designs (the engine I work with serializes only fields). Why can't we have some form of multiple inheritance instead where a max of 1 concrete class is allowed and other abstract classes can be inherited 'virtually'? No complexities with access modifiers is needed. Why can't we have interfaces and shapes be the same thing? It seems like the current design will lead to endless refactorings. To add to that, we have further splitting of null and default bringing more style diversity into the picture. |
Beta Was this translation helpful? Give feedback.
-
Inheritance is already 3 things at once to just put that into the mix.
In a hypothetical case of a language do-over I might consider making each of these characteristics their own construct, instead of having an endless set of recombinations. Food for thought, perhaps? I hope that made sense. |
Beta Was this translation helpful? Give feedback.
-
Oh, I forgot a 4th thing:
|
Beta Was this translation helpful? Give feedback.
-
@jjvanzon I've always looked at it like 2 things combined into one: 1) Polymorphism: one thing can behave like another and 2) A mechanism for implementation (code) reuse. just because a class reuses some code does not mean that it 'is' substitute-able with something else. After looking at some talks from people who do other languages with other type of inheritance, I've come to the conclusion that we really don't have a good mechanism for doing (2) without getting cobbled into (1) or limitations (which include: only 1 base class, no fields in interfaces, 'clumsy' implementations like extensions over interfaces etc.) I too always thought there could have been some separation between these 2 concepts. However, it is there now and most people take it as it is. Back to the topic, what about traits and mixins? Some other languages have traits and it is not wildly out of possibility to get traits some day, which would bring us to: more stuff to do a similar thing. All of this comes from the desire to avoid multiple inheritance, which we already kind of have. Maybe there can be some better solution to multiple inheritance problem than adding implementation to interfaces? I understand the Language team has a specific problem in mind with this feature, but maybe it is worth considering the alternatives once again? Specific problems come and go but language design is here to stay. |
Beta Was this translation helpful? Give feedback.
-
Specific alternatives would have to address the very specific problems that the language team has outlined, specifically around the ability to evolve an existing interface definition without breaking existing assemblies or compiled code. So far none of the alternatives proposed so far even remotely address that. This proposal is not about bringing traits to C#, that just happens to be a bonus. |
Beta Was this translation helpful? Give feedback.
-
I think we can't call it "traits" as long you can't implement an interface for an external type. |
Beta Was this translation helpful? Give feedback.
-
Really, though, you shouldn't really use inheritance purely for code re-use. Composition over Inheritance and all that. In my mind, polymorphism [Is-a] should really be the only thing to consider using inheritance for (code re-use is merely a side-effect), otherwise it's better to compose your things together [Has-a] instead. |
Beta Was this translation helpful? Give feedback.
-
Surely. But default implementation interfaces feature is for code reuse purposes, not polymorphism purposes. And so are extensions over interfaces and other techniques. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
C# Language Design Notes for Jun 14, 2017
Several issues related to default implementations of interface members
Please discuss.
Beta Was this translation helpful? Give feedback.
All reactions