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

Champion "Covariant Return Types" (VS 16.8, .NET 5) #49

Open
1 of 5 tasks
gafter opened this issue Feb 9, 2017 · 69 comments
Open
1 of 5 tasks

Champion "Covariant Return Types" (VS 16.8, .NET 5) #49

gafter opened this issue Feb 9, 2017 · 69 comments
Assignees
Labels
Design Review Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification Proposal champion
Milestone

Comments

@gafter
Copy link
Member

gafter commented Feb 9, 2017

@gafter gafter self-assigned this Feb 9, 2017
@gafter gafter added this to the X.0 candidate milestone Feb 22, 2017
@Joe4evr
Copy link
Contributor

Joe4evr commented Jun 23, 2017

This would apply to methods and properties, and be supported in classes and interfaces.

The part about support in interfaces, does this mean something like the following?

public interface IFoo { }
public interface IBar
{
    IFoo Get();
}
public interface IBaz : IFoo { }
public interface IQux : IBar
{
    override IBaz Get(); //or would it be 'override IBaz IBar.Get();'?
}

I guess it's lucky that discussions around Default Interface Implementations have pushed back a little bit on using override as a keyword for just that. Even if it wasn't, there probably could've been a way for the two to co-exist, but if you're talking about adding one new thing to interfaces, it would be good to consider the other things that are proposed to be added to interfaces, and make sure that one design choice doesn't preclude any other features entirely.

@JonHanna
Copy link

I find myself wishing C# supported covariant returns very often, and often with code that isn't particularly "clever".

@TylerBrinkley
Copy link

Covariant returns would be very useful for typed cloning methods, preventing the need to manually create unique protected cloning methods.

@ghost
Copy link

ghost commented May 25, 2018

@gafter @Joe4evr @JonHanna @TylerBrinkley @srivatsn
Or better:

public interface IQux : IBar
{
    this Get();
}

or better #1566 :

public interface IQux : IBar
{
    [IBaz] Get(); 
}


@ghost
Copy link

ghost commented May 25, 2018

Why didn't this happen yet?
It seems popular enough.

@HaloFour
Copy link
Contributor

@MohammadHamdyGhanem

Why didn't this happen yet?
It seems popular enough.

Because you haven't written it yet.

Seriously, there are a million "good" ideas, modifying the language is a very expensive process and the team has finite resources. Things take time.

@ghost
Copy link

ghost commented May 25, 2018

@HaloFour

Because you haven't written it yet.

If I can write "it", I would rather write a new S# (Smart Sharp) language, where I do whatever I want :).

Anyway, may God help the team :)

@yaakov-h
Copy link
Member

Would this need CLR changes, or compiler changes only?

@BreyerW
Copy link

BreyerW commented Jun 10, 2018

According to linked proposal document it is purely compiler magic. It is mentioned that compiler will spit two methods instead of one. One virtual override and another who will call this override and cast result to more specific instance so nothing fancy. Though for that reason alternative mentioned in proposal SEEMS to be better for performance but that will come at the cost of ugliness in code (method duplications).

I wonder if both couldnt be combined into one proposal? So compiler will produce same code to alternative but source code will look like original proposal.

@NickRedwood
Copy link

For a long time I didn't realise that the problems I had with many of my inheritance/interface heirarchies nearly always came back to this common problem - the lack of return type covariance to allow overriding methods to narrow their return types. This then leads to duplication of methods and properties to satisfy both the base and the subclass contracts, which is a whole lot of general messyness.

So I am very much in favour of this proposal.

@YairHalberstadt
Copy link
Contributor

@gafter
I think this feature is an extremely important one, and would be willing to put some work towards implementing it. I know @ldematte did some work on this, and I can build on his proposal.

If you think this is likely to succeed, I will go ahead. Some pointers on the process I will need to take would be helpful.

@ldematte, I assume you are no longer working on this, but if you are, please let me know, and we can discuss if I can help. Also, if you've achieved anything useful so far, it would be great if I could build on that.

Also @sharwell and @HaloFour were arguing on how best to implement this, and no decision seemed to be made. Should I just go for this with Method Shadowing, or do we need to finish that discussion first?

@gafter
Copy link
Member Author

gafter commented Sep 12, 2018

I think this will require some (perhaps substantial) design work before the desired/best implementation techniques are decided/agreed.

@YairHalberstadt
Copy link
Contributor

@gafter
What would that involve? Would a proposal containing C# and target IL for a variety of examples be sufficient?

@gafter
Copy link
Member Author

gafter commented Sep 13, 2018

@YairHalberstadt That would help. It would also help to understand how this would affect any tools that consume IL, including compilers for other languages.

@YairHalberstadt
Copy link
Contributor

Ok. I will get started on a design proposal.

@ldematte
Copy link

To save you a little time, fell free to recycle the work I did when I was looking at feasibility.
There is already a mock C# syntax (which is really simple), target IL compiled with ILASM and tested with the C# and VB compilers. And with ILASM/DASM and reflector.
A tool that needed modification for sure was Visual Studio: intellisense got confused, producing strage/non intuitive suggestions, but it worked (no crashes).
Do you have a link at my work/gists?

@ldematte
Copy link

@YairHalberstadt
Copy link
Contributor

Thanks Idematte. I'll definitely use whatever I can from your work.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Sep 14, 2018

A tool that needed modification for sure was Visual Studio: intellisense got confused, producing strage/non intuitive suggestions, but it worked (no crashes).

Can you be more specific? What strange/unintuitive suggestions are you referring to? Thanks!

@ldematte
Copy link

Honestly, I can't recall.
Mind you, the test was done a couple of years ago, meaning VisualStudio 2015, I don't know how VS2017 could react. I probably still have the dlls though, I can try them again (or recreate them from .il)

@ldematte
Copy link

I have just tried it with VS2017 and it works flawlessly, proposing them as overloads (even it is just the return type changing).
I still have a test project with a compiled dll in my dropobox if you want to try it out.

@YairHalberstadt
Copy link
Contributor

YairHalberstadt commented Sep 14, 2018 via email

@ldematte
Copy link

https://www.dropbox.com/sh/hw0sgrae83i93lm/AAC_YXcNxjY3q70JExCoAWfOa?dl=0

There is also a draft proposal I forgot I wrote :)
And a console application with some performance tests too (the stub was designed to work efficiently across multiple subclasses)

@gafter
Copy link
Member Author

gafter commented May 1, 2020

@kinchungwong
Copy link

My two cents.

Repurpose the as keyword in the method return type specification.

For example:

public abstract class Foo {
    public abstract object DoStuff();
}

public class ProposedBar : Foo {
    public override string as object DoStuff() => "stuff"; // covariantly overrides Foo.DoStuff()
}

The presence of the as keyword can trigger some compiler magic that will help the compiler understand which interface method or base class abstract method it is supposed to be implementing covariantly.

The use of as keyword can be made optional. It is only required if the compiler do not have enough information to figure out automatically.

@gafter
Copy link
Member Author

gafter commented Jul 21, 2020

@kinchungwong The compiler doesn't need any "help" to understand which method is being overridden. The return type was never part of that calculation.

@TobiasBengtsson
Copy link

TobiasBengtsson commented Jul 23, 2020

Are there any plans to make it simpler to write functions that returns typeof(this)? It seems like when we have covariant return types, just some syntactic sugar needs to be added to support this. This is one of the most common scenarios for covariant returns I have encountered. E.g.

abstract class FluentBuilder
{
    public virtual thistype DoSomething() // new keyword - will return FluentBuilder in this class, but subclass type in subclasses
    {
        // ...
        return this; // Can only be allowed to "return this" for it to work(?)
    }
}
class SomeFluentBuilder : FluentBuilder { } // will not need to override DoSomething to make it return SomeFluentBuilder

instead of (C#9)

abstract class FluentBuilder
{
    public virtual FluentBuilder DoSomething()
    {
        // ...
        return this;
    }
}
class SomeFluentBuilder : FluentBuilder
{
    public override SomeFluentBuilder DoSomething()
    {
        base.DoSomething();
        return this;
    }
}

@yaakov-h
Copy link
Member

There are other proposals that cover that.

@jcouv jcouv changed the title Champion "Covariant Return Types" Champion "Covariant Return Types" (VS 16.8, .NET 5) Sep 1, 2020
@MadsTorgersen MadsTorgersen modified the milestones: 9.0 candidate, 9.0 Sep 9, 2020
@J0rgeSerran0
Copy link

One question about Covariant Return Types
I am a little confused with it

Is this feature ready to be used in the last version of NET 5 (5.0.100-rc.1.20452.10 version) or is in progress yet?

@amis92
Copy link
Contributor

amis92 commented Oct 2, 2020

One question about Covariant Return Types
I am a little confused with it

Is this feature ready to be used in the last version of NET 5 (5.0.100-rc.1.20452.10 version) or is in progress yet?

It's implemented but RC1 contains a bug with using derived type in abstract causing a TypeLoadException. Hit me. RC2 has it fixed.

@J0rgeSerran0
Copy link

I was writing this because my Covariant Return Types code was not working as expected and I was confused about it
Thanks a lot for the details about the issue with it @amis92

@J0rgeSerran0
Copy link

I confirm you that, with the .NET 5 RC2 published yesterday, Covariant Return Types is working correctly now

@333fred 333fred added the Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification label Oct 16, 2020
@Daniel15
Copy link

It sounds like this is now available in C# 9.0 (ref https://devblogs.microsoft.com/dotnet/c-9-0-on-the-record/#covariant-returns) so this issue can probably be closed?

@BreyerW
Copy link

BreyerW commented Mar 29, 2021

@Daniel15 such issues are closed only after spec is added (look for Needs ECMA spec badge)

@erichiller
Copy link

erichiller commented Nov 23, 2021

The spec leaves open the question of whether override would be supported for interface implementators to have covariant return types, has a decision been made on this?

example, the spec mentions

interface I1 { I1 M(); }
interface I2 { I2 M(); }
interface I3: I1, I2 { override I3 M(); }

But currently override is not allowed on interface properties as far as I can tell.

@TahirAhmadov
Copy link

It's a bit unclear from this thread and other notes. Is it done using "CLR hack" of just allowing covariant return types, or the compiler double-method emittance?

@HaloFour
Copy link
Contributor

@TahirAhmadov

The CLR was modified to allow the overriding method to have a covariant return type. I don't see why that would be considered a "hack", though.

@TylerBrinkley
Copy link

Perhaps they thought of the solution as a "hack" since it required users to target a new CLR as opposed to being backwards compatible with older CLR's like that of the .NET Framework.

@TahirAhmadov
Copy link

@TahirAhmadov

The CLR was modified to allow the overriding method to have a covariant return type. I don't see why that would be considered a "hack", though.

You'll laugh but that's how it was characterized by a member of the language team about 10 years ago when I first brought it up, before GitHub.

@andre-ss6
Copy link

@TahirAhmadov
The CLR was modified to allow the overriding method to have a covariant return type. I don't see why that would be considered a "hack", though.

You'll laugh but that's how it was characterized by a member of the language team about 10 years ago when I first brought it up, before GitHub.

Yea, things changed a lot in that time. I understand that the main reason they think differently now is the fact that changing the CLR no longer introduces the risk of automatically breaking every .NET application on your PC.

@TheConstructor
Copy link

The spec leaves open the question of whether override would be supported for interface implementators to have covariant return types, has a decision been made on this?

example, the spec mentions

interface I1 { I1 M(); }
interface I2 { I2 M(); }
interface I3: I1, I2 { override I3 M(); }

But currently override is not allowed on interface properties as far as I can tell.

One thing I would love, would be to write GetEnumerator() once to implement IEnumerable and IEnumerable<T>. Would override-support in interfaces be required for this?

@Charlieface
Copy link

@gafter @MadsTorgersen

What is happening with doing this for interfaces as well? Is that going ahead? Is there another issue tracking that?

If not, what are the arguments against doing so? I didn't see any in this thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Review Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification Proposal champion
Projects
None yet
Development

No branches or pull requests