【问题标题】:Can I do Contract.Ensures on IQueryable and IEnumerable?我可以在 IQueryable 和 IEnumerable 上执行 Contract.Ensures 吗?
【发布时间】:2012-11-16 13:00:10
【问题描述】:

让我们看看这段代码:

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

还是不行?

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

【问题讨论】:

  • 如果您使用的是完整的运行时合同检查器,我看不出它如何避免强制枚举发生 - 它还能如何检查它?
  • @Damien_The_Unbeliever 它不能。而且我没有使用运行时检查器。对我来说,现在,合约是一种让工具测试我的代码是否存在简单逻辑冲突的方法。
  • 因此,如果您想要一个明确的答案,您需要澄清您正在使用合同系统的哪些部分。有很多选项可以打开。
  • @Damien_The_Unbeliever 我只使用静态检查器。

标签: c# .net ienumerable iqueryable code-contracts


【解决方案1】:

假设您正在使用二进制重写器并在运行时执行合同,您应该这样做。

当你像这样使用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&lt;Category&gt; 将被迭代并导致另一个请求被发送到底层数据存储。

根据操作,您可能不会注意到它,但查询肯定会被执行两次,这对性能不利。

对于这种性质的事情,您应该在IQueryable&lt;T&gt; 的消费点检查您是否有任何元素(您可以使用设置为foreach 的布尔标志来完成它,或者当你实现它时)。

但是,如果您没有在已编译的程序集上运行二进制重写器,则无法强制执行这种性质的 Contract.Result&lt;T&gt; 合同;在这种情况下,它是一个 noop,可能不应该存在,因为它什么也没做。

【讨论】:

    猜你喜欢
    • 2011-05-02
    • 2021-08-23
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-28
    • 1970-01-01
    • 2011-06-09
    相关资源
    最近更新 更多