Reputation: 3164
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?
Upvotes: 1
Views: 292
Reputation: 125620
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>
.
Upvotes: 4