代码之家  ›  专栏  ›  技术社区  ›  Joe Daley

在LINQ中使用TryGetValue()?

  •  2
  • Joe Daley  · 技术社区  · 14 年前

    这段代码可以工作,但效率很低,因为它会双重查找 ignored 字典。我怎么用字典 TryGetValue() 方法来提高效率?

    IDictionary<int, DateTime> records = ...
    
    IDictionary<int, ISet<DateTime>> ignored = ...
    
    var result = from r in records
                 where !ignored.ContainsKey(r.Key) ||
                 !ignored[r.Key].Contains(r.Value)
                 select r;
    

    问题是我不知道如何在LINQ语句中声明一个变量来用于out参数。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Thomas Levesque    14 年前

    out

    ISet<DateTime> s = null;
    var result = from r in records
                 where !ignored.TryGetValue(r.Key, out s)
                    || !s.Contains(r.Value)
                 select r;
    

        2
  •  9
  •   Dai    5 年前

    (我的回答涉及使用 TrySomething( TInput input, out TOutput value ) 方法(如 IDictionary.TryGetValue( TKey, out TValue ) Int32.TryParse( String, out Int32 ) 因此它不会直接用OP自己的exmaple代码回答OP的问题。我在这里发布这个答案是因为这个QA是目前Google对“linqtrygetvalue”的最高搜索结果(截至2019年3月)。

    1使用C值元组, System.Tuple

    打电话给 TrySomething 方法第一 Select

    使用C#7.0值元组(推荐):

    // Task: Find and parse only the integers in this input:
    IEnumerable<String> input = new[] { "a", "123", "b", "456", ... };
    
    List<Int32> integersInInput = input
        .Select( text => Int32.TryParse( text, out Int32 value ) ? ( ok: true, value ) : ( ok: false, default(Int32) ) )
        .Where( t => t.ok )
        .Select( t => t.value )
        .ToList();
    

    这实际上可以通过利用另一个巧妙的技巧来简化 value .Select lambda,因此三元表达式变得不必要,如下所示:

    // Task: Find and parse only the integers in this input:
    IEnumerable<String> input = new[] { "a", "123", "b", "456", ... };
    
    List<Int32> integersInInput = input
        .Select( text => ( ok: Int32.TryParse( text, out Int32 value ), value ) ) // much simpler!
        .Where( t => t.ok )
        .Select( t => t.value )
        .ToList();
    

    // Task: Find and parse only the integers in this input:
    IEnumerable<String> input = new[] { "a", "123", "b", "456", ... };
    
    List<Int32> integersInInput = input
        .Select( text => Int32.TryParse( text, out Int32 value ) ? new { ok = true, value } : new { ok = false, default(Int32) } )
        .Where( t => t.ok )
        .Select( t => t.value )
        .ToList();
    

    使用.NET Framework 4.0 Tuple<T1,T2> :

    // Task: Find and parse only the integers in this input:
    IEnumerable<String> input = new[] { "a", "123", "b", "456", ... };
    
    List<Int32> integersInInput = input
        .Select( text => Int32.TryParse( text, out Int32 value ) ? Tuple.Create( true, value ) : Tuple.Create( false, default(Int32) ) )
        .Where( t => t.Item1 )
        .Select( t => t.Item2 )
        .ToList();
    

    我写了自己的扩展方法: SelectWhere

    它通过声明自己的 delegate 为具有第二个 out 参数。Linq默认不支持这些,因为 System.Func 外面的 TryFunc 具有 任何 方法,包括 Int32.TryParse Double.TryParse , Dictionary.TryGetValue

    支持其他 Try... 具有更多参数的方法,只需定义一个新的委托类型,并为调用方提供指定更多值的方法。

    public delegate Boolean TryFunc<T,TOut>( T input, out TOut value );
    
    public static IEnumerable<TOut> SelectWhere<T,TOut>( this IEnumerable<T> source, TryFunc<T,TOut> tryFunc )
    {
        foreach( T item in source )
        {
            if( tryFunc( item, out TOut value ) )
            {
                yield return value;
            }
        }
    }
    

    用法:

    // Task: Find and parse only the integers in this input:
    IEnumerable<String> input = new[] { "a", "123", "b", "456", ... };
    
    List<Int32> integersInInput = input
        .SelectWhere( Int32.TryParse ) // The parse method is passed by-name instead of in a lambda
        .ToList();
    

    如果仍要使用lambda,另一种定义使用值元组作为返回类型(需要C#7.0或更高版本):

    public static IEnumerable<TOut> SelectWhere<T,TOut>( this IEnumerable<T> source, Func<T,(Boolean,TOut)> func )
    {
        foreach( T item in source )
        {
            (Boolean ok, TOut output) = func( item );
    
            if( ok ) yield return output;
        }
    }
    

    // Task: Find and parse only the integers in this input:
    IEnumerable<String> input = new[] { "a", "123", "b", "456", ... };
    
    List<Int32> integersInInput = input
        .SelectWhere( text => ( Int32.TryParse( text, out Int32 value ), value ) )
        .ToList();
    

    这是因为C#7.0允许在 out Type name

        3
  •  1
  •   nmclean    11 年前

    使用外部变量,您不必担心它超出范围,因为LINQ表达式是一个 关闭

    public IEnumerable GetRecordQuery() {
        ISet<DateTime> s = null;
        return from r in records
               ... 
    }
    
    ...
    
    var results = GetRecordQuery();
    

    s 变量,以及任何其他查询(从对 GetRecordQuery )每个都有自己的变量实例。