Почему это не Кодирует отношения Контрактов, доказывают?

У меня есть метод, который запускается как это:

    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).

1
задан Dan Bryant 23 June 2010 в 17:39
поделиться

1 ответ

Я подозреваю , что вызовы контрактов заканчиваются в 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 г. включают:

Первоначальная поддержка контрактов на итераторах

... но, если вы используете недавний выпуск, предположительно этой начальной поддержки недостаточно: )

2
ответ дан 2 September 2019 в 23:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: