Skip to content
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

Proposal: Explicit overriding of virtual methods #1618

Closed
YairHalberstadt opened this issue Jun 10, 2018 · 5 comments
Closed

Proposal: Explicit overriding of virtual methods #1618

YairHalberstadt opened this issue Jun 10, 2018 · 5 comments

Comments

@YairHalberstadt
Copy link
Contributor

YairHalberstadt commented Jun 10, 2018

Currently there's a proposal relating to covariant return types: #49

However it doesn't seem to be going anywhere very fast, so I would like to propose a different workaround.

At the moment you can create something that behaves like a covariant return types with interfaces as follows:

     public interface IAnimal
     {
         public IAnimal GiveBirth();
     }

     public class Cat : IAnimal
    {
         IAnimal IAnimal.GiveBirth() => GiveBirth();

         public Cat GiveBirth() => new Cat();
    }

This doesn't work however when inheriting classes, since you cannot explicitly override the base. I propose allowing similar syntax when overriding a virtual method;

    public abstract class Animal
    {
        public abstract Animal GiveBirth();
    }

    public class Cat : Animal
    {
        Animal Animal.GiveBirth() => GiveBirth();

        public Cat GiveBirth() => new Cat();
    }

Then you could use it as follows

var cat = new Cat();
cat = cat.GiveBirth(); // calls Cat.GiveBirth
var animal = ((Animal)cat).GiveBirth(); // calls cats virtual override of Animal.GiveBirth

This will feel like covariant return types to anyone who consumes Cat.

There may be other uses, when someone wants to inherit from a class but hide their methods (similar to explicit interface implementation), although I cannot think of one right now.

@yaakov-h
Copy link
Member

Reformatted for readability.

Before (C# 7.x):

public interface IAnimal
{
    public IAnimal GiveBirth();
}

public class Cat : IAnimal
{
     IAnimal IAnimal.GiveBirth() => 
 GiveBirth();

     public Cat GiveBirth() => new Cat();
 }

Future (C# X.0):

public abstract class Animal
{
    public abstract Animal GiveBirth();
}

public class Cat : Animal
{
    Animal base.GiveBirth() => GiveBirth();

    public Cat GiveBirth() => new Cat();
}

Did you mean to use an interface for the before and an abstract class for the proposal? It doesn't seem like an equivalent translation.

Using base. would be ambiguous if a class implements multiple interfaces, or has a base class and one or more interfaces. This could possibly be solved with base<IAnimal> or base(IAnimal) from the DIM proposal, but that seems to me like extra verbosity for little gain.

Also, no language feature can only be a stopgap. Whatever is done will need to be supported until C#'s dying days to maintain backwards compatibility.

@YairHalberstadt
Copy link
Contributor Author

yaakov-h: I have updated my proposal to explain it better.

@YairHalberstadt
Copy link
Contributor Author

Design3 of my exploration of proposals for covariant return types in C# discusses this proposal in great detail, including it's implementation, specification, advantages and disadvantages.

See https://github.com/YairHalberstadt/csharplang/blob/master/proposals/covariant-returns-exploration.md.

@GeeLaw
Copy link

GeeLaw commented Aug 15, 2020

A few explorable areas:

  1. Should you say new for the new member?

In addition to making the override syntax stand out with override RetType base.Member, it's important for the programmer to see clearly that the new Member is, new. Since the two might be far away from each other, the new can help remind us.

  1. Can you override an explicitly overridden member again in a further-derived class?

I regard the main objective of explicit overriding is to override a member and hide it with a new one. Now suppose a class hides a member from the base class by using new, then there is no way for a further-derived class to override the member from base.

If we decide that an explicitly overridden member cannot be overridden in further-derived class, we should mark it as final in IL. But it remains unclear whether we want to disallow, allow, or force the programmer to say sealed override RetType base.Member.

If we decide that an explicitly overridden member can be overridden again in further-derived class (unless sealed is specified) --- the rationale might be that it's always possible to re-implement an interface and explicit override resembles explicit interface implementation --- then we cannot use base. as the prefix. It should be ClassName.Member. But what ClassName? I argue the NAME should be the name of the class that introduced this virtual member slot. Yet this can be very hard to remember.

@CyrusNajmabadi
Copy link
Member

@YairHalberstadt given that covariant return types are happening, do we still need this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants