Overriding an abstract property with a derived return type in c#
Asked Answered
U

6

34

I have four classes. Request, DerivedRequest, Handler, DerivedHandler. The Handler class has a property with the following declaration:

public abstract Request request { get; set; }

The DerivedHandler needs to override this property so that it returns DerivedRequest instead:

public override DerivedRequest request { get; set; }

Does anyone have any ideas about how to make this work?

Ulcer answered 14/6, 2011 at 22:42 Comment(3)
This is not strictly good OOP, as it violates the interface. Some types of setter operations (with non-dervied values) will throw unexpectedly.Ragtime
In this case, I suppose, I wouldn't need a setter. I could make a private property and set it in the constructor. That would take care of setter operation exceptions, yes?Ulcer
In that case, you wouldn't need to override the property. Just have the constructor only accept the DerivedRequest.Ragtime
A
21

This isn't really a good way to structure things. Do one of the following

1) Just don't change the return type, and override it normally in the subclass. In DerivedHandler you can return an instance of DerivedRequest using the base class signature of Request. Any client code using this can choose to cast it to DerivedRequest if they want to.

2) Use generics instead if they are not supposed to be polymorphic.

public abstract class HandlerBase<T> where T: Request
{
    public abstract T Request {get;set;}
}

public class Handler: HandlerBase<Request>()

public class DerivedHandler: HandlerBase<DerivedRequest>()
Abolish answered 14/6, 2011 at 22:58 Comment(1)
If there is multiple cases, such as several properties that I want to override in a such way that they use derived classes, then I need to do something like class HandlerBase<A,B,C,..> where A:classA, B:classB,... ?Nita
K
6

In the C# language you are not allowed to change the signature of an inherited method, unless you substitute it with another method with the same name. This technique is referred to as "member hiding" or "shadowing".

If you are using .NET 2.0 or later, you could solve this problem by turning the return type of the Request property into a generic type parameter of the Handler class. The DerivedHandler class would then specify the DerivedRequest class as argument for that type parameter.

Here's an example:

// Handler.cs
public class Handler<TRequest> where TRequest : Request
{
    public TRequest Request { get; set; }
}

// DerivedHandler.cs
public class DerivedHandler : Handler<DerivedRequest>
{
}
Kagera answered 14/6, 2011 at 22:55 Comment(2)
You certainly can do so; nothing in "OOP" forbids you from doing so as long as the Liskov Substitution Principle is maintained. For example, in the object oriented language "C++" it is legal to override a method that returns an Animal with one that returns a Tiger. This feature is called "return type covariance" and it is fairly common in OOP languages. It is not a feature of C# though.Leges
@Eric Lippert You're absolutely right. I'm familiar with return type covariance and parameter type contravariance. C# has supported it for delegate types since 1.0. Later C# 4.0 added support for generic interfaces and delegate types as well. Thanks for pointing out my mistake, I corrected the answer.Kagera
W
5

Except for hiding the original property:

public new DerivedRequest Request { get;set;}

However, I strongly advise against that. Hiding something supposed to be overriden is inviting trouble, especially if the property isn't a simple auto generated one. Also, if using it as an interface or base class, the original implementation (in that case, one class higher in the inheritance tree). If you are implementing an abstract class or interface, you won't even be able to hide the original signature, as you are required to implement it.

Usually, if you think about using the new keyword, you are on the wrong track. There are cases where it is necessary and required, however, in most cases, it isn't.

Instead, make another property:

public DerivedRequest DerivedRequest {/* make adequate conversions here*/ }

That way, you are on the clear side concerning OOP and you get your information in a clear way.

Waksman answered 14/6, 2011 at 22:47 Comment(2)
Well, how would you handle setter?Keitloa
Any DerivedRequest is still a Request further down and can be directly assigned.Waksman
M
1

Edit: You can't change the type on a derived type, but new might help:

In the derived type...

public new DerivedRequest request
{
   get{return (DerivedRequest) base.request;}
   set{base.request = value;}
}
public override Request request
{
   get{return base.request;}
   set{base.request = (DerivedRequest) value;} // Throws InvalidCastException if misused.
}
Molybdate answered 14/6, 2011 at 22:44 Comment(1)
this is not usually called "override"Keitloa
K
0

This is not theoretically possible. The override must be covariant for return type (that is, the return type must be more specific or the same), and contravariant for parameter (that is, parameter type must be less specific or the same). So your new type must be effectively at the same time covariant and contravariant with respect to the Request -- that means, the only possible type is just Request.

For this reason, it's not allowed in C# to change the type of properties for overrides.

Keitloa answered 14/6, 2011 at 22:48 Comment(2)
Wouldn't DerivedRequest be more specific than Request, and consequently make the override covariant for return type? Or did I read your comment wrong?Ulcer
@threed: a property consists of a getter and a setter. the problem is with the setter :-)Keitloa
M
0
public class Request{}

public class DerivedRequest : Request{}

public class Handler<T>
  where T : Request
{
  public abstract T Request { get; set; }
}

public class DerivedHandler : Handler<DerivedRequest>
{
  public override DerivedRequest Request { get; set; }
}
Molybdate answered 14/6, 2011 at 22:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.