在有关使用IEnumerable而不是List的初步建议之后编辑。
我正在尝试这样做:
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
}
//...
}
所以我有一个报表数据对象接口,可能还有多个报表数据类,它们实现了这个接口并添加了它们特定的额外位。我有一个抽象的报告类,它实现了通用报告功能和多个派生类,每个类都使用自己的报告数据类。
不幸的是,当我尝试在派生类中创建我的报表详细信息结构列表并将其作为接口列表传递回基类时,它会抱怨:
Cannot implicitly convert type
'System.Collections.Generic.List<NetVisSolution.Reports.AirTemp_KPIDetails>' to
'System.Collections.Generic.List<NetVisSolution.Reports.IKPIDetails>'
它不会让我隐瞒或明确地这样做。我有什么方法可以做到这一点吗?
提前致谢,
--- Alistair。
答案 0 :(得分:4)
public override List<IKPIDetails> MakeReportDataStructure()
{
List<AirTemp_KPIDetails> d = new List<AirTemp_KPIDetails>();
return d; // <-- this is where is breaks
}
应该是:
public override List<IKPIDetails> MakeReportDataStructure()
{
List<IKPIDetails> d = new List<AirTemp_KPIDetails>();
return d;
}
您的分配不起作用,因为在List<T>
中,类型参数T
不是协变的,也就是说,T
更具体时不允许分配。如果您使用IEnumerable
,您的代码将正常运行:
public override IEnumerable<IKPIDetails> MakeReportDataStructure()
{
List<AirTemp_KPIDetails> d = new List<AirTemp_KPIDetails>();
return d; // <-- works, because T in IEnumerable is covariant
}
你可能会问,为什么IEnumerable
是协变的?好吧,IEnumerable
只允许您读取数据(因此协方差用<out T>
标记),并且您不能将任何对象写入其中。 List允许您将对象写入集合,并且您将遇到危险,以后您可能会将不太具体的类型写入该集合,这可能会导致错误,因为较少特定类型不提供与更多派生类型相同的功能:
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
答案 1 :(得分:1)
这两个列表不同,即使AirTemp_KPIDetails
我是IKPIDetails
(即是-a - 关联)。您必须将元素从一个列表复制到另一个列表:
kpiDetailsList = new List<IKPIDetails>(airTempKPIList);
一般来说,这属于Co-和Contra-Variance的主题。您可能想阅读Covariance and contravariance (computer science)上的Wiki文章。
答案 2 :(得分:1)
你不应该将一种类型的List转换为另一种类型,因为插入操作会失败,或者获取。
示例:类型A实现(扩展)类型B
List<A> listA;
List<B> listB;
((List<B>) listA).add(new B());
如果这是合法的,那么你通过放置比A更通用的东西来打破listA不变量。
A a = ((List<A>) listB).get(); //like get first, or something similar
这将打破只有A对象可以分配给A引用的事实。
答案 3 :(得分:0)
您必须将列表创建为接口列表,而不是派生类型:
List<IKPIDetails> d = new List<IKPIDetails>();
答案 4 :(得分:0)
尝试使用以下代码。
public override List<IKPIDetails> MakeReportDataStructure()
{
return new List<AirTemp_KPIDetails>() as List<IKPIDetails>;
}