让我们看看这段代码:
public IQueryable<Category> GetAllActive()
{
Contract.Ensures(Contract.Result<IQueryable<Category>>() != null);
return dataSource.GetCategories(T => T.IsActive);
}
有一个小问题。代码合同是否可以写下这个:
public IQueryable<Category> GetAllActive()
{
Contract.Ensures(Contract.Result<IQueryable<Category>>() != null);
Contract.Ensures(Contract.Result<IQueryable<Category>>().All(T => T.IsActive));
return dataSource.GetCategories(T => T.IsActive);
}
或不?
这样的事情是否会产生不必要的序列枚举,这是非常不可取的?
答案 0 :(得分:2)
假设您正在使用二进制重写器并在运行时强制执行合同,您应该不执行此操作。
当您使用Contract.Ensures
时:
Contract.Ensures(Contract.Result<T>() <operation>);
return expression;
它被转换,操作被提升为如下所示:
T expression = <expression>;
// Perform checks on expression.
if (!(expression <operation>) <throw appropriate exception>;
// Return value.
return expression;
在这种情况下,这意味着您的代码将展开:
IQueryable<Category> temp = dataSource.GetCategories(T => T.IsActive);
// Perform checks.
if (!(temp != null)) throw new Exception();
if (!temp.All(T => T.IsActive)) throw new Exception();
// Return values.
return temp;
在这种情况下,您的IQueryable<Category>
将被迭代,并将另一个请求发送到基础数据存储。
根据操作的不同,您可能不会注意到它,但查询肯定将执行两次,这对性能有害。
对于这种性质的东西,你应该在IQueryable<T>
的消费点检查你是否有任何元素(你可以使用设置为foreach
的布尔标志来做通过它,或当你实现它。)
但是,如果您不在已编译的程序集上运行二进制重写器,则无法强制执行此类Contract.Result<T>
个合同;在这种情况下,它是一个noop,可能不应该在那里,因为它没有做任何事情。