Понимание контрактов кода для интерфейсов

1

Я хочу создать кодовый контракт для определенного интерфейса, однако мне трудно поверить, что на самом деле это делается именно так.

[ContractClass(typeof(AsyncCacheProviderContract))]
public interface IAsyncCacheProvider {

    Task<bool> GetAsync<T>(string key, out T value);

}

[ContractClassFor(typeof(AsyncCacheProviderContract))]
internal abstract class AsyncCacheProviderContract : IAsyncCacheProvider {

    public Task<bool> GetAsync<T>(string key, out T value)
    {
        Contract.Requires(!String.IsNullOrEmpty(key));

        value = default(T);
        return Task.Factory.StartNew(() => false);
    }

}

Контракт должен гарантировать, что 1) все классы, реализующие интерфейс, требуют, чтобы ключ аргумента не был пустым или пустым, а также 2) автоматически генерировал проверку в сборке, например, аналогично

public Task<bool> GetAsync<T>(string key, out T value) {
    if(String.IsNullOrEmpty(key))
        throw new ArgumentException //...
}

Однако в этом конкретном случае мне кажется странным, что я должен назначить аргумент out а также вернуть фиктивную Task чтобы сделать компилятор счастливым. Нет ли более простого способа, например, используя атрибуты?

  • 1
    Небольшая проблема в вашем коде, не связанная с вашим вопросом: атрибут ContractClassFor должен ссылаться на интерфейс, например [ContractClassFor(typeof(IAsyncCacheProvider))]
Теги:
validation
code-contracts
codeguard

1 ответ

2
Лучший ответ

Мне кажется странным, что я должен назначить аргумент out, а также возвратить фиктивную задачу, чтобы сделать компилятор счастливым.

Вам не нужно, если вы throw исключение вместо return -ing от метода. Тогда вам не нужно создавать возвращаемое значение, и вам не нужно назначать параметр out:

[ContractClassFor(typeof(IAsyncCacheProvider))] // note: there a small change here!
sealed class AsyncCacheProviderContract : IAsyncCacheProvider
{
    public Task<bool> GetAsync<T>(string key, out T value)
    {
        Contract.Requires(!String.IsNullOrEmpty(key));
        throw new NotSupportedException(); // makes the compiler happy, too
    }

    private AsyncCacheProviderContract() { } // optional safeguard:
}                                            // prevent instantiation (see below)

На самом деле это семантически более корректно, чем возврат из метода. Зачем? Потому что никто не должен называть эти методы контрактов. Они никогда не должны были делать какую-либо значимую работу, поэтому им не нужно возвращать какие-либо значимые ценности. Все, что требуется от вашего контракта, заключается в том, что он объявляет контракты; фактическая работа выполняется где-то в другом месте.

Связанный вопрос:
Реализация не-void интерфейса Кодовые контракты - по умолчанию (T) против throw NotImplementedException

Ещё вопросы

Сообщество Overcoder
Наверх
Меню