Есть ли способ упростить оператор LINQ с помощью оператора if-else в С #
У меня есть выражение LINQ, которое работает, но я хотел сделать его проще и чище.
var tryCatchTerminator = true;
return tryCatchTerminator
? from varKeyword in MatchToken(SyntaxKind.VarKeyword)
from declarations in ParseVarDeclarationClause.AtLeastOnceDelimitedBy(MatchToken(SyntaxKind.Comma))
from terminator in MatchToken(SyntaxKind.SemiColon).OptionalOrDefault()
select (StatementSyntax) new VarDeclarationStatement(varKeyword, declarations, terminator)
: from varKeyword in MatchToken(SyntaxKind.VarKeyword)
from declarations in ParseVarDeclarationClause.AtLeastOnceDelimitedBy(MatchToken(SyntaxKind.Comma))
select (StatementSyntax) new VarDeclarationStatement(varKeyword, declarations, Token<SyntaxKind>.Empty);
Я искал в Интернете какой-то способ включить оператор if в выражение LINQ, где я мог бы остановиться, если какое-то условие выполнено, и вернуть объект... или продолжить выполнение другого запроса, если условие не выполнено.
Может быть, это очевидно, но я действительно невежественен.
4 ответа
Мне кажется, это должно сработать для вас:
return
from varKeyword in MatchToken(SyntaxKind.VarKeyword)
from declarations in ParseVarDeclarationClause.AtLeastOnceDelimitedBy(MatchToken(SyntaxKind.Comma))
from terminator in tryCatchTerminator ? MatchToken(SyntaxKind.SemiColon).OptionalOrDefault() : new[] { Token<SyntaxKind>.Empty }
select (StatementSyntax)new VarDeclarationStatement(varKeyword, declarations, terminator);
Ключ к его работе - просто дать from terminator
выражение одноэлементный массив, чтобы вернуть пустой токен, если tryCatchTerminator
является false
.
Я закончил тем, что создал сквозной парсер... который не потребляет токены и возвращает пустой токен.
private static TokenListParser<SyntaxKind, StatementSyntax> ParseExpressionStatement(
bool lookForTerminator)
{
return from expression in ParsePrefixExpression.Or(ParseCallExpression())
from terminator in lookForTerminator
? MatchToken(SyntaxKind.SemiColon).OptionalOrDefault()
: PassThrough<SynaxKind>()
select (StatementSyntax) new ExpressionStatementSyntax(expression, terminator);
}
private static TokenListParser<T, Token<T>> PassThrough<T>(Token<T> empty)
{
return input =>
{
var output = input.ConsumeToken();
return TokenListParserResult.Value(Token<T>.Empty, output.Location, output.Location);
};
}
Трудно сказать, будет ли это работать на основе вашего образца кода, но я не понимаю, почему вы не смогли проверить условие внутри запроса LINQ:
return from varKeyword in MatchToken(SyntaxKind.VarKeyword)
from declarations in ParseVarDeclarationClause.AtLeastOnceDelimitedBy(MatchToken(SyntaxKind.Comma))
from terminator in MatchToken(SyntaxKind.SemiColon).DefaultIfEmpty()
select (StatementSyntax)new VarDeclarationStatement(varKeyword, declarations, tryCatchTerminator ? terminator : Token<SyntaxKind>.Empty); // check here and pass correct value to VarDeclarationStatement
Если я правильно понимаю ваш вопрос, то нет, нет (встроенного) способа "остановить" запрос после его запуска. Если вы хотите добавить то, что составляет предикат отмены во время перечисления, который сигнализирует, следует ли продолжать перечисление, самый простой способ сделать это - создать собственный итератор. Такая реализация может выглядеть так:
public sealed class BreakingEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> _query;
private readonly Predicate<T> _continuePredicate;
public BreakingEnumerable(IEnumerable<T> query, Predicate<T> predicate)
{
_query = query ?? throw new ArgumentNullException(nameof(query));
_continuePredicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in _query)
{
if (_continuePredicate(item))
{
yield return item;
}
else
{
yield break;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Конечно, вы захотите сделать эту часть своего запроса, поэтому вы, вероятно, захотите, чтобы класс методов расширения преобразовывал запрос в это настраиваемое перечисляемое:
public static class BreakingEnumerableExtensions {
public static BreakingEnumerable<T> WithTerminationClause<T>(
this IEnumerable<T> query,
Predicate<T> breakCondition)
{
return new BreakingEnumerable<T>(query, breakCondition);
}
}
А вот фактическое использование:
static void Main(string[] args)
{
var enumerable = Enumerable.Range(1, 100);
var array = enumerable.WithTerminationClause(i => i > 100).ToArray();
Console.WriteLine($"Enumerable with termination clause array length: {array.Length}");
array = enumerable.Where(i => i < 20).WithTerminationClause(i => i % 2 == 0)
.ToArray();
Console.WriteLine($"Enumerable with termination clause length: {array.Length}");
}
Что дает результат:
Enumerable with termination clause array length: 0
Enumerable with termination clause length: 9
Это можно связать, чтобы произвести небольшую оптимизацию:
// Outputs: `Query results: [100, 200, 300]`
var enumerable = Enumerable.Range(1, 100);
var sub = enumerable.WithTerminationClause(i => i <= 3)
.Select(i => i * 100);
Console.WriteLine("Query results: [{0}]", string.Join(", ", sub));
Единственная "загвоздка" в том, что вы никогда не захотите использовать это, если вы не можете гарантировать какую-либо форму упорядочивания: например, все числа появляются в упорядоченной последовательности. Если вы не обеспечили соблюдение этой гарантии, то ваша программа может дать неверные результаты.
Надеюсь это поможет!