Я, наконец, играю в catchup со всем новым, добавленным в .NET 3.5/4.0 Framework. Последние несколько дней я работал с CodeContracts, и я действительно очень стараюсь их любить. Мне любопытно, что другие люди думают о реализации CodeContracts в С#? В частности, как люди организуют такие вещи, как классы контрактов для интерфейсов, методы контрактов для контрактных инвариантов и т.д.?
Мне нравится подтверждение, которое предоставляют контракты, на первый взгляд они выглядят великолепно. С помощью нескольких простых строк я могу получить хорошую проверку сборки, прежде чем я даже запустил свой код. К сожалению, мне сложно преодолевать чувство, что способ, которым заключаются контракты на коде, реализуется на С#, они забивают мой код больше, чем документируют контракты. И чтобы в полной мере воспользоваться контрактами, я замалчиваю свой код предположениями и утверждениями и т.д. (Что я знаю, что некоторые скажут, что это хорошо); но, как показывают некоторые из моих примеров ниже, он превращает одну простую строку в 4 или 5 строк и на самом деле не добавляет достаточного значения по сравнению с альтернативными подходами (т.е. утверждает, исключения и т.д.).
В настоящее время мои самые большие разочарования:
Контракт интерфейса:
[ContractClass(typeof(IInterfaceContract))]
public interface IInterface
{
Object Method(Object arg);
}
[ContractClassFor(typeof(IInterface))]
internal abstract class IInterfaceContract
{
private IInterfaceContract() { }
Object IInterface.Method(Object arg)
{
Contract.Requires(arg != null);
Contract.Ensures(Contract.Result<Object>() != null);
return default(Object);
}
}
Мне кажется, что это такая татуировка, я бы хотел, чтобы был более чистый способ документировать требования, либо через атрибуты, либо в виде встроенной поддержки языка. Тот факт, что я должен реализовать абстрактный класс, который реализует мой интерфейс, так что я могу указать контракт, кажется, в лучшем случае утомительно.
Код Bloat:
typeof(Action<>).MakeGenericType(typeof(Object);
Требуется несколько предположений только для проверки информации, которая легко доступна. Я понимаю, что все, что знает анализатор, это то, что он работает на Типе и поэтому должен работать над этим ограниченным знанием, но мне все же кажется, что одна строка кода требует, чтобы я перезаписывал как
var genericAction = typeof(Action<>);
Contract.Assume(genericAction.IsGenericType);
Contract.Assume(genericAction.GetGenericArguments().Length == 1);
genericAction.MakeGenericType(typeof(Object));
Просто для того, чтобы все было задокументировано (да, я знаю, что могу использовать ContractVerificationAttribute, чтобы отключить это для метода/класса и т.д., или SuppressMessageAttribbute для целевых сообщений, но это, похоже, превзойдет цель, так как ваш код будет быстро засорен супрессиями и т.д.
Кроме того, взяв такой случай, как
public class MyClass
: IInterface
{
private readonly Object _obj;
public Object Property
{
get
{
Contract.Ensures(Contract.Result<Object>() != null);
return _obj;
}
}
public MyClass(Object obj)
{
Contract.Requires(obj != null);
_obj = obj;
}
}
obj требуется, чтобы он был не равным null и был установлен в поле только для чтения, которое не может быть изменено, но мне все еще требуется добавить метод "разметки" в мой класс, чтобы мое требование к свойствам не возвращать значение null могло быть доказано
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(_obj != null);
}
Есть больше, но я подумал, что я, вероятно, достаточно расхвалил, и я был бы очень благодарен за понимание людей гораздо умнее, чем я, чтобы помочь мне "подойти" к "Кодовым контрактам" и убрать это чувство от клонирования кода. Было бы очень полезно узнать о том, как лучше структурировать код, обходные проблемы и т.д.
Спасибо!