46

As far as i know it is not possible to do the following in C# 2.0

public class Father
{
    public virtual Father SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

public class Child : Father
{
    public override Child SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

I workaround the problem by creating the property in the derived class as "new", but of course that is not polymorphic.

public new Child SomePropertyName

Is there any solution in 2.0? What about any features in 3.5 that address this matter?

Luis Filipe
  • 8,488
  • 7
  • 48
  • 76
  • 1
    All the solutions except for the one that says you cant do not really help the situation. I think the point was to be able to have a Child class (cast as a father) and call SomeProperty and have that return a Child without a cast. Of course i am guessing at the true purpose like evryone else :) – mattlant Oct 01 '08 at 11:37
  • Despite of no perfect solution exists, the idea is to find out the clearest way to implement this, with the least noisy stuff, like casts, check types using "is" and so on. – Luis Filipe Oct 01 '08 at 12:34
  • 3
    Although you can't do this in C#, I've written a series of blog posts about doing exactly this: http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/14/93495.aspx – thecoop Aug 01 '10 at 20:57
  • As of C#9, [yes, you can override with derived types](https://stackoverflow.com/a/68627108/5405967). – MarredCheese Sep 21 '21 at 16:54

9 Answers9

45

You can re-declare (new), but you can't re-declare and override at the same time (with the same name). One option is to use a protected method to hide the detail - this allows both polymorphism and hiding at the same time:

public class Father
{
    public Father SomePropertyName
    {
        get {
            return SomePropertyImpl();
        }
    }
    protected virtual Father SomePropertyImpl()
    {
        // base-class version
    }
}

public class Child : Father
{
    public new Child SomePropertyName
    {
        get
        { // since we know our local SomePropertyImpl actually returns a Child
            return (Child)SomePropertyImpl();
        }
    }
    protected override Father SomePropertyImpl()
    {
        // do something different, might return a Child
        // but typed as Father for the return
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
30

This is not possible in any .NET language because of type-safety concerns. In type-safe languages, you must provide covariance for return values, and contravariance for parameters. Take this code:

class B {
    S Get();
    Set(S);
}
class D : B {
    T Get();
    Set(T);
}

For the Get methods, covariance means that T must either be S or a type derived from S. Otherwise, if you had a reference to an object of type D stored in a variable typed B, when you called B.Get() you wouldn't get an object representable as an S back -- breaking the type system.

For the Set methods, contravariance means that T must either be S or a type that S derives from. Otherwise, if you had a reference to an object of type D stored in a variable typed B, when you called B.Set(X), where X was of type S but not of type T, D::Set(T) would get an object of a type it did not expect.

In C#, there was a conscious decision to disallow changing the type when overloading properties, even when they have only one of the getter/setter pair, because it would otherwise have very inconsistent behavior ("You mean, I can change the type on the one with a getter, but not one with both a getter and setter? Why not?!?" -- Anonymous Alternate Universe Newbie).

Neuron
  • 5,141
  • 5
  • 38
  • 59
Alex Lyman
  • 15,637
  • 3
  • 38
  • 42
  • 3
    Your point is valid, but the example had a read-only property. – Anthony Oct 01 '08 at 12:02
  • 2
    You are introducing an aritficial constraint that `Get` has to return the same `T` that `Set` receives. Without this constraint your argument does not hold. See this answer for a better explanation: http://stackoverflow.com/questions/5709034/does-c-sharp-support-return-type-covariance – lex82 Apr 26 '17 at 13:02
13

No, but you can use generics in 2 and above:

public class MyClass<T> where T: Person
{
    public virtual T SomePropertyName
    {
        get
        {
            return  ...;
        }
    }
}

Then Father and Child are generic versions of the same class

Keith
  • 150,284
  • 78
  • 298
  • 434
  • 1
    Not quite the same thing. Under his code, this: Father child = new Child(); Child newChild = child.SomePropertyName; would require a cast. With yours, it won't even compile. – James Curran Oct 01 '08 at 13:40
  • You're right, it isn't. This is just a rather basic example of something similar - you can't quite match his code with generics, I think you'd get a circular type reference. – Keith Oct 01 '08 at 17:29
8

Modern answer

As of C# 9, return type covariance is supported. Here's a basic example copied from that link:

class Compilation ...
{
    public virtual Compilation WithOptions(Options options)...
}

class CSharpCompilation : Compilation
{
    public override CSharpCompilation WithOptions(Options options)...
}
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
7

From Wikipedia:

In the C# programming language, support for both return-type covariance and parameter contravariance for delegates was added in version 2.0 of the language. Neither covariance nor contravariance are supported for method overriding.

It doesn't explicitly say anything about covariance of properties though.

dalle
  • 18,057
  • 5
  • 57
  • 81
2

You can create a common interface for father and child and return a type of that interface.

VVS
  • 19,405
  • 5
  • 46
  • 65
  • This is a short and lazy answer. But it's the simpelest answer that works in this case, I like it :-) +1 – Mendelt Oct 01 '08 at 11:21
  • The laziest answer would be: Since Child is also of type Father you can simply return the child without changing the return type of the property. ;-) – VVS Oct 01 '08 at 12:27
  • Or.. "return type covariance does not exist in C#" – VVS Oct 01 '08 at 12:29
2

This is the closest I could come (so far):

public sealed class JustFather : Father<JustFather> {}

public class Father<T> where T : Father<T>
{ 
    public virtual T SomePropertyName
    { get { return (T) this; }
    }
}

public class Child : Father<Child>
{ 
    public override Child SomePropertyName
    { get { return  this; }
    }
}

Without the JustFather class, you couldn't instantiate a Father<T> unless it was some other derived type.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
1

No. C# does not support this idea (it's called "return type covariance"). You can however do this:

public class FatherProp
{
}

public class ChildProp: FatherProp
{
}


public class Father
{
    public virtual FatherProp SomePropertyName
    {
        get
        {
            return new FatherProp();
        }
    }
}


public class Child : Father
{
    public override FatherProp SomePropertyName
    {
        get
        {
            // override to return a derived type instead
            return new ChildProp();
        }
    }
}

i.e. use the contract defined by the base class, but return a derived type. I have made a more detailed sample to make this point clearer - returning "this" again wouldn't change anything.

It is possible (but messy) to test the returned object for it's actual type (i.e. "if someObject is ChildProp"), but it is better to call a virtual method on it that does the right thing for its type.

The base class virtual method (in this case, virtual property) not only has an implementation, but also defines a contract: that a child class can supply a different implementation of SomePropertyName if it meets this contract (i.e. SomePropertyName returns an object of type "FatherProp"). Returning an object of type "ChildProp" derived from "FatherProp" meets this contract. But you can't change the contract in "Child" - this contract applies to all classes descended from "Father".

If you take a step back and look at your broader design, there are other language constructs in the C# toolkit that you may also want to think about instead - Generics, or interfaces.

Anthony
  • 5,176
  • 6
  • 65
  • 87
  • Not sure it's worth another answer, but in C++, you could change the return type to return a Child rather than Father as return types are allowed to get more specialised in derived class virtual functions. However, the only reason is because they can be used as the base type so it is superfluous :) – workmad3 Oct 01 '08 at 11:10
  • Java allows this too, as of 1.5. It's called return type covariance. – Jon Skeet Oct 01 '08 at 11:37
1

No. C# does not support this idea (it's called "return type covariance").

From Wikipedia:

In the C# programming language, support for both return-type covariance and parameter contravariance for delegates was added in version 2.0 of the language. Neither covariance nor contravariance are supported for method overriding.

You can re-declare (new), but you can't re-declare and override at the same time (with the same name). One option is to use a protected method to hide the detail - this allows both polymorphism and hiding at the same time:

The best solutions would be to use generics:

public class MyClass<T> where T: Person
{
   public virtual T SomePropertyNameA
   {        
      get { return  ...; }    
   }
}//Then the Father and Child are generic versions of the same class
Micah
  • 111,873
  • 86
  • 233
  • 325