【问题标题】:Implement same Generic Interface 2 times with different Generic parameter使用不同的通用参数实现相同的通用接口 2 次
【发布时间】:2011-10-05 01:33:46
【问题描述】:

我必须同时使用不同的通用参数实现 2 个接口,如下所示。我对此感到困惑。我不知道其中哪一个在 foreach 中迭代自己。现在我明白第一个是隐式选择的。

我尝试过new BarList().GetEnumerator(),但我无法在方法级别指定类型参数。

我发现的唯一解决方案是将其转换为类似界面(new BarList() as IEnumerable<string>)

在对它感到困惑之后。我只是想知道这个设计不是很好的主意吗?我必须避免再次实现相同的通用接口?

class Program
{
    static void Main(string[] args)
    {
        foreach (var item in new BarList())
        {

        }
    }
}

class BarList: IEnumerable<string>, IEnumerable<int>
{

    public IEnumerator<int> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator<string> IEnumerable<string>.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

编辑:

让我解释一下我为什么要这样做。

我必须实现从IList&lt;T&gt; 继承的IPagedList&lt;T&gt; 接口。我想编写将其转换为我的视图模型的扩展方法。如下所示

GetAll().ToPagedList(pageindex);//这是返回 IPagedList 然后我想像下面这样使用它;

GetAll().ToPagedList(pageindex).ToViewModel<T,TViewModel>();

为了实现这一点,我尝试通过该扩展方法返回IPagedList&lt;ViewModel&gt;。在这种情况下,我必须使用不同的参数实现 IPagedList 2 次。但是这种策略使事情变得混乱。这就是它的原因。

【问题讨论】:

  • 你能贴出 IPagedList 或 PagedList 的链接/代码吗?

标签: c# .net


【解决方案1】:

这似乎有点令人困惑。为什么不通过将枚举数添加为属性而不是在类上实现它们来明确发生了什么。例如,

class ProductCollection 
{
   public IEnumerable<int> EnumerateTheInts { get { //code to produce enumerator }}
   public IEnumerable<string> EnumerateTheStringss { get { //code to produce enumerator }}
}

在一个对象上两次实现一个开放的通用接口并不总是坏事。例如,IHandle 可以由可以处理两种类型 T 的类来实现。但是,我会发现两次实现 IEnumerable 会令人困惑,因为您可能不会在 for-each 或 LINQ 中枚举您期望的类型。偶然实施多个索引器的原因相同。您的索引器的类型将决定您的结果,我可以证明这非常令人困惑!

【讨论】:

    【解决方案2】:

    编译器按照 C# 语言规范 8.8.4 中的规则选择 IEnumerator&lt;int&gt; GetEnumerator 方法,首先在 BarList 类型上查找可访问的 GetEnumerator() 方法。唯一可用的是返回IEnumerator&lt;int&gt;。

    如果你让 那个 方法也使用显式接口实现,那么它将进入第 8.8.4 节的后期阶段,其中指出如果有多个类型 T 这样从表达式类型(此处为BarList)到IEnumerable&lt;T&gt; 的隐式转换会产生错误。

    我会说这是一个令人困惑的设计 - 我可能会添加属性或方法来检索数据的适当“视图”。

    【讨论】:

      【解决方案3】:

      我会避免它。但是,这取决于您的使用情况。

      如果您只是想将实例传递给一个明确需要IEnumerable&lt;string&gt; 参数的函数,那也没关系:

      • 您不必投射
      • 该函数甚至不会“看到”其他实现的接口,因此不会有任何混淆。

      YMMV

      【讨论】:

        【解决方案4】:

        您当前的设计令人困惑。虽然您没有提供有关集合本身性质的任何信息,但从名称来看,我可以假设您应该迭代一堆产品。或许,您应该简单地拥有一个类型为 Product 的类,该类具有一个 string 属性和一个 int 属性,并简单地返回一个 IEnumerable&lt;Product&gt;。

        这样,使用 LINQ 扩展方法,您可以组合 IEnumerable&lt;T&gt; 对象,您的实际意思是:

        collection.Select(product => product.IntegerProperty)
        collection.Select(product => product.StringProperty)
        

        当然,你也可以在对象内部提供辅助方法:

        class ProductCollection : IEnumerable<Product> {
            public IEnumerable<Product> GetEnumerator() {
               // ... return Product objects here.
            }
            public IEnumerable<int> AsIntegerCollection() {
               // yield the integer collection here
            }
            public IEnumerable<string> AsStringCollection() {
               // yield the string collection here
            }
        }
        

        【讨论】:

          【解决方案5】:

          这些字符串和整数的集合是什么?我想它们的意思是与产品有关的东西(例如名称、ID 等),所以我宁愿做这样的事情:

          class ProductCollection : IEnumerable<Product>
          {
              public IEnumerator<Product> GetEnumerator()
              {
                 ...
              }
          
              public IEnumerator<string> ProductNames // a helper to enumerate product names
              {
                 ...
              }
          
              public IEnumerator<int> ProductIds // a helper to enumerate product ids
              {
                 ...
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-07-25
            • 1970-01-01
            相关资源
            最近更新 更多