(Мой ответ касается общего случая использования TrySomething( TInput input, out TOutput value )
методов (таких как IDictionary.TryGetValue( TKey, out TValue )
и Int32.TryParse( String, out Int32 )
), и поэтому он не дает прямого ответа на вопрос ОП с помощью собственного кода ОП. Я публикую этот ответ здесь, потому чтоэтот QA в настоящее время является лучшим результатом Google для "linq trygetvalue" по состоянию на март 2019 г.).
При использовании синтаксиса метода расширения существуют как минимум эти два подхода.
1. Использование значения C #-tuples, System.Tuple
или anonymous-types:
Сначала вызовите метод TrySomething
в вызове Select
и сохраните результат в кортеже значений в 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
, поэтому тернарное выражение становится ненужным, например:
// 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 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();
2.Используйте метод расширения
Я написал свой собственный метод расширения: SelectWhere
, который сводит это к одному вызову.Он должен быть быстрее во время выполнения, хотя это не должно иметь значения.
Он работает, объявляя свой собственный тип delegate
для методов, имеющих второй параметр out
.Linq не поддерживает их по умолчанию, потому что System.Func
не принимает out
параметров.Однако из-за того, как делегаты работают в 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();
Если вы все еще хотите использовать лямбду, альтернативное определение использует кортеж значения в качестве типа возвращаемого значения (требуется 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
, использоваться в других значениях кортежа.