Реализация непустого интерфейса Code Contracts - по умолчанию (T) против throw NotImplementedException

Это может быть в основном вопросом стиля, но при определении контрактов кода для не пустых членов интерфейса, какой подход лучше:

Интерфейс:

[ContractClass(typeof(IFooContract))]
public interface IFoo
{
    object Bar();
}

Вариант договора 1:

[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
    object IFoo.Bar()
    {
        Contract.Ensures(Contract.Result<object>() != null);
        throw new NotImplementedException();
    }
}

Вариант договора 2:

[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
    object IFoo.Bar()
    {
        Contract.Ensures(Contract.Result<object>() != null);
        return default(T);
    }
}

Большая часть литературы, которую я видел, имеет тенденцию к варианту 2, но я чувствую, что вариант 1 лучше, поскольку более ясно, что это чисто о контракте (и вариант 2 технически нарушает контракт, который он только что определил).

Существуют ли сценарии, в которых вариант 2 предпочтительнее варианта 1?

2 ответа

Решение

Создание исключения семантически более правильно, потому что класс контракта больше не может вызываться и использоваться, по-видимому, разумным способом; абоненты будут остановлены и сообщат о своей ошибке.

Тем не мение, NotImplementedException не похоже на правильное исключение для броска: это исключение обычно отмечает часть кода (например, тело метода), которая еще не реализована. Но не void реализован контрактный метод; это просто не должно называться. Таким образом, я бы предпочел InvalidOperationException или NotSupportedException,

(Вы даже можете выдать пользовательский тип исключения, например NotMeantToBeCalledException.)

Это должен быть полностью субъективный (и академический) аргумент, учитывая, что код в ContractClassFor класс никогда не должен называться, никогда.:-). Как вы намекнули (через non-void), exception или же default Маршрут требуется только для класса для компиляции и не имеет отношения к контрактам, содержащимся в методах.

Очевидно, что абстрактный класс не может быть непосредственно создан, однако вы должны уменьшить видимость ContractClassFor класс для internal чтобы уменьшить изменения того, что он был случайно разделен на подклассы.

Кроме того, Jon Skeet идет дальше и добавляет частный конструктор без параметров, чтобы предотвратить любую возможность создания экземпляра:

private IFooContract() { }

(И чтобы ответить на ваш вопрос косвенно, дополнительно выразив тот факт, что это "специальный" класс помощников по контрактам, работающий вокруг того факта, что контракты не могут быть размещены непосредственно в интерфейсе, читатель вашего кода легко распознает его как такие просто игнорируют default / exception компилятор плацебо)

Другие вопросы по тегам