Supposing I have the following interfaces:
public interface IVehicle
{
ITrackInfo TrackInfo { get; set; }
void ShowInfo();
}
public interface ITrackInfo
{
int Length { get; set; }
}
Now I have these classes implementing ITrackInfo
public class RailInfo : ITrackInfo
{
int Length { get; set;}
int SleeperSpacing { get; set; }
}
public class CanalInfo : ITrackInfo
{
int Length { get; set; }
decimal AverageDepth { get; set; }
}
public class RoadInfo : ITrackInfo
{
int Length { get; set; }
Color Colour { get; set; }
}
But with the classes that implement IVehicle
, I want them to have a specific implementation of ITrackInfo
.
public class Train : IVehicle
{
RailInfo TrackInfo { get; set; }
public void ShowInfo() {
Console.WriteLine("Railway is {0}km long". TrackInfo.Length.ToString());
Console.WriteLine("Distance between sleepers is {0}cm", TrackInfo.SleeperSpacing);
}
}
public class Boat : IVehicle
{
CanalInfo TrackInfo { get; set; }
public void ShowInfo() {
Console.WriteLine("Canal is {0}km long". TrackInfo.Length.ToString());
Console.WriteLine("The average depth is {0}m", TrackInfo.AverageDepth.ToString("F2"));
}
}
public class Bus : IVehicle
{
RoadInfo TrackInfo { get; set; }
public void ShowInfo() {
Console.WriteLine("Road is {0}km long". TrackInfo.Length.ToString());
Console.WriteLine("The road is coloured {0}", TrackInfo.Colour.ToString());
}
}
Obviously I can't write it as shown above - this produces a compile error in the IVehicle
implementations as the TrackInfo
property must be specified as ITrackInfo
.
Is there a way of forcing specific implementations of ITrackInfo
for specific implemenations of IVehicle
?
I'm hoping for something a bit more elegant than just putting a check in ShowInfo()
:
var info = TrackInfo as RailInfo;
if (info == null)
{
throw new Exception("Incorrect track info");
}
Is there a way of forcing specific implementations of
ITrackInfo
for specific implemenations ofIVehicle
?
Yes, use generics like this:
public interface IVehicle<TI> where TI: ITrackInfo
{
TI TrackInfo { get; set; }
void ShowInfo();
}
Here you are saying that TrackInfo
's type will be a concrete implementation of ITrackInfo
.
With this in your hand, the IVehicle
implementations can be fixed:
public class Train: IVehicle<RailInfo>
{
public RailInfo TrackInfo { get; set; }
public void ShowInfo()
{
Console.WriteLine("Railway is {0}km long", TrackInfo.Length.ToString());
Console.WriteLine("Distance between sleepers is {0}cm", TrackInfo.SleeperSpacing);
}
}
public class Boat: IVehicle<CanalInfo>
{
public CanalInfo TrackInfo { get; set; }
public void ShowInfo()
{
Console.WriteLine("Canal is {0}km long", TrackInfo.Length.ToString());
Console.WriteLine("The average depth is {0}m", TrackInfo.AverageDepth.ToString("F2"));
}
}
public class Bus: IVehicle<RoadInfo>
{
public RoadInfo TrackInfo { get; set; }
public void ShowInfo()
{
Console.WriteLine("Road is {0}km long", TrackInfo.Length.ToString());
Console.WriteLine("The road is coloured {0}", TrackInfo.Colour.ToString());
}
}
Dotnet Fiddle link: https://dotnetfiddle.net/GAb9k9