I want an extension method that works on both my List and IQueryable. The extension methods below accomplish this, but then if I add another identical extension method, but on a different totally unrelated type I get ambiguous call compile errors. Why is that? Isn't the compiler smart enough to know which extension method works? I mean, only one of these calls 开发者_如何学运维is valid, why can't the compiler tell? Thanks a lot!
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);
}
The overload rules don't take account of the constraints on methods that it's considering - it determines which overload is best and then validates that the constraints match.
The compiler is exactly following the rules of the C# specification.
Related blog posts:
- Overloading and generic constraints (me)
- Constraints are not part of the signature (Eric Lippert)
- Evil code - overload resolution workaround (me - really nasty stuff, but fun)
EDIT: Note that using an "enumerableOrQueryable" is always going to convert your lambda expression to a delegate, not an expression tree. So if you wanted it to perform the logic differently for a database, you'd need a change anyway.
EDIT: Your idea also wouldn't work because you wouldn't get the same result type out anyway - if you call Where
on a List<string>
, the returned value isn't a List<string>
.
What you can do is this, if you can introduce a new interface to 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 resolve ambiguity from the generic constraints. For your case, can't you just do something like this ?
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);
}
}
精彩评论