У меня следующий сценарий:
public interface ISomething
{
void DoStuff();
//...
}
public class Something : ISomething
{
private readonly ISomethingElse _somethingElse;
//...
public Something (ISomethingElse somethingElse)
{
Contract.Requires(somethingElse != null);
_somethingElse = somethingElse;
}
public void DoStuff()
{
// *1* Please look at explanation / question below
_somethingElse.DoThings();
}
}
В строке 1 и при включенной статической проверке я получаю предупреждение о том, что _somethingElse
возможно имеет значение null, и если я добавлю контракт, это приведет к ошибке
[Type] реализует метод интерфейса {Interface. Таким образом, метод} не может добавить требует
Что лучше всего здесь сделать? Варианты, которые я вижу, включают
Контракт. Примите
Пожалуйста обратите внимание, что это поле только для чтения
, поэтому после установки значения в конструкторе его невозможно изменить. Таким образом, предупреждение из контрактов кода кажется немного несущественным.
Раздел 3: Наследование контракта руководства пользователя утверждает, что все предварительные условия должны быть определены в корневом методе цепочки наследования / реализации:
Если клиент удостоверяется, что он выполнил предварительное условие и имеет переменную
o
, статический тип которой равенT
, то клиент не должен получать нарушение предусловия при вызовеOM
. Это должно быть верно, даже если значение времени выполненияo
имеет типU
. Следовательно, методU.M
не может добавить предусловие, которое сильнее, чем предусловиеT.M
.Хотя мы могли допустить более слабое предварительное условие, мы обнаружили, что сложности, связанные с этим, перевешивают преимущества. Мы просто не видели убедительных примеров, когда ослабление предусловия было бы полезным.Таким образом, мы вообще не разрешаем добавлять какие-либо предварительные условия в подтип.
Как следствие, предварительные условия метода должны быть объявлены в корневом методе цепочки наследования / реализации, то есть в первом объявлении виртуального или абстрактного метода или в самом методе интерфейса.
В вашей ситуации лучший способ действий - установить инвариант, утверждающий, что поле _somethingElse
никогда не будет нулевым:
[ContractInvariantMethod]
private void ObjectInvariant() {
Contract.Invariant(_somethingElse != null);
}
Это, конечно, всегда верно, поскольку поле помечен только для чтения
и инициализируется в конструкторе. Статическая проверка не может сделать этого сама по себе, поэтому вы должны явно указать это через этот инвариант.
При желании вы можете добавить постусловие Contract.Ensures (_somethingElse! = Null);
в свой конструктор, но статическая проверка этого не требует.