【问题标题】:Pattern for exposing non-generic version of generic interface暴露非泛型接口的非泛型版本的模式
【发布时间】:2011-10-01 04:17:47
【问题描述】:

假设我有以下用于公开分页列表的界面

public interface IPagedList<T>
{
    IEnumerable<T> PageResults { get; }
    int CurrentPageIndex { get; }
    int TotalRecordCount { get; }
    int TotalPageCount { get; }        
    int PageSize { get; }
}   

现在我想创建一个分页控件

public class PagedListPager<T>
{
    public PagedListPager<T>(IPagedList<T> list)
    {
        _list = list;
    }

    public void RenderPager()
    {
        for (int i = 1; i < list.TotalPageCount; i++)
            RenderLink(i);
    }
}

分页控件对T(列表的实际内容)没有兴趣。它只需要页数、当前页等。所以PagedListPager 是通用的唯一原因是它可以使用通用IPagedList&lt;T&gt; 参数进行编译。

这是代码异味吗?我应该关心我实际上有一个多余的泛型吗?

在这种情况下,是否有标准模式可以公开额外的非泛型接口版本,以便我可以删除寻呼机上的泛型类型?

public class PagedListPager(IPagedList list)

编辑

我想我也会添加我解决此问题的当前方式,并邀请 cmets 了解它是否是合适的解决方案:

public interface IPagedList // non-generic version
{
    IEnumerable<object> PageResults { get; }
    int CurrentPageIndex { get; }
    int TotalRecordCount { get; }
    int TotalPageCount { get; }        
    int PageSize { get; }
}


public class ConcretePagedList<T> : IPagedList<T>, IPagedList
{
    #region IPagedList<T> Members

    public IEnumerable<T> PageResults { get; set; }
    public int CurrentPageIndex { get; set; }
    public int TotalRecordCount { get; set; }
    public int PageSize { get; set; }

    #endregion

    #region IPagedList Members

    IEnumerable<object> IPagedList.PageResults
    {
        get { return PageResults.Cast<object>(); }
    }

    #endregion
}

现在我可以将ConcretePagedList&lt;T&gt; 传递给非泛型类/函数

【问题讨论】:

  • PagedListPager&lt;T&gt; 是类还是方法声明?
  • @Gregg 抱歉,我会编辑它。
  • 我不喜欢在两个接口中定义属性,因为你可以显式地实现它们来做不同的事情。例如, IPagedList.PageSize {get{return 8;}} IPageList.PageSize{get{return this.PageResults.Count();}} 您拥有第二个界面的唯一原因是提供强类型,所以 Marc 的回答似乎消除了类对不应不同的属性产生不同结果的能力。

标签: c# design-patterns generics interface open-generics


【解决方案1】:

我在这里的方法是使用new 重新声明PageResults,并将T 公开为Type

public interface IPagedList
{
    int CurrentPageIndex { get; }
    int TotalRecordCount { get; }
    int TotalPageCount { get; }        
    int PageSize { get; }

    Type ElementType { get; }
    IEnumerable PageResults { get; }
}   

public interface IPagedList<T> : IPagedList
{
    new IEnumerable<T> PageResults { get; }
}  

然而,这将需要“显式接口实现”,即

class Foo : IPagedList<Bar>
{
    /* skipped : IPagedList<Bar> implementation */

    IEnumerable IPagedList.PageResults {
        get { return this.PageResults; } // re-use generic version
    }
    Type IPagedList.ElementType {
        get { return typeof(Bar); }
    }
}

这种方法使 API 可以通过通用和非通用 API 完全可用。

【讨论】:

  • 谢谢 Marc,我喜欢这个,它与我想出的非常相似,但实现更简洁。
  • @Moop 取决于签名的变化方式。你有具体的例子吗?
  • @MarcGravell 这样的事情:public interface IBag { IFruit GetNextObject();} public interface IBag&lt;T&gt; where T : IFruit { new T GetNextObject();}
【解决方案2】:

一种选择是创建 2 个接口,这样:

    public interface IPagedListDetails
    {
        int CurrentPageIndex { get; }
        int TotalRecordCount { get; }
        int TotalPageCount { get; }
        int PageSize { get; }
    }

    public interface IPagedList<T> : IPagedListDetails
    {
        IEnumerable<T> PageResults { get; }
    }

然后你的控制:

public class PagedListPager(IPagedListDetails details)

【讨论】:

  • 我曾考虑过这一点,但觉得接口继承通常不是一个好主意?
  • 接口继承没有问题,如果你看看.NET BCL你会发现有各种泛型接口继承自非泛型类
  • @fearofawhackplanet:恕我直言,接口继承未得到充分利用。恕我直言,像 IList 这样的东西应该派生自 IReadableByIndex、IMutableByIndex、IAppendable 和 IRemovableByIndex;数组应该实现了前两个而不是后两个。
【解决方案3】:

定义两个接口,先

    public interface IPageSpecification
    {
        int CurrentPageIndex { get; }
        int TotalRecordCount { get; }
        int TotalPageCount { get; }        
        int PageSize { get; }
    }   

public interface IPagedList<T> : IPageSpecification
{
    IEnumerable<T> PageResults { get; }
}   

如您所见,IPagedList 派生自 IPageSpecification。在您的方法中,仅使用 IPageSpecification 作为参数。在其他情况下,IPagedList - IPagedList 的实现者也将包含来自 IPageSpecification 的数据

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多