简体   繁体   English

有没有办法在 c# 中使用 if-else 语句来简化 LINQ 语句

[英]Is there way to simplify a LINQ statement using if-else statement in c#

I have a LINQ expression that works but I wanted to make it simpler and cleaner.我有一个有效的 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);

I looked all over the internet for some way to include an if statement inside the LINQ expression where I could stop if some condition is met and return an object... or continue to execute another query if the condition is not met.我在互联网上寻找某种方式在 LINQ 表达式中包含 if 语句,如果满足某些条件,我可以停止并返回一个对象……或者如果不满足条件,则继续执行另一个查询。

Maybe this is obvious but I'm really clueless.也许这很明显,但我真的一无所知。

It seems to me that this should work for you:在我看来,这应该适合你:

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);

The key to it working is just giving the from terminator expression a single element array to return the empty token if tryCatchTerminator is false .如果tryCatchTerminatorfalse ,它工作的关键就是给from terminator表达式一个单元素数组以返回空标记。

I ended up creating a passthrough parser.. that doesn't consume tokens and returns an empty token.我最终创建了一个直通解析器..它不消耗令牌并返回一个空令牌。

    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);
        };
    }

It's hard to tell if this will work based on your code sample, but I don't see why you couldn't check for the condition inside the LINQ query:很难根据您的代码示例判断这是否有效,但我不明白为什么您无法检查 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

If I understand your question properly, then no, there's no (built-in) way to "stop" a query once it is started.如果我正确理解您的问题,那么不,一旦查询开始,就没有(内置)方法可以“停止”查询。 If you wanted to add what amounts to a cancellation predicate during enumeration, which signals whether the enumeration should continue, the easiest way to do this would be by creating a custom iterator.如果您想在枚举期间添加等于取消谓词的内容,这表明枚举是否应该继续,最简单的方法是创建自定义迭代器。 Such an implementation might look like this:这样的实现可能如下所示:

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();
    }
}

Of course, you'd want to make this part of your query, so you'd probably want an extension methods class to convert a query to this custom enumerable:当然,您希望将这部分作为查询的一部分,因此您可能需要一个扩展方法类来将查询转换这个自定义可枚举:

public static class BreakingEnumerableExtensions {
    public static BreakingEnumerable<T> WithTerminationClause<T>(
        this IEnumerable<T> query,
        Predicate<T> breakCondition)
    {
        return new BreakingEnumerable<T>(query, breakCondition);
    }
}

And here's the actual usage:这是实际用法:

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}");
}

Which produces the result:结果如下:

Enumerable with termination clause array length: 0
Enumerable with termination clause length: 9

This can be chained to produce some minor optimizations:这可以链接以产生一些小的优化:

// 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));

The only "hitch" is you would never want to use this unless you could guarantee some form of ordering: all numbers appear in an ordered sequence, for example.唯一的“障碍”是您永远不会想要使用它,除非您可以保证某种形式的排序:例如,所有数字都以有序的顺序出现。 If you didn't enforce this guarantee, then you could possibly produce incorrect results from your program.如果您没有强制执行此保证,则您的程序可能会产生不正确的结果。

Hope this helps!希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM