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>は繰り返され、別のリクエストが基になるデータ ストアに送信されます。

操作によっては気付かないかもしれませんが、クエリは確実に 2 回実行され、パフォーマンスが低下します。

この性質のものについてはIQueryable<T>、要素があるかどうかを消費する時点で確認する必要があります (それをforeach介して、またはそれを具体化するときに設定されたブール値フラグを使用して行うことができます)。

ただし、コンパイル済みアセンブリでバイナリ リライターを実行していないContract.Result<T>場合、この性質のコントラクトを適用することはできません。この場合、それはヌープであり、何もしていないため、おそらくそこにあるべきではありません。

于 2012-11-16T20:06:10.877 に答える