linq 2合1检查是否存在并存储-在搜索词典时是否可能?

kg7wmglp  于 2022-12-06  发布在  其他
关注(0)|答案(2)|浏览(106)

我喜欢在C#中使用的一种常见模式是,我不需要先用Any检查集合中是否存在某个东西,然后再用First查找它,而是简单地调用FirstOrDefault,它告诉我它是否存在,如果存在,则给我一个对它的引用:

List<Person> people = ...;
Person found;
if ((found = people.FirstOrDefault(x => x.Age > 10)) != null) {
    // Found person with age over 10, and we have them in 'found'
    //...
}

当被找到的对象是一个引用类型时,这是有效的,并且可以为空。然而,我试图对Dictionary的条目做同样的事情:

Dictionary<(int X, int Y), ITileData<(int X, int Y)>> srchField = new();
KeyValuePair<(int X, int Y), ITileData<(int X, int Y)>> next;
while ((next = srchField.FirstOrDefault(x => !x.Value.Reached)) != null) {
    // Found next, and we have it in 'next'
    //...
}

但是,这并不起作用,因为srchField.FirstOrDefault(...)返回一个KeyValuePair<TKey, TValue>,它是一个结构体。我可以通过首先在srchField上调用Any来解决这个问题,如果Any找到了任何东西,则使用相同的 predicate 调用First,但这必须进行两次相同的搜索。有没有什么方法可以进行2合1的存在性检查并存储在这里,在LINQ?中只执行一次词典搜索

nxagd54h

nxagd54h1#

感谢@TheodorZoulias为我指出了正确的方向,并受到@Samuel的答案和@JonSkeet的代码解决方案的启发(我实际上没有意识到,如果LINQ .Take(1)只找到0个元素,它实际上会给您一个0的枚举值,而不是引发异常),我想出了一个很好地完成此任务的扩展方法:

public static class IEnumerableExtensions {
    public static bool TryGetFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource first) {
        if (predicate == null) {
            throw new ArgumentNullException(nameof(predicate));
        }

        var query = source.Where(predicate).Take(1).ToList();
        if (query.Count == 0) {
            first = default;
            return false;
        }
        first = query[0];
        return true;
    }
}

示例用法:

while (srchField.TryGetFirst(x => !x.Value.Reached, out KeyValuePair<(int X, int Y), ITileData<(int X, int Y)>> next)) {
    var key = next.Key;
    var val = next.Value;
    // ... use eg. key.X, key.Y, and val ...
}
nqwrtyyt

nqwrtyyt2#

如果你想在搜索中忽略键,并且你的值是引用类型,你可以只使用字典的Values属性:

srchField.Values.FirstOrDefault(!v => v.Reached)

如果你想用钥匙做点什么,那就有点笨拙了:

srchField.Where(kvp => Predicate(kvp.Key)).Select(kvp => kvp.Value).FirstOrDefault()

相关问题