开发者

Extension method that works on IEnumerable<T> and IQueryable<T>?

开发者 https://www.devze.com 2023-04-12 03:55 出处:网络
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 unrel

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号