有没有办法让代码契约与LINQ一起使用?

时间:2009-09-24 19:40:53

标签: c# .net linq code-contracts

代码契约一直给我“可能在空引用上调用一个方法”警告所有我的LINQ语句,我找不到让它们静音的方法。例如,以下方法生成两个这样的警告,因为我正在访问“car”对象的“Make”和“Model”属性,而不首先检查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语句的“where”子句中添加一个显式的空检查:

    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属性

2 个答案:

答案 0 :(得分:5)

可能会抱怨无效检查汽车。试试这个:

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为空,您将获得异常。

答案 1 :(得分:2)

您是否尝试过最新版本的代码合约?有一个在10月份发布,我无法用它重现。

或者,代码约定在静态ForAll类上定义了自己的Contracts方法,它的逻辑可能比LINQ扩展All方法更好。