C# – Workaround for Casting Method Parameter Interface to Concrete Type

cinheritancepolymorphism

Let's say that I have a group of report types to be created and all of them inherit from base abstract class:

abstract class Report
{
   public abstract void GenerateReport(IReportData data);

   protected virtual string SaveReportFile(string filepath)
   {
       // common code to save the report file
   }
}

GenerateReport() method accepts an interface that presents a data object needed for specific type of report and is defined as:

interface IReportData
{
    string ReportTitle {get; set;}
    int SomeOtherProperty {get; set;}
}

Concrete implementations of reports data classes are as follows:

class ReadingsReportData : IReportData
{
    public string ReportTitle {get; set;}
    public int SomeOtherProperty {get; set;}
    public Dictionary<DateTime, double> Readings {get; set;}
}

class CalculationReportData : IReportData
{
    public string ReportTitle {get; set;}
    public int SomeOtherProperty {get; set;}
    public int Factor {get; set;}
    public List<decimal> Calculations {get; set;}   
}

Now, I have several concrete report classes that should use concrete implementation of IReportData respectively – CalculationReport needs to work on
CalculationReportData object and ReadingsReport should use ReadingsReportData object

class CalculationReport : Report
{
   public override void GenerateReport(IReportData data)
   {
       CalculationReportData cdata = data as CalculationReportData; 
       // do something with cdata
   }
}

class ReadingsReport : Report
{
   public override void GenerateReport(IReportData data)
   {
      ReadingsReportData rdata = data as ReadingsReportData;        
      // do something with rdata
   }
}

To get to required ReportData object for every report type, I have to cast interface to desired concrete implementation class, which is not nice.

What are my options to change the design so that I would not need to perform that cast?

Best Answer

If it were me, I would dispense with the IReportData interface, and make your abstract class generic:

abstract class Report<T>
{
   public abstract void GenerateReport(T data);

   protected virtual string SaveReportFile(string filepath)
   {
       // common code to save the report file
   }
}