Има ли някакъв начин договорите за код да работят с LINQ?

Code Contracts продължават да ми дават предупреждения „Възможно извикване на метод при нулева препратка“ за всички мои 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)]

Но и това не проработи. Така че реших, че просто ще добавя изрична нулева проверка в клаузата "къде" на оператора LINQ:

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

Това успешно премахва предупреждението за клаузата "къде", но не елиминира предупреждението за клаузата "избор". Всъщност единственият начин, който открих да се отърва от двете предупреждения, е да добавя нулеви проверки към всяка клауза в оператора 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 е нула.

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 клас, който може да работи по-добре с неговата логика от LINQ разширението All метод.

person porges    schedule 05.11.2009