Есть ли способ заставить кодовые контракты работать с LINQ?

Контракты кода продолжают выдавать мне предупреждения «Возможно, вызов метода по нулевой ссылке» для всех моих операторов LINQ, и я не могу найти способ заставить их замолчать. Например, следующий метод генерирует два таких предупреждения, потому что я обращаюсь к свойствам «Make» и «Model» объекта «car» без предварительной проверки на null.

    public IEnumerable<string> GetCarModelsByMake(string make)
    {
        return from car in Cars
               where car.Make == make
               select car.Model;
    }

В моем конкретном случае я знаю, что коллекция Cars никогда не будет содержать никаких нулевых записей, поэтому я решил, что могу просто добавить Assume к методу, чтобы отключить статическую проверку, например:

    public IEnumerable<string> GetCarModelsByMake(string make)
    {
        Contract.Assume(Cars.All(car => car != null));

        return from car in Cars
               where car.Make == make
               select car.Model;
    }

Но это не работает, по-видимому, потому, что ожидать, что статическая проверка поймет, слишком много. Итак, я решил просто подавить предупреждения, используя следующий атрибут SuppressMessage:

    [SuppressMessage("Microsoft.Contracts", "NonNull")]

Но по какой-то причине это не может подавить предупреждения. Я даже попробовал следующие атрибуты SuppressMessage, ни один из которых не сработал:

    [SuppressMessage("Microsoft.Contracts", "Requires")]
    [SuppressMessage("Microsoft.Contracts", "Ensures")]
    [SuppressMessage("Microsoft.Contracts", "Invariant")]

Я даже попытался полностью отключить проверку контракта для метода с помощью атрибута ContractVerification:

    [ContractVerification(false)]

Но и это не сработало. Итак, я решил, что просто добавлю явную нулевую проверку в предложение «where» оператора LINQ:

    public IEnumerable<string> GetCarModelsByMake(string make)
    {
        return from car in Cars
               where car != null && car.Make == make
               select car.Model;
    }

Это успешно избавляет от предупреждения о предложении «where», но не устраняет предупреждение о предложении «select». Фактически, единственный способ избавиться от обоих предупреждений, который я нашел, - это добавить нулевые проверки к каждому предложению в операторе LINQ, например:

    public IEnumerable<string> GetCarModelsByMake(string make)
    {
        return from car in Cars
               where car != null && car.Make == make
               select car == null ? null : car.Model;
    }

Ясно, что это не очень чистый или эффективный код, и я на самом деле не собираюсь добавлять такие избыточные нулевые проверки ко всем моим операторам LINQ - особенно когда я знаю, что перечисление не содержит никаких нулевых записей. Лучшим решением этой проблемы было бы, чтобы средство статической проверки понимало операторы Contract.Assume, которые гарантируют ненулевые значения для каждого элемента в коллекции, но если это невозможно сделать, то, по крайней мере, соблюдайте атрибут SuppressMessage в методе .


person James Messinger    schedule 24.09.2009    source источник


Ответы (2)


Он может жаловаться на нулевую проверку автомобилей. Попробуй это:

public IEnumerable GetCarModelsByMake(string make)
{
    if (null == Cars)
        return new string[0];  // or null if you like

    return from car in Cars
        where car.Make == make
        select car.Model;
}

Помните, что этот оператор LINQ фактически такой же, как:

return Cars.Where(car => car.Make == make).Select(car => car.Model);

Вы получите исключение, если Cars имеет значение null.

person Travis Heseman    schedule 24.09.2009
comment
+1, но я думаю, что добавление Cars != null к инвариантам класса будет ближе к духу исходного кода, который, очевидно, не ожидает, что Cars будет null. msdn.microsoft.com/en-us/magazine/ee236408.aspx# id0070066 - person Wim Coenen; 26.09.2009
comment
Абсолютно; инвариант для этого класса лучше подошел бы для характера этого вопроса с контрактом кода. - person Travis Heseman; 26.09.2009
comment
Я должен был уточнить, что у этого класса уже есть инвариант, гарантирующий Cars! = Null. Однако предупреждения контракта не об этом. Предупреждения касаются нулевой переменной car, а не коллекции Cars. - person James Messinger; 26.09.2009
comment
Не могли бы вы добавить еще один инвариант, например! Cars.Contains (null)? - person Travis Heseman; 26.09.2009

Вы пробовали последнюю версию Code Contracts? Один был выпущен в октябре, и я не могу воспроизвести его с ним.

В качестве альтернативы Code Contracts имеет свой собственный ForAll метод, определенный в статическом Contracts классе, который может работать лучше с его логикой, чем метод All расширения LINQ.

person porges    schedule 05.11.2009