【问题标题】:returning a list<interface> in C#在 C# 中返回一个列表<接口>
【发布时间】:2012-09-18 11:39:02
【问题描述】:

在关于使用 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>'

它不会让我隐含或明确地这样做。有什么办法可以做到吗?

提前致谢,

--- 阿利斯泰尔。

【问题讨论】:

    标签: c# interface


    【解决方案1】:
    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&lt;T&gt; 中,类型参数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 只允许你读取数据(因此协方差用&lt;out T&gt; 标记),你不能向其中写入任何对象。 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
    

    【讨论】:

    • 其实我不是协方差方面的专家,我在解释这个问题时遇到了一些问题,我什至可以从中提出一个问题,请耐心等待:)
    • 感谢快速重播,它看起来不错,直到我尝试构建它,当它说'NetVisSolution.Reports.SSAirTempSummaryReport.MakeReportDataStructure()':返回类型必须是'System.Collections.Generic .List' 以匹配被覆盖的成员 'NetVisSolution.Reports.BaseReport.MakeReportDataStructure()' 我尝试了几种将 IEnumerable 和 List 放在不同位置的变体,但没有一个起作用。还有其他建议吗?
    • 将修改后的代码放入您的问题中,以便我们可以看到您究竟做了哪些更改。
    • 我已经修改了我的代码。更改的行注释为 // 以前... 希望他们清楚。
    • 好吧,我没说你应该选择IEnumerable。这完全取决于您的要求。我只是试图解释协方差。我的代码中的第二个代码 sn-p 是应该可以帮助您的解决方案。现在你遇到了问题,因为你改变了MakeReportDataStructure的签名(在基类中你返回List在派生中你返回IEnumerable)。
    【解决方案2】:

    您不应该将一种类型的 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】:

    这两个列表是不同的,尽管 AirTemp_KPIDetails 我是 IKPIDetails(即具有 is-a-关系)。您必须将元素从一个列表复制到另一个:

    kpiDetailsList = new List<IKPIDetails>(airTempKPIList);
    

    一般来说,这属于协方差和反方差的主题。您可能想阅读Covariance and contravariance (computer science) 上的 Wiki 文章。

    【讨论】:

      【解决方案4】:

      您必须将列表创建为接口列表,而不是派生类型:

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

      【讨论】:

        【解决方案5】:

        尝试使用下面的代码。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-24
          • 1970-01-01
          • 2012-09-20
          相关资源
          最近更新 更多