【问题标题】:How to avoid Linq chaining to return null?如何避免 Linq 链接返回 null?
【发布时间】:2010-06-19 10:09:06
【问题描述】:

我对代码合同和 linq 有疑问。我设法将问题缩小到以下代码示例。现在我被困住了。

public void SomeMethod()
{
    var list = new List<Question>();

    if (list.Take(5) == null) { }
    // resharper hints that condition can never be true

    if (list.ForPerson(12) == null) { }
    // resharper does not hint that condition can never be true
}

public static IQueryable<Question> ForPerson(this IQueryable<Question> source, int personId)
{
    if(source == null) throw new ArgumentNullException();

    return from q in source
           where q.PersonId == personId
           select q;
}

我的 linq 链有什么问题?分析 ForPerson 调用时,为什么 resharper 不“抱怨”?

EDIT:ForPerson 方法的返回类型从字符串更改为 IQueryable,我的意思是。 (我的坏)

【问题讨论】:

    标签: linq resharper code-contracts


    【解决方案1】:

    Reshaper 是正确的,TakeSkip 的结果永远不会为空 - 如果没有项目,则结果是一个没有元素的 IEnumerable&lt;Question&gt;。我认为做你想做的事你应该检查Any

    var query = list.Take(5);
    if (!query.Any())
    {
        // Code here executes only if there were no items in the list.
    }
    

    但是这个警告是如何起作用的呢? Resharper 无法仅通过查看方法定义就知道该方法永远不会返回 null,并且我假设它不会对方法体进行逆向工程以确定它永远不会返回 null。因此,我假设它已经过特殊硬编码,其中包含一条指定 .NET 方法 Skip 和 Take 不返回 null 的规则。

    当您编写自己的自定义方法时,Reflector 可以从接口对您的方法行为做出假设,但您的接口允许它返回 null。因此它不会发出警告。如果它分析了方法体,那么它可以看到 null 是不可能的,并且能够发出警告。但是分析代码以确定其可能的行为是一项非常困难的任务,我怀疑 Red Gate 是否愿意花钱解决这个问题,因为他们可以在其他地方以更低的开发成本添加更多有用的功能。

    确定一个布尔表达式是否可以返回true被称为Boolean satisfiability problem,这是一个NP-hard问题。

    您希望 Resharper 确定 通用方法主体 是否可以返回 null。这是上述 NP-hard 问题的推广。在 100% 的情况下,任何工具都不可能正确地做到这一点。

    【讨论】:

    • 感谢您的迅速答复。返回类型应该是 IQueryable。我在我的问题中改变了这一点。问题是 resharpers 没有抱怨。应该是因为 ForPeson 永远不会返回 null。
    • 实际上,据我了解,ReSharper 对各种方法进行了逆向工程,然后然后 对通过逆向工程确定的值进行硬编码。结果是它有时会得出错误的结论,例如 XmlReader.Create 可以返回 null。有一个非常模糊的边缘情况。
    • @John Saunders:我刚刚查看了 Reflector 中 XmlReader.Create 的代码,我对 ReSharper 遇到问题并不感到惊讶。困难似乎在于它调用的私有方法:XmlReader.AddWrapper。在一种特殊情况下,它可以返回其参数之一作为结果。要知道结果是否可以返回 null,您必须知道该参数是否可以为 null。这是一个非常简单的案例。我敢打赌,您可以使用相互递归的函数创建更多邪恶的示例。
    • 实际上比这更糟。判断一个方法是否返回null,就相当于解决了停机问题。
    【解决方案2】:
    if(source == null) throw new ArgumentNullException(); 
    

    这不是代码契约的方式,你的意思是:

    Contract.Require(source != null);
    

    【讨论】:

    • 不,我不是这个意思。您可以将 'if (list.ForPerson(12) == null)' 替换为 'Contract.Requires(list.ForPerson(12) != null)' 之类的东西,你也会遇到同样的问题。这实际上是我遇到的问题。由于使用 Resharper 的人比使用 Code Contracts 的人多,所以我想在 de Resharper-context 中问这个问题......
    • 我明白了,问题是标记为代码合同而不是标记为 resharper,所以我很困惑。 Anyhoo - 这个场景应该得到代码契约的支持。
    猜你喜欢
    • 2012-04-25
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 2017-01-01
    • 2021-10-18
    相关资源
    最近更新 更多