C#将集合作为接口集合传递

时间:2012-07-06 17:55:46

标签: c# collections interface decoupling

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

导致此问题的设计决策的一些背景:我已经创建了一个用于处理“IFruit”的Web服务,但由于我理解它们的SOAP和端点绑定的性质,我创建了具有显式实现的方法IFruit。在业务层,为每个特定的水果实现创建一个单独的方法首先会导致很多重复的代码,其次紧密地结合业务和服务层,以便稍后在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);
}

4 个答案:

答案 0 :(得分:9)

尝试将EatFruit方法更改为接受IEnumerable<IFruit>

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

界面IEnumerable<out T>支持协方差,因为它标有out修饰符。 Collection<T>未标记此类修饰符。

答案 1 :(得分:1)

这就是所谓的协方差 但是,使用可变集合是不可能的。

否则,EatFruit可以写

fruits.Add(new Orange());

.Net 4确实支持reas-only接口的协方差,因此您可以将其更改为IEnumerable<IFruit>并且它将起作用。

答案 2 :(得分:1)

简单的解决方案:

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

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

答案 3 :(得分:0)

除了使用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

(它就像你做的那样安全!)