在哪些情况下我需要为 IEnumerable 和 IQueryable 创建两种不同的扩展方法?

In which cases do I need to create two different extension methods for IEnumerable and IQueryable?

假设我需要一个扩展方法,它只从不同的来源中选择所需的属性。源可以是数据库或内存中的集合。所以我定义了这样的扩展方法:

 public IQueryable<TResult> SelectDynamic<TResult>(
            this IQueryable<T> source,
            ...)

这适用于 IQueryables。但是,我也必须为 IEnumerables.

调用此函数

在那种情况下,我可以在 .AsQueryable():

的帮助下调用它
  myEnumerable.AsQueryable()
        .SelectDynamic(...)
        .ToList();

两者都很好。如果两者都工作正常,在哪些条件下 我必须为同一目的创建两种不同的扩展方法,一种用于 IEnumerable,另一种用于 IQueryable?

Queryable 的情况下,我的方法必须向数据库发送查询。

例如,这里是 System.Linq 命名空间中 .Select 扩展方法的来源:

我再次重复我的主要问题:

我的方法必须在 Queryable 的情况下向数据库发送查询,但在 IEnumerable 的情况下则不需要。现在,我使用 AsQueryable() 作为可枚举值。因为,我不想为 Enumerable 编写相同的代码。 会不会有一些副作用?

如果您的代码仅在其处理的对象加载到内存中时才真正起作用,只需提供 IEnumerable 变体并让您的 调用者 决定他们何时需要将 IQueryable 转换为内存中的 IEnumerable.

通常,您不会围绕 IQueryable 实施新的变体,除非您正在编写新的数据库提供程序。

myEnumerable.AsQueryable() returns 自定义对象:new EnumerableQuery<TElement>(myEnumerable); (source code)

EnumerableQuery class 实现 IEnumerable<T>IQueryable<T>

当使用 .AsQueryable()EnumerableQuery 结果作为 IEnumerable 时,接口方法 IEnumerable<T>.GetIterator() 的实现只是 returns 原始源迭代器,所以没有变化和最小的开销。

当使用.AsQueryable()的结果作为IQueriable时,接口[=52​​=]IQueriable.Expression的实现简单returnsExpression.Constant(this),准备好稍后在整个表达式树被消耗时作为 IEnumerable 进行评估。

(据我所知,当 EnumerableQuery 直接从 IEnumerable 构造时,EnumerableQuery 的所有其他方法和代码路径都不是真正相关的)

如果我没理解错的话,你已经实现了你的方法 selectDynamic<TResult>() ,你在方法中构建了一个表达式树,编译时会产生所需的结果。

据我了解源代码,当您调用例如myEnumerable.AsEnumerable().selectDynamic().ToList(),你构建的表达式树是在 myEnumerable 上编译和执行的,总开销应该是相当小的,因为所有这些工作只在每个查询中完成一次,而不是每个元素一次。

所以我认为像这样实现您的 IEnumerable 扩展方法没有任何问题:

public IEnumerable<TResult> SelectDynamic<TResult>(
        this IEnumerable<T> source,...)
    return source.AsQueryable().SelectDynamic();
}

有一些轻微的开销,因为每次调用此方法时都会编译一次查询,而且我不确定 JITer 是否足够智能以缓存此编译。但我认为在大多数情况下这不会引起注意,除非您每秒执行此查询一千次。

以这种方式实现 IEnumerable 扩展方法时,除了轻微的性能问题外,应该没有其他副作用。