An extension method that works on IEnumerable <T> and IQueryable <T>?
I need an extension method that works with both my list and IQueryable. The extension methods are given below, but if I add another identical extension method, but on a completely different type, I get an ambiguous call to compile the errors. Why is this? Isn't the compiler smart enough to find out which extension method works? I mean, only one of these calls is valid, why can't the compiler say? Many thanks!
class ClassA { public bool IsActive{ get; set;} } class ClassB { public bool IsActive { get; set;} } // then here are my extensions public static T IsActive<T>(this T enumerableOrQueryable, bool isActive) where T : IEnumerable<ClassA> { return (T)enumerableOrQueryable.Where(x => x.IsActive == isActive); } public static T IsActive<T>(this T enumerableOrQueryable, bool isActive) where T : IEnumerable<ClassB> { return (T)enumerableOrQueryable.Where(x => x.IsActive == isActive); }
Overload rules do not take into account the restrictions on the methods that he considers - he determines which overload is best, and then checks whether the restrictions are consistent.
The compiler strictly follows the rules of the C # specification.
Related Blog Entries:
- Overload and general restrictions (me)
- Limitations not included in signature (Eric Lippert)
- Evil code is a solution to the problem of overloading (to me - really unpleasant stuff, but fun).
EDIT: Note that using "enumerableOrQueryable" will always convert your lambda expression to a delegate, not an expression tree. Therefore, if you want it to execute logic differently for the database, you still need a change.
EDIT: Your idea won't work either, because you still won't get the same type of result - if you call Where
on a List<string>
, the return value is not List<string>
.
What you can do is if you can introduce a new interface that will be implemented by both ClassA and ClassB:
public static IQueryable<T> IsActive<T>(this IQueryable<T> source, bool isActive) where T : ICanBeActive { // Lambda converted to an expression tree return source.Where(x => x.IsActive == isActive); } public static IEnumerable<T> IsActive<T>(this IEnumerable<T> source, bool isActive) where T : ICanBeActive { // Lambda converted to a delegate return source.Where(x => x.IsActive == isActive); }
The compiler cannot disambiguate the general constraints. In your case, can't you just do something like that?
public static IEnumerable<ClassA> IsActive(this IEnumerable<ClassA> enumerableOrQueryable, bool isActive) { return enumerableOrQueryable.Where(x => x.IsActive == isActive); }
You can try something like this:
public interface IActivatable { bool IsActive { get; set; } } public class ClassA : IActivatable { public bool IsActive{ get; set;} } public class ClassB : IActivatable { public bool IsActive { get; set;} } public static class Ext { public static IEnumerable<T> IsActive<T>(this IEnumerable<T> collection, bool isActive) where T : IActivatable { return collection.Where(x => x.IsActive == isActive); } }