Question

This is driving me bonkers, so I figured I'd toss it up to the community rather than bang my head against my desk.

I'm switching out an area of my application from using IEnumerable to ICollection. I have several Extension Methods defined in IEnumerableOfIInterfaceExtensions that I want to translate into ICollectionOfIInterfaceExtensions and after I've switched everything out under the covers but when I switch out the collection on my repository, I get the (unexpected) error:

System.Collections.Generic.ICollection<T>' does not contain a definition for 'Bar' and the best extension method overload 'Application.Data.Repositories.RepositoryExtensions.Bar(System.Collections.Generic.ICollection<Application.Domain.Models.IInterface>, System.Collections.Generic.IEnumerable<Application.Domain.Models.IInterface>)' has some invalid arguments   

This only pops up when I try to use ICollection rather than IEnumerable. My implementation with IEnumerable

[Edit I've updated to add my class definition for clarity. IDataRepository contains a single method "Retrieve" that returns an IEnumerable and IEntityRepository contains a single void method "Save" that accepts an IInterface

public class Repository<T>: IDataRepository<T>,IEntityRepository<T>
                        where T: class, IInterface
{
    protected virtual IEnumerable<T> Collection{ get; set;}
    public virtual void Bar(T thing)
    {
        try
        {
            if (Collection == null) return;
            Collection = (IEnumerable<T>)Collection.Foo(thing);

        }
        catch(Exception ex)
        {
            //breakpoint for debugging
        }
    }
}

Foo's signature as IEnumerable

internal static class MyExtensions
{
    internal static IEnumerable<IInterface> Foo(this ICollection<IInterface> c1,
                                                    IEnumerable<IInterface> c2)
    {
        //implementation removed for posting
        return c1;
    }
internal static IEnumerable<IInterface> Foo(this IEnumerable<IInterface> c1,
                                                    IInterface thing)
    {
        return Foo(c1, new List<IInterface>() { thing });
    }
}

As an IEnumerable, CodeLens reports that each Foo has 1 reference. The client calls into the second one which wraps around the first one. Whenever I switch these to ICollection, my references in CodeLens disappear and I get the exception I posted above...which makes no sense seeing as how I have extension methods on ICollection at the point the exception is thrown. Here is Foo's interface using ICollections

protected virtual ICollection<T> Collection { get; set;}
    public virtual void Bar(T thing)
    {
        try
        {
            if (Collection == null) return;
            Collection = (ICollection<T>)Collection.Foo(entity);

        }
        catch(Exception ex)
        {
            //breakpoint
        }
    }

internal static class MyExtensions
{
    internal static ICollection<IInterface> Foo(this ICollection<IInterface> c1,
                                                    IEnumerable<IInterface> c2)
    {
        //implementation removed for posting
        return c1;
    }
    internal static ICollection<IInterface> Foo(this ICollection<IInterface> c1,
                                                    IInterface thing)
    {
        return Foo(cache, new List<IInterface>() { thing });
    }
}

Can anyone identify what the problem is when switching this over to an ICollection from an IEnumerable?

Was it helpful?

Solution

This is covariance problem. IEnumerable<T> is covariant, ICollection<T> is not.

public interface IEnumerable<out T> : IEnumerable
public interface ICollection<T> : IEnumerable<T>, IEnumerable

You can see the out keyword only in IEnumerable<T> declaration.

That's why your extension method works only for ICollection<IInterface>, and not for ICollection<ClassWhichImplementsIInterface>.

Make your extension methods generic to make it work:

internal static class MyExtensions
{
    internal static ICollection<T> Foo<T>(this ICollection<T> c1, IEnumerable<T> c2) where T : IInterface
    {
        //implementation removed for posting
        return c1;
    }
    internal static ICollection<T> Foo<T>(this ICollection<T> c1, T thing) where T : IInterface
    {
        return Foo(cache, new List<T>() { thing });
    }
}

or

internal static class MyExtensions
{
    internal static ICollection<IInterface> Foo<T>(this ICollection<IInterface> c1, IEnumerable<T> c2) where T : IInterface
    {
        //implementation removed for posting
        return c1;
    }
    internal static ICollection<IInterface> Foo<T>(this ICollection<T> c1, T thing) where T : IInterface
    {
        return Foo(cache, new List<IInterface>() { thing });
    }
}

Depending what you want it to return: ICollection<IInterface> or ICollection<T>.

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