-
Notifications
You must be signed in to change notification settings - Fork 1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature propsoal: Allow use of override keyword on interface method implementations. #3510
Comments
There are some options:
It would generate an error even without the In general, I'm not convinced that this would be actually valuable. If an interface changes in your own code, you should update all its implementations at the same time, so this wouldn't help much. An interface changing like this in an external library should be quite rare and when it does happen, it's likely to cause some kind of compiler error. And even when it doesn't, the only harm is some unnecessary code. How is that worth changing the language? |
I agree with this being a valuable premise. Unfortunately, I also think the ship has already sailed. You'll only detect the error in cases where the developer was aware of this benefit and used I know I'm beating a dead horse here, but I think this is a good use case for analyzers. Interfaces could be marked with attributes like That way the feature works with existing code without the breaking change of adding a new language feature that introduces a similar warning. (Of course, in an alternative universe where this proposal is accepted, you could just as easily implement an analyzer that complains about any interface implementations not marked as |
If you design the analyzer differently, I think you don't need an alternative universe: the analyzer could complain about any interface implementation not marked |
I don't think that's right? The problem here (at least in my experience, and I've wanted this for a long time) is that you write a method to implement an interface method, then later that interface method is removed, but your implementation lives on, unnecessarily. Therefore what you want is an error if a method is marked This is the same story as overriding base methods. The only reason that we have an Explicit interface implementations work here sometimes, but they have issues with inheritance (and FxCop will moan at you if you use one in a non-sealed class) and of course callers need cast you to the interface type first. An analyser and |
Also a good idea, although I'd suggest the name
You're right. I was only looking at it from the perspective of the interface author wanting errors to occur when things change rather than the interface implementer. |
Interface overrides are specified in https://github.com/dotnet/csharplang/blob/master/proposals/covariant-returns.md |
You should. And this is precisely the use-case for this feature. You delete a method from an interface; and you should also delete all implementations. But there is currently no way to find all the implementations of that deleted method. The designers of C# thought this use-case important enough for method overrides that they provided a keyword specifically for that purpose. But there is no equivalent tooling for interface methods.. In the case of deleted methods,there is no warning or error whatsoever. Explicit interface implementation is not a realistic option (as previously pointed out). The problem with relying on warnings about unused public methods is that kind of warning works when code is stable, and ready to commit, and you're cleaning up all the last warnings and infos. But for code-in-progress, this is the kind of information you'd like when the interface gets changed, rather than when you're tidying up. It seems to me it's an error of a different priority than whether a member variable was unsued (for example). |
The current status of the covariant returns proposal: "Offline discussion toward a decision to support overriding of class methods only in C# 9.0.". |
Why? Either the mtehod is public, in which case you expected people to call it, outside of the interface, in which case you need to keep it. Or it's an explicit-impl, in which case this will break. It feels weird that you're trying to have it both ways. "this should only be called by people calling the interface, but i'm not going to explicit-impl it". I'm struggling to find a case where i've ever wanted that, or where that would make sense. |
I think the reason Consider code like this: class Base
{
public virtual void SomeLongMethodName() {}
}
class Derived : Base
{
public override void SomeLongMethodNam() {}
} Thanks to Except that's not actually true anymore, because of default interface methods: an interface can contain a Because of that, I think I am now slightly in favor of this feature. |
Suppose The modifiers on An interface member is always It's very helpful to avoiding many traps by disconnecting a public member and the interface member it implicitly implements and only retaining the "forwarding relationship". To detect removed/misspelt interface members, just always use explicit implementation (and forward the call to a public member, if it was intended to be implicitly implemented). (*) What actually happens is different. Implicit implementation without |
Yes, the
This is also the convention in Java where the public class C : AutoCloseable {
@Override
public void close() { }
} This will cause a compiler error if you remove |
@HaloFour Edit: Thanks to HaloFour for pointing out that CLR does participate in resolving interface mapping.
This is quite debatable in the world of C#. Let me elaborate on the philosophy of "implicit = forwarding explicit". Suppose we have the following code: interface IFoo { void Foo(); }
class FooImpl : IFoo { public /* virtual? */ void Foo() { } }
class FooFwdExpl : IFoo { public /* virtual? */ void Foo() { } void IFoo.Foo() { Foo(); } } My claim is that as far as C# language is concerned, there is no difference (*) between the two:
(*) The emitted IL by today's C# compiler are different, but that's an implementation detail. Using this methodology, one can regard any implicit implementation as being rewritten as follows:
The rule is simple and clear. The rule that all modifiers goes to the If you agree with this methodology, it's clear that The separation of interface and class members will be important in certain cases: // Foo0.Foo = IFoo.Foo = 0
class Foo0 : IFoo { public void Foo() { Console.WriteLine("Foo0"); } }
// Foo1.Foo = 1, Foo0.Foo = IFoo.Foo = 0
class Foo1 : Foo0 { public new void Foo() { Console.WriteLine("Foo1"); } }
// Foo2.Foo = IFoo.Foo = 2, Foo0.Foo = 0
class Foo2 : Foo0, IFoo { public new void Foo() { Console.WriteLine("Foo2"); } }
// 1 0 0
(new Foo1()).Foo();
((Foo0)new Foo1()).Foo();
((IFoo)new Foo1()).Foo();
// 2 0 2
(new Foo2()).Foo();
((Foo0)new Foo2()).Foo();
((IFoo)new Foo2()).Foo(); Also, by orthogonality of features, if this proposal were to be accepted, It's quite a weak justification for a counterpart to be in C# just because some feature exists in Java. The worst feature C# borrowed from Java is array covariance. Also, interfaces in Java are very different from those in C#:
Interfaces are closer to abstract classes in Java than they are in C#. |
The magic is done by both. The language makes the member automatically
This is very much not true. C# and Java interfaces are nearly identical, with the exception that C# interfaces are a bit more flexible due to the explicit You are correct that the distinction between the two, such as with explicit implementation, is a language concern. At the IL level they are nearly identical, and it's totally possible to have "explicit" overrides of virtual members on the base class. The point of this proposal is to redefine those terms in C# so that |
I've just created a Roslyn analyzer to address this. |
Yes, I also miss that compiler detects deleted interface members. After having read this discussion, I have another proposal: if an interface member is marked with So you would mark the interface method as obsolete, clean up the implementations that the compiler complains about and finally remove the member from the interface. |
The justification is not that Java has it. The justification is that it is useful, increases productivity, and prevents defects in production code. And that the current implementation of the existing "override" modifier feature in C# misses an important use-case and is therefore cognitively dissonant, and in many cases counterproductive. The subtle distinction between virtual default method, pure virtual class method, interface method, and default interface method is too fine a distinction to be made over the keyboard while pounding out code. I just want a way to make sure that it will be an error if the method I am writing no longer overrides something without having to break flow by navigating through class hierarchy to find out whether it was originally declared as an interface method rather than a class method. (Worth point out in passing that the feature request is NOT the same as the Java features, since THAT feature can trivially be implemented with existing C# features). And because Java doesn't have an existing "override" modifier. |
If I can chip in, C++ also allows the But C# is not English. A strongly typed programming language like C# should be concerned about maintainability and refactoring use cases. Using |
With default interface methods, even alteration is on the table :) |
Also, we already use |
And another case with refactorings. Given
Now, just adding Anyway, I'd be most happy to just acknowledge that To support existing code, there should be two warnings: 1) Class method silently defines a non-DIM interface method [turning it on would make it possible to fix existing code gradually], 2) loud warning if a class overwrites a vtable/itable slot without |
As this is not a championed proposal, I'm converting this to a discussion, as per our README. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Proposal:
Allow the use of the override method modifier on methods that implement interface methods.
Rationale:
The override modifier cannot be used on methods that implement an inteface method, but do not implement a virtual or abstract method in a base class.
The problem with this is that there is no way to detect orphaned implementations of interface methods at compile time.
Orphaned overrides of virtual or abstract methods generate a compiler error; not so for orphaned methods that used to implement an interface method.
If the override method modifier is permitted on methods that implement interface methods, developers can mark these methods with the override keyword, and be given an error message when the method no longer implements an interface method.
Java tooling, interestingly, optionally allows use of the @OverRide attribute on interface methods. By default it is not allowed. But when enabled, will generate a warning or error when a method does not override an existing base class method or interface method.
Since all methods are virtual in Java, but not in C#, there should be a mechanism to declare virtual and abstract implementations of interface methods.
Example:
If the Start method in IStartable were later renamed to OnStart(), the declaration of StartableClass.Start wold generate an error since it no longer implements an interface method.
Startable class would then be rewritten as:
Details:
When a method overrides an existing method in a base class, regardless of whether it also implements an interface method, existing semantics apply as usual:
When a method does not override an existing method, the following semantics apply:
override - An error if the method does not implement an interface member
Otherwise, a non-virtual implementation of the interface method.
override virtual - An error if the method does not implement an interface member.
Otherwise a virtual implementation of the interface method.
override abstract - An error if the method does not implement an interface member;
otherwise, an abstract implementation of an interface method.
Use of the override modifier on interface method implementation is optional. If the override keyword is not present, current semantics apply.
Whether the method implements an interface or not is determined within the scope of the declaring class: the declaring class, or base classes of the declaring class must indicate that they implement the interface in question. The method may become an interface method implementation in a derived class by mixin; but must be declared without the override modifier in that case.
Impact:
The proposal is currently syntactically legal, but not semantically legal. It has no impact on emitted code, since it generates an error during semantic analysis of a class declaration. It is not backward compatible, because it allows code that would be semantically invalid in previous versions of C#.
The text was updated successfully, but these errors were encountered: