4

让我们看看这段代码:

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

或不?

这样的事情会产生不必要的序列枚举,这是非常不可取的吗?

4

1 回答 1

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 并且可能不应该在那里,因为它没有做任何事情。

于 2012-11-16T20:06:10.877 回答