Здравствуйте, Аноним, Вы писали:
А>Это особенности языка. например из такого интерфейса
А>А>public interface Locker {
А> void Lock();
А> void Unlock();
А>}
А>
А>совершенно не очевидно можно ли lock-ать вложено или нет.
А>в таких случаях можно использовать комментарии
А>Если мало комментарием то кидай исключения и используй тесты.
В принципе, в тех же дот-нетах, контракты можно определить и более формально:
[ContractClass(typeof(LockerContracts))] // указываем контракт
public interface Locker {
// Без дополнительного публичного свойства никак,
// вызывающий код должен понимать, какие контракты налагаются каждым
// методом и должен иметь возможность проверить выполнимость предусловия
bool Locked {get;}
void Lock();
void Unlock();
}
// В .net-е контракты указываются не с помощью атрибутов (они слишком слабовыразительны для этого)
// а с помощью отдельных классов, реализующих требуемый интерфейс
internal abstract class LockerContracts : Locker
{
public bool Locked {get {throw new NotImplementedException;}}
public void Lock() {
// Все, теперь клиенты будут знать, что дважды локать нельзя
// в противном случае - это баг со всеми вытекающими (ассертом или
// исключением в рантайме)
Contract.Requires(!Locked, "We can't acquire lock twice");
}
public void Unlock() {
// Здесь картина аналогична, попытка заанлокать незалоченное приведет к беде
Contract.Requires(Locked, "We can't release free lock!");
}
}
Теперь можно на это дело натравить статический анализатор, который еще и во время компиляции (точнее посткомпиляции) сможет отловить проблемы.