У меня есть метод, который запускается как это:
public static UnboundTag ResolveTag(Type bindingType, string name, string address)
{
Contract.Requires(bindingType != null);
var tags = GetUnboundTagsRecursively(bindingType).ToArray();
Контракт для реализации GetUnboundTagsRecursively (реализованный в том же классе) похож на это:
public static IEnumerable<UnboundTag> GetUnboundTagsRecursively(Type bindingType)
{
Contract.Requires(bindingType != null);
Contract.Ensures(Contract.Result<IEnumerable<UnboundTag>>() != null);
Статический анализатор указывает на отказ на строке присвоения тегов ResolveTag с сообщением "requires unproven: source != null"
. Я несколько раз просматривал это, и я не могу выяснить, почему это было бы. Я предполагаю, что это - ссылка на source
параметр для дополнительного метода ToArray()
. Мой метод указывает, что гарантирует, что результат не является пустым, таким образом, это, казалось бы, подразумевало бы что источник для ToArray()
является также не пустым. Что я пропускаю?
Дополнительная информация: метод, возвращая IEnumerable реализован с ответными визитами урожая. Я задаюсь вопросом, смешивает ли, возможно, волшебство перечислителя с контрактами кода так или иначе...
Я просто попытался изменить реализацию для возврата пустого массива вместо того, чтобы использовать возврат урожая, и это передает контракты, так по-видимому, это - проблема с методом с помощью возврата урожая. Кто-либо знает путь вокруг этого?
Я смотрел на IL для Контрактов DLL, и он на самом деле помещает вызовы контрактов в MoveNext () для реализации перечислителя:
.method private hidebysig newslot virtual final
instance bool MoveNext() cil managed
{
.override [mscorlib]System.Collections.IEnumerator::MoveNext
// Code size 410 (0x19a)
.maxstack 3
.locals init ([0] bool V_0,
[1] int32 V_1)
.try
{
IL_0000: ldarg.0
IL_0001: ldfld int32 PLCLink.Bind.UnboundTag/'<GetUnboundTagsRecursively>d__c'::'<>1__state'
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: ldc.i4.0
IL_0009: beq.s IL_0024
IL_000b: ldloc.1
IL_000c: ldc.i4.3
IL_000d: sub
IL_000e: switch (
IL_00cd,
IL_018d,
IL_0162)
IL_001f: br IL_018d
IL_0024: ldarg.0
IL_0025: ldc.i4.m1
IL_0026: stfld int32 PLCLink.Bind.UnboundTag/'<GetUnboundTagsRecursively>d__c'::'<>1__state'
IL_002b: ldarg.0
IL_002c: ldfld class PLCLink.Bind.IUnboundTagGroup PLCLink.Bind.UnboundTag/'<GetUnboundTagsRecursively>d__c'::group
IL_0031: ldnull
IL_0032: ceq
IL_0034: ldc.i4.0
IL_0035: ceq
IL_0037: call void [mscorlib]System.Diagnostics.Contracts.Contract::Requires(bool)
IL_003c: call !!0 [mscorlib]System.Diagnostics.Contracts.Contract::Result<class [mscorlib]System.Collections.Generic.IEnumerable`1<valuetype PLCLink.Bind.UnboundTag>>()
Это интересно как Контракт. Вызов результата на самом деле использует неправильный тип (так как MoveNext возвращает bool).
Я подозреваю , что вызовы контрактов заканчиваются в MoveNext ()
сгенерированного типа реализации итератора. Попробуйте следующее:
public static IEnumerable<UnboundTag> GetUnboundTagsRecursively
(Type bindingType)
{
Contract.Requires(bindingType != null);
Contract.Ensures(Contract.Result<IEnumerable<UnboundTag>>() != null);
return GetUnboundTagsRecursivelyImpl(bindingType);
}
private static IEnumerable<UnboundTag> GetUnboundTagsRecursivelyImpl
(Type bindingType)
{
// Iterator block code here
}
Теперь вам может потребоваться немного дополнительной работы, чтобы эти методы компилировались без каких-либо нарушений контракта. Например:
public static IEnumerable<UnboundTag> GetUnboundTagsRecursively
(Type bindingType)
{
Contract.Requires(bindingType != null);
Contract.Ensures(Contract.Result<IEnumerable<UnboundTag>>() != null);
IEnumerable<UnboundTag> ret = GetUnboundTagsRecursivelyImpl(bindingType);
// We know it won't be null, but iterator blocks are a pain.
Contract.Assume(ret != null);
return ret;
}
Это немного неэффективно, так как при этом дважды выполняется проверка на недействительность. Это также эффективно просто подавляет предупреждение через Assume
.
Я не удивлюсь, если узнаю, что команда кодовых контрактов работает над исправлением этого ... Я думаю, что слышал о чем-то похожем, но не могу вспомнить подробностей. Примечания к выпуску для выпуска за сентябрь 2009 г. включают:
Первоначальная поддержка контрактов на итераторах
... но, если вы используете недавний выпуск, предположительно этой начальной поддержки недостаточно: )