Question

I have simplified and reproduced my problem in the code below. The error that I am receiving is: Argument 1: cannot convert from 'System.Collections.ObjectModel.Collection' to 'System.Collections.ObjectModel.Collection

Some background for the design decisions that led to this issue: I have created a web service for dealing with "IFruit", but due to the nature of SOAP and endpoint bindings as I understand them I have created methods with explicit implementations of IFruit. On the business layer, creating a separate method for each specific implementation of fruit would firstly cause a lot of duplicate code, and secondly couple the business and service layer tightly enough that accepting new IFruit types in the web service later would also require changing my code in the business layer (adding another copy of redundant code). This design is one I have successfully implemented using Java in the past, but C# interfaces seem to be just different enough to throw me. Please advise.

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);
}
Was it helpful?

Solution

Try changing your EatFruit method to accept IEnumerable<IFruit>

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

The interface IEnumerable<out T> supports covariance since it is marked with the out modifier. Collection<T> is not marked with such a modifier.

OTHER TIPS

That's called covariance.
However, it's not possible with mutable collections.

Otherwise, EatFruit could write

fruits.Add(new Orange());

.Net 4 does support covariance for reas-only interfaces, so you can change it to IEnumerable<IFruit> and it will work.

Simple solution :

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

Better solution : use the covariance introduced in C# 4 : see Kevin's answer

Aside from using IEnumerable you could type check the collection (bit horrid but it should work):

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);
}

Though I'd use IEnumerable instead - this is just an option if you can't refactor :P

(it's as type safe as you make it!)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top