代码之家  ›  专栏  ›  技术社区  ›  Jez

二合一检查是否存在并存储-在搜索词典时是否可能?

  •  0
  • Jez  · 技术社区  · 2 年前

    我喜欢在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'
        //...
    }
    

    当发现的对象是引用类型并且可以为null时,此方法有效。然而,我试图用一个 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(...) 返回a KeyValuePair<TKey, TValue> ,这是一个结构体。我可以先打个电话来解决这个问题 任何 srchField ,然后打电话 弗斯特 如果具有相同的谓词 任何 查找任何内容,但这必须进行两次相同的搜索。有什么方法可以在LINQ中只搜索一次词典,就可以在这里进行二合一检查并存储吗?

    0 回复  |  直到 2 年前
        1
  •  1
  •   Jez    2 年前

    感谢@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 ...
    }
    
        2
  •  0
  •   n0rd    2 年前

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

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

    如果你想用钥匙做点什么,它会有点笨重:

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