29

Is the code below the only way to implement covariant return types?

public abstract class BaseApplication<T> {
    public T Employee{ get; set; }
}

public class Application : BaseApplication<ExistingEmployee> {}

public class NewApplication : BaseApplication<NewEmployee> {}

I want to be able to construct an Application or a NewApplication and have it return the appropriate Employee type from the Employee property.

var app = new Application();
var employee = app.Employee; // this should be of type ExistingEmployee

I believe this code works fine, but it gets really nasty when I have several properties that require the same behavior.

Are there any other ways to implement this behavior? Generics or otherwise?

Brian
  • 1,027
  • 2
  • 11
  • 17
  • 5
    I don't see anything involving covariance in your code... – Thomas Levesque Dec 03 '10 at 18:47
  • 2
    I don't understand what you want different from what your code does? – CodesInChaos Dec 03 '10 at 18:55
  • Btw the way, your current example has a syntax problem - new Application(); does not match the class definition of Application. (No generic!) – asawyer Dec 03 '10 at 18:58
  • I guess what I'm looking for is any other way to do this. It will get really nasty when the class definition looks like: Application – Brian Dec 03 '10 at 18:59
  • 2
    @Thomas: The question states the covariant mapping: "I want to be able to construct a NewApplication and have it return the appropriate Employee type". The covariant relation is not *assignment compatibility* but rather *virtual slot compatibility*. The question would have been more clear had property Employee been marked as abstract or virtual. – Eric Lippert Dec 03 '10 at 20:16

7 Answers7

75

UPDATE: This answer was written in 2010. After two decades of people proposing return type covariance for C#, it looks like it will finally be implemented; I am rather surprised. See the bottom of https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/ for the announcement; I'm sure details will follow. The portions of the answer below which speculate on the possibility of the feature being implemented should be considered of historical interest only going forwards.


First off, the answer to your question is no, C# does not support any form of return type covariance on virtual overrides.

A number of answerers and commenters have said "there is no covariance in this question". This is incorrect; the original poster was entirely correct to pose the question as they did.

Recall that a covariant mapping is a mapping which preserves the existence and direction of some other relation. For example, the mapping from a type T to a type IEnumerable<T> is covariant because it preserves the assignment compatibility relation. If Tiger is assignment compatible with Animal, then the transformation under the map is also preserved: IEnumerable<Tiger> is assignment compatible with IEnumerable<Animal>.

The covariant mapping here is a little bit harder to see, but it is still there. The question essentially is this: should this be legal?

class B
{
    public virtual Animal M() {...}
}
class D : B
{
    public override Tiger M() {...}
}

Tiger is assignment-compatible with Animal. Now make a mapping from a type T to a method "public T M()". Does that mapping preserve compatibility? That is, if Tiger is compatible with Animal for the purposes of assignment, then is public Tiger M() compatible with public Animal M() for the purposes of virtual overriding?

The answer in C# is "no". C# does not support this kind of covariance.

Now that we have established that the question has been asked using the correct type algebra jargon, a few more thoughts on the actual question. The obvious first problem is that the property has not even been declared as virtual, so questions of virtual compatibilty are moot. The obvious second problem is that a "get; set;" property could not be covariant even if C# did support return type covariance because the type of a property with a setter is not just its return type, it is also its formal parameter type. You need contravariance on formal parameter types to achieve type safety. If we allowed return type covariance on properties with setters then you'd have:

class B
{
    public virtual Animal Animal{ get; set;}
}
class D : B
{
    public override Tiger Animal { ... }
}

B b = new D();
b.Animal = new Giraffe();

and hey, we just passed a Giraffe to a setter that is expecting a Tiger. If we supported this feature we would have to restrict it to return types (as we do with assignment-compatibility covariance on generic interfaces.)

The third problem is that the CLR does not support this kind of variance; if we wanted to support it in the language (as I believe managed C++ does) then we would have to do some reasonably heroic measures to work around signature matching restrictions in the CLR.

You can do those heroic measures yourself by carefully defining "new" methods that have the appropriate return types that shadow their base class types:

abstract class B 
{
    protected abstract Animal ProtectedM();
    public Animal Animal { get { return this.ProtectedM(); } }
}
class D : B
{
    protected override Animal ProtectedM() { return new Tiger(); }
    public new Tiger Animal { get { return (Tiger)this.ProtectedM(); } }
}

Now if you have an instance of D, you see the Tiger-typed property. If you cast it to B then you see the Animal-typed property. In either case, you still get the virtual behaviour via the protected member.

In short, we have no plans to ever do this feature, sorry.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Is there any way to do this in VB.net? When I try to declare a method as both 'overrides' and 'shadows' the compiler squawks. – supercat Dec 13 '10 at 21:31
  • 1
    @Supercat: I don't really understand the question; why would you want to both *override* and shadow? You want to *overload* and shadow, right? – Eric Lippert Dec 13 '10 at 23:43
  • @Eric Lippert: Override and shadow. Example #1: I have an object of type FordFactory which derives from CarFactory, which provides a MakeCar() method that returns a Car. It would be handy if SomeFordFactory.MakeCar() would return a Ford (which derives from Car). An override of MakeCar must return a Car. A shadow of MakeCar could return a Ford. What I'd like to do is have an overriden MakeCar function which calls the shadowed MakeCar routine (which returns a Ford) and returns the result as a Car. – supercat Dec 13 '10 at 23:57
  • @Eric Lippert: Another use for override and shadow would be when a base class ReadableFoo supports an abstract read-only property and a derived class MutableFoo should support a read-write property of the same name. If one level of inheritance is used to override the property, another level can shadow the property with a read-write one, but that's rather clunky. – supercat Dec 13 '10 at 23:59
  • @Eric Lippert: I misread your example; I see that the overridden method is protected, so the public one can be recast as needed. That approach works okay for the methods case, but I still don't know any good approach for the property case. – supercat Dec 14 '10 at 00:02
  • @EricLippert, do any of these problems exist if we disallow covariance in property *setters*, and only allow it in method returns (including property getters)? Java and C++ both support return type covariance; is there some limitation in the CLR that makes this impossible? – kpozin Feb 06 '12 at 22:10
  • @kpozin: The CLR does not support return type covariance; an overriding virtual method's signature has to *exactly* match the signature of the overridden method (using the CLR's rules for signature matching, in which the return type is part of the signature). As I said, you can emulate return type covariance through clever use of "new" virtual methods and helper methods that are mere shims that defer to other methods. – Eric Lippert Feb 06 '12 at 23:07
3

There might be multiple problems with what you try to achieve.

First of all, as somebody already noticed, there is no covarianace in your example. You can find a short description of covariance and generics here, new features in C# 2.0 - Variance, covariance on generics.

Secondly it seems that you try to solve with generics what should be solved with polymorphism. If both ExistingEmployee and NewEmployee inherit from a base class Employee, your problem would be solved:

public class Application {
    public ExistingEmployee Employee { get; }
}

public class NewApplication {
    public NewEmployee Employee { get; }
}

...

Application app = new Application;
var emp = app.Employee; // this will be of type ExistingEmployee!

Please note that the below is also true:

Employee emp = app.Employee; // this will be of type ExistingEmployee even if 
                             // declared as Employee because of polymorphism

The one thing that would be different between polymorphism and generics, would be that if you return the variable to the specific type you would need a cast in the later case:

ExistingEmployee emp = (ExistingEmployee)app.Employee;  // would have not been needed 
                                                        // if working with generics

Hope this helps.

Hauleth
  • 22,873
  • 4
  • 61
  • 112
Florin Dumitrescu
  • 8,182
  • 4
  • 33
  • 29
  • Both derived employee classes do inherit from a base Employee class. However when I get the Employee property from the Application, it will return the proper type but I will have to use casting to get to the derived class's specific properties. This just looks ugly ((ExistingEmployee)emp).ExistingId – Brian Dec 03 '10 at 19:43
  • I could also implement the property in the derived class and cast there, but then that kind of defeats the purpose of the implementation in the abstract class. – Brian Dec 03 '10 at 19:46
  • @Brian - using var emp = app.Employee does actually set emp as an ExistingEmployee. You do not have to make the cast. You can directly access emp.ExistingId! However, related to the fact that you say casting looks ugly. It might do, but that is the way inheritance works and it is a good way for grouping common functionality. When you have custom functionality, you indeed have to cast. – Florin Dumitrescu Dec 03 '10 at 19:55
  • @Florin - you're right, I was mixing the code in my original question with another method that I am attempting at the moment. – Brian Dec 03 '10 at 19:59
  • Also you don't have to cast for any property you access, you can cast only once when declaring the variable: ExistingEmployee emp = (ExistingEmployee)app.Employee; ... int id = emp.ExistingId; – Florin Dumitrescu Dec 03 '10 at 20:02
  • @Florin: There is too covariance; the covariant relation is the compatibility of a property in a derived type with an overridden property in a base type; the question would have been more clear had it called out that the property was virtual. – Eric Lippert Dec 03 '10 at 20:19
  • 1
    @Eric - indeed there is a covariant relation between the inheritance direction in the property and in the containers. However, in this example I don't think there is covariancy at generics level, or none that would solve the problem. In fact, as said in my answer, I think the use of generics in this example is redundant. – Florin Dumitrescu Dec 03 '10 at 20:45
1

You could code against an Employee Interface to get what you want I think.

public interface IEmployee
{}

public abstract class BaseApplication<T> where T:IEmployee{ 
    public T IEmployee{ get; set; } 
} 

public class ExistingEmployee : IEmployee {}
public class NewEmployee : IEmployee {}

public class Application : BaseApplication<ExistingEmployee> {} 

public class NewApplication : BaseApplication<NewEmployee> {} 
asawyer
  • 17,642
  • 8
  • 59
  • 87
  • I can't use interfaces because the Employee types will actually have different members specific to the type. – Brian Dec 03 '10 at 19:01
0

You can achieve a somewhat neat looking version of this using generics.

Covariant return types are not supported by c#. So this is not a solution, however, my feeling is that syntactically speaking this reads well. It does achieve a similar result.

I find it useful when creating fluent API's where the base class needs to perform some actions, but I need the derived implementation back. All it really achieves is to hide the cast.

public class Base
{
    public virtual T Foo<T>() where T : Base 
    { 
        //... // do stuff
        return (T)this; 
    }
}

public class A : Base
{
    public A Bar() { "Bar".Dump(); return this; }
    public A Baz() { "Baz".Dump(); return this; }

    // optionally override the base...
    public override T Foo<T>() { "Foo".Dump(); return base.Foo<T>(); }
}

var x = new A()
    .Bar()
    .Foo<A>() // cast back to A
    .Baz();

Opinions will vary, and it's not 100% pretty. It's probably not appropriate for an API that will be published, but for internal use, for instance in unit tests, I find it useful.

Jim
  • 14,952
  • 15
  • 80
  • 167
0

The code you posted will not compile but I get the basic idea of what you want to do. In short the answer is yes, that is the only way. If you want a property to return different types and be typed differently in extended classes then you have to use generics in the way that you already are.

If you can encapsulate the public contract of an employee object, new or existing, into an interface then you don't need to use generics at all. Instead you can just return the interface and let polymorphism take over.

public interface IEmployee
{ }

public class Employee1 : IEmployee
{ }

public class Employee2 : IEmployee
{ }

public abstract class ApplicationBase
{
    public abstract IEmployee Employee { get; set; }
}

public class App1 : ApplicationBase
{
    public override IEmployee Employee
    {
        get { return new Employee1(); }
        set;
    }
}

public class App2 : ApplicationBase
{
    public override IEmployee Employee
    {
        get { return new Employee2(); }
        set;
    }
}
Ryan Pedersen
  • 3,177
  • 27
  • 39
-1

YES!! Like this. There is more boiler plate than you would hope for, but it does work. The trick is done with extension methods. It dose some nasty casting internally, but presents a covariant interface.

See also: http://richarddelorenzi.wordpress.com/2011/03/25/return-type-co-variance-in-c/

using System;

namespace return_type_covariance
{
    public interface A1{} 
    public class A2 : A1{}
    public class A3 : A1{}

    public interface B1 
    {
        A1 theA();
    }

    public class B2 : B1
    {
        public A1 theA()
        {
            return new A2();
        }
    }

    public static class B2_ReturnTypeCovariance
    {
        public static A2 theA_safe(this B2 b)
        {
            return b.theA() as A2;    
        }
    }

    public class B3 : B1
    {
        public A1 theA()
        {
            return new A3();    
        }
    }

    public static class B3_ReturnTypeCovariance
    {
        public static A3 theA_safe(this B3 b)
        {
            return b.theA() as A3;    
        }
    }

    public class C2
    {
        public void doSomething(A2 a){}    
    }

    class MainClass
    {
        public static void Main (string[] args)
        {
            var c2 = new C2();
            var b2 = new B2();
            var a2=b2.theA_safe();

            c2.doSomething(a2);
        }
    }
}
ctrl-alt-delor
  • 7,506
  • 5
  • 40
  • 52
-2

One idea without generics, but it has other downsides:

public abstract class BaseApplication {
 public Employee Employee{ get; protected set; }
}

public class Application : BaseApplication
{
 public new ExistingEmployee Employee{ get{return (ExistingEmployee)base.Employee;} set{base.Employee=value; }}
}

public class NewApplication : BaseApplication
{
 public new NewEmployee Employee{ get{return (NewEmployee)base.Employee;} set{base.Employee=value; }}
}

In particular with this code you can cast to the base class and assign an employee of an undesirable type. So you need to add checks against that in the setter of the base-class. Or remove the setter, which I usually prefer anyways. one way to do that is making the setter protected.
Another is adding a virtual function EmployeeType() which you override in derived classes and return a derived type. Then you check in the setter if EmployeeType().IsInstanceOf(value) and else throw an exception.

And IMO simulating covariant return types is one of the few good applications of the new marker. It returns the same thing as the base-class and just adds additional guarantees to the function contract.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262