在哪些情况下我需要为 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,
...)
这适用于 IQueryable
s。但是,我也必须为 IEnumerable
s.
调用此函数
在那种情况下,我可以在 .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 扩展方法时,除了轻微的性能问题外,应该没有其他副作用。
假设我需要一个扩展方法,它只从不同的来源中选择所需的属性。源可以是数据库或内存中的集合。所以我定义了这样的扩展方法:
public IQueryable<TResult> SelectDynamic<TResult>(
this IQueryable<T> source,
...)
这适用于 IQueryable
s。但是,我也必须为 IEnumerable
s.
在那种情况下,我可以在 .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 扩展方法时,除了轻微的性能问题外,应该没有其他副作用。