这段代码可以工作,但效率很低,因为它会重复查找ignored字典。如何在LINQ语句中使用dictionary 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参数。
发布于 2010-07-19 20:06:01
您需要在查询之前声明out变量:
ISet<DateTime> s = null;
var result = from r in records
where !ignored.TryGetValue(r.Key, out s)
|| !s.Contains(r.Value)
select r;但是,如果要等到以后才对查询进行评估,请注意副作用...
发布于 2019-03-26 16:28:51
(我的回答涉及到使用TrySomething( TInput input, out TOutput value )方法的一般情况(如IDictionary.TryGetValue( TKey, out TValue )和Int32.TryParse( String, out Int32 ) ),因此它不能用OP自己的示例代码直接回答OP的问题。我之所以在这里发布这个答案,是因为截至2019年3月,这个QA是谷歌对"linq trygetvalue“的最高搜索结果)。
在使用扩展方法语法时,至少有这两种方法。
1.使用匿名类型、System.Tuple或C#值类型:
首先在Select调用中调用TrySomething方法,并在C# 7.0中将结果存储在值元组中(或者在旧版本的C#中存储匿名类型,请注意,由于值元组的开销较低,因此应优先使用值元组):
使用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();使用C# 3.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 ) ? new { ok = true, value } : new { ok = false, default(Int32) } )
.Where( t => t.ok )
.Select( t => t.value )
.ToList();使用.NET Framework4.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();2.使用扩展方法
我编写了自己的扩展方法:SelectWhere,它将此操作简化为一次调用。它在运行时应该会更快,尽管这并不重要。
它的工作方式是为具有第二个out参数的方法声明自己的delegate类型。out默认情况下不支持这些参数,因为Linq不接受Linq参数。然而,由于委托在C#中的工作方式,您可以将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表达式中声明的变量用于其他元组值。
发布于 2014-01-08 23:03:29
使用外部变量,您不需要担心它超出作用域,因为LINQ表达式是一个闭包,它将使它保持活动状态。但是,为了避免任何冲突,您可以将变量和表达式放在一个函数中:
public IEnumerable GetRecordQuery() {
ISet<DateTime> s = null;
return from r in records
...
}
...
var results = GetRecordQuery();这样,只有查询才能访问s变量,而任何其他查询(从单独的GetRecordQuery调用返回)都将拥有自己的变量实例。
https://stackoverflow.com/questions/3280589
复制相似问题