【问题标题】:C# Passing a collection as a collection of interfacesC# 将集合作为接口集合传递
【发布时间】:2012-07-07 05:10:47
【问题描述】:

我在下面的代码中简化并重现了我的问题。我收到的错误是:参数 1:无法从 'System.Collections.ObjectModel.Collection' 转换为 'System.Collections.ObjectModel.Collection

导致此问题的设计决策的一些背景:我创建了一个用于处理“IFruit”的 Web 服务,但由于 SOAP 和端点绑定的性质,据我所知,我创建了具有显式实现的方法水果。在业务层上,为每个水果的具体实现创建一个单独的方法首先会导致大量重复代码,其次业务和服务层耦合得足够紧密,以至于以后在 Web 服务中接受新的 IFruit 类型也需要更改我的代码在业务层中(添加另一个冗余代码副本)。这种设计是我过去使用 Java 成功实现的一种设计,但 C# 接口似乎完全不同,足以让我失望。请指教。

public interface IFruit{
    public string TakeBite();
}

public Apple : IFruit{
    public string TakeBite(){
        return "tasty bite of apple";
    }
}

public void EatFruit(Collection<IFruit> fruits){
    foreach(var fruit in fruits){
        Console.WriteLine(fruit.TakeBite());
    }
}

public void EatApples(Collection<Apple> apples){
    this.EatFruit(apples);
}

【问题讨论】:

    标签: c# collections interface decoupling


    【解决方案1】:

    简单的解决方案:

    public void EatApples(Collection<Apple> apples){
         this.EatFruit(
              new Collection<IFruit>(
                  apples.Cast<IFruit>()
              )
         );
    }
    

    更好的解决方案:使用 C# 4 中引入的协方差:参见 Kevin 的回答

    【讨论】:

      【解决方案2】:

      除了使用 IEnumerable 之外,您还可以键入检查集合(有点可怕,但它应该可以工作):

      public void EatApples(Collection<IFruit> fruit)
          var col = new Collection<IFruit>();
      
          fruit.Where(x => x is Apple).ToList().ForEach(x => col.Add(x));
      
          this.EatFruit(col);
      }
      

      虽然我会改用 IEnumerable - 如果您无法重构,这只是一个选项:P

      (它和你做的一样安全!)

      【讨论】:

      • 这将做 2 倍的工作 - 创建一个 List 然后将每个添加到集合中。最好这样做:foreach (var x in fruit.OfType&lt;Apple&gt;()) { col.Add(x); }.
      • 是的,我没有说过它会表现出色(我也确实提到了可怕的东西!):D
      【解决方案3】:

      尝试更改您的 EatFruit 方法以接受 IEnumerable&lt;IFruit&gt;

      public void EatFruit(IEnumerable<IFruit> fruits)
      {
          foreach (var fruit in fruits)
          {
              Console.WriteLine(fruit.TakeBite());
          }
      }
      

      接口IEnumerable&lt;out T&gt; 支持协方差,因为它用out 修饰符标记。 Collection&lt;T&gt; 没有标记这样的修饰符。

      【讨论】:

        【解决方案4】:

        这称为协方差。
        但是,对于可变集合,这是不可能的。

        否则,EatFruit 可以写

        fruits.Add(new Orange());
        

        .Net 4 确实支持纯接口的协方差,因此您可以将其更改为 IEnumerable&lt;IFruit&gt; 并且它会起作用。

        【讨论】:

        • 假设 Orange 实现了 IFruit 那么那里有问题吗?
        • @Charleh: fruits实际上Collection&lt;Apple&gt;。它不能容纳Orange
        • 我认为由于 fruits 是一个 Collection,它可以包含任何实现 IFruit 的类型?
        • @Charleh:他通过了Collection&lt;Apple&gt;,而不是Collection&lt;IFruit&gt;
        • 抱歉完全错过了调用前一个的最后一个方法:D
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-16
        • 2023-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多