c# – 我可以在IQueryable和IEnumerable上进行Contract.Ensures吗?

让我们看看这段代码:

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

或不?

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

最佳答案 假设您正在使用二进制重写器并在运行时强制执行合同,则不应执行此操作.

当您像这样使用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>的消费点.你是否有任何元素(你可以使用一个布尔标志,你通过它设置,或实现它时).

但是,如果您没有在已编译的程序集上运行二进制重写器,则无法执行此类性质的Contract.Result<T>合同;在这种情况下,它是一个noop,可能不应该在那里,因为它没有做任何事情.

点赞