user41013
user41013

Reputation: 1241

returning a list<interface> in C#

Edited after initial suggestions about using IEnumerable instead of List.

I am trying to do this:

public interface IKPIDetails // Previously: interface IKPIDetails
{ 
    string name {get; set;}
    //...
}


public abstract class BaseReport
{
    public List<IKPIDetails> m_KPIDetails;  // Previously: list<IKPIDetails> m_KPIDetails;

    public BaseReport()
    {
        m_KPIDetails = MakeReportDataStructure();
        //...
    }
    public abstract List<IKPIDetails> MakeReportDataStructure(); // Previously: public abstract IKPIDetails MakeReportDataStructure();

    //...
}


public class AirTemp_KPIDetails : NetVisSolution.Reports.IKPIDetails // Previously: protected class AirTemp_KPIDetails : IKPIDetails
{
    #region IKPIDetails implementation
    private string _Name;
    public string Name
    {
        get { return _Name; }
        set { _Name = value; }
    }
    #endregion
    ...
}

public class SSAirTempSummaryReport : BaseReport
{
    public SSAirTempSummaryReport() : base ()
    {
        // Do any extra initialisation here
        m_reportName = "Air Temperature Summary Report";
    }

    //It now complains on the declaration below at the word
    // MakeReportDataStructure()
    public override IEnumerable<IKPIDetails> MakeReportDataStructure() // Previously: public override List<IKPIDetails> MakeReportDataStructure()
    {
        List<AirTemp_KPIDetails> d = new List<AirTemp_KPIDetails>();
        return  d;  // <-- this is where it used to break
    }

    //...
}

So I have a report data object interface and potentially multiple report data classes implementing this interface and adding their specific extra bits. And I have an abstract report class implementing generic report functionality and multiple derived classes that each use their own report data class.

Unfortunately, when I try to create a list of my report detail structures in the derived class and pass it back to the base class as a list of the interface, it complains:

Cannot implicitly convert type 
'System.Collections.Generic.List<NetVisSolution.Reports.AirTemp_KPIDetails>' to 
'System.Collections.Generic.List<NetVisSolution.Reports.IKPIDetails>'

It won't let me do it implicitely or explicitly. Is there any way that I can do this?

Thanks in advance,

--- Alistair.

Upvotes: 1

Views: 784

Answers (5)

Bartosz
Bartosz

Reputation: 3358

public override List<IKPIDetails> MakeReportDataStructure()
{
    List<AirTemp_KPIDetails> d = new List<AirTemp_KPIDetails>();
    return  d;  // <-- this is where is breaks
}

Should be:

public override List<IKPIDetails> MakeReportDataStructure()
{
    List<IKPIDetails> d = new List<AirTemp_KPIDetails>();
    return  d;
}

Your assignment does not work, because in List<T>, type argument T is not covariant, that is, assignment is not allowed when T is more specific. Should you use IEnumerable, your code would work fine:

public override IEnumerable<IKPIDetails> MakeReportDataStructure()
{
    List<AirTemp_KPIDetails> d = new List<AirTemp_KPIDetails>();
    return  d;  // <-- works, because T in IEnumerable is covariant
}

You may ask, why IEnumerable is covariant? Well, IEnumerable only allows you to read data (hence covariance is marked with <out T>), and you cannot write any object into it. List allows you to write objects into collection, and you would run into danger that you would later write less specific type to that collection, which could cause error, as less specific type does not provide same functionality as more derived one:

public override IEnumerable<BaseClass> MakeList()
{
    List<DerivedClass> d = new List<DerivedClass>();
    // ...
    return  d; // assume it would work
}

var l = MakeList();
l.Add(BaseClass); // now we added object of BaseClass to the list that expects object of DerivedClass, any operation on the original list (the one with List<DerivedClass> type could break

Upvotes: 4

Nitesh Kumar
Nitesh Kumar

Reputation: 1774

Try to use the below code.

public override List<IKPIDetails> MakeReportDataStructure() 
{ 
    return new List<AirTemp_KPIDetails>() as List<IKPIDetails>; 
}

Upvotes: 0

Kuba
Kuba

Reputation: 3056

you shouldn't cast a List of one type to another because, then either the insert operation would fail, or the getting.

example: type A implements(extends) type B

List<A> listA;
List<B> listB;


((List<B>) listA).add(new B()); 

if this would legal, then you break the listA invariant by putting something more general than A.

A a = ((List<A>) listB).get(); //like get first, or something similar

this would break the fact that only A objects, could be assigned to the A reference.

Upvotes: 1

Dennis Traub
Dennis Traub

Reputation: 51634

You must create the list as a list of the interface, not the derived type:

List<IKPIDetails> d = new List<IKPIDetails>();

Upvotes: 0

Matthias Meid
Matthias Meid

Reputation: 12513

These two lists are different, even though AirTemp_KPIDetails my be a IKPIDetails (i.e. have an is-a-relation). You must copy the elements from one list to the other:

kpiDetailsList = new List<IKPIDetails>(airTempKPIList);

In general this belong to the subject of Co- and Contra-Variance. You may want to read the Wiki Article on Covariance and contravariance (computer science).

Upvotes: 1

Related Questions