views:

3144

answers:

8
+1  A: 

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
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
Java allows this too, as of 1.5. It's called return type covariance.
Jon Skeet
Jon Skeet: thanks for that info, I have added it.
Anthony
+7  A: 

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
+5  A: 

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
+8  A: 

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
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
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
+2  A: 

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

VVS
This is a short and lazy answer. But it's the simpelest answer that works in this case, I like it :-) +1
Mendelt
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
Or.. "return type covariance does not exist in C#"
VVS
A: 

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
+3  A: 

EDIT: I had just woken up when I wrote the original answer, and I think I made it a little less than clear, and used the wrong word a couple times. Below is a modified version that says the same thing, but (I hope) explains better, and uses the correct terminology.

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 referencea 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).

Alex Lyman
Your point is valid, but the example had a read-only property.
Anthony
Perfectly demonstrated!
Luis Filipe
A: 

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.

Mark Cidade