Can an interface property of type [abstract class] be implemented as a concrete class in C#?

Viewed 117

Perhaps a bit of a weird question.

I have an interface IReservable that describes behaviour to make a objects "reservable". A class which implements it must have a list of reservations, and a few methods to handle creating/removing a reservation. The interface declares a list of reservations, which is an abstract class inherited by 2 concrete classes; one for a room and one for a chair (RoomReservation & ChairReservation).

Both the room and chair classes implement IReservable, and both will always create instances of only one type (Chair -> ChairReservation and Room -> RoomReservation)

Is there a way to make it so that IReservable requires there to be a list of Reservations, but the implementation can decide the concrete Reservation?

explained in code:

public interface IReservable
{
    ICollection<Reservation> Reservations { get; set; }
    
    // Rest omitted for brevity
}

public class Room : IReservable
{
    // Can I make this RoomReservation only?
    public ICollection<Reservation> Reservations { get; set; }
}

public class Seat : IReservable
{
    // And can I make this ChairReservation only?
    public ICollection<Reservation> Reservations { get; set; }
}

It isn't necessary to do this, but I still wondered whether or not it was actually possible; since I can't find any examples of it.

3 Answers

You could do this by using generics. For example

First let's assume you have some classes like this:

public abstract class Reservation { }

public class ChairReservation : Reservation { }
public class RoomReservation : Reservation { }

Now your interface would change to this, note the type constraint to make sure the reservations are always derived from Reservation:

public interface IReservable<TReservation> 
    where TReservation : Reservation
{
    ICollection<TReservation> Reservations { get; set; }
}

And now your Room and Seat classes would be something like this:

public class Room : IReservable<RoomReservation>
{
    public ICollection<RoomReservation> Reservations { get; set; }
}

public class Seat : IReservable<ChairReservation>
{
    public ICollection<ChairReservation> Reservations { get; set; }
}

Even though the suggested generic interface technique works, I usually have a hard time dealing with generic interfaces. One of the advantages of interfaces is polymorphism. By using a generic interface your different implementations won't have a common base type so you will lose polymorphism. i.e., you can't do this:

IReservable reservable;
reservable = new Room();
reservable = new Seat();
Console.WriteLine(reservable.Reservations.Count);

If you never intended to take advantage of polymorphism, using a generic interface is enough (however, you may ask yourself why you need an interface in this case). Otherwise my suggestion is to use two different interfaces, one for reading reservations and one for reading and modifying:

interface IReservable
{
    IEnumerable<Reservation> Reservations { get; }
}

interface IReservable<TReservation> : IReservable
    where TReservation : Reservation
{
    new ICollection<TReservation> Reservations { get; }

    // Reservation methods
}

public class Room : IReservable<RoomReservation>
{
    public ICollection<RoomReservation> Reservations { get; }

    IEnumerable<Reservation> IReservable.Reservations => Reservations;
}

And then you will be able to use polymorphism.

I think the best solution here to use generics as @DavidG showed here

But if for some reason you cannot use generics you can use Explicit Interface Implementation

It will be something like this:

public class Room : IReservable
{
    IEnumerable<Reservation> reservations;
    ICollection<Reservation> IReservable.Reservations { get { return (ICollection<Reservation>)reservations; } set { reservations = value; } }
    public ICollection<RoomReservation> Reservations { get { return (ICollection<RoomReservation>)reservations.Cast<RoomReservation>(); } set { reservations = value; } }
}

public class Seat : IReservable
{
    IEnumerable<Reservation> reservations;
    ICollection<Reservation> IReservable.Reservations { get { return (ICollection<Reservation>)reservations; } set { reservations = value; } }
    public ICollection<SeatReservation> Reservations { get { return (ICollection<SeatReservation>)reservations.Cast<SeatReservation>(); } set { reservations = value; } }
}
Related