Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Roslyn:如何确定表达式中的运算符优先级是否等于或低于某个运算符?_C#_.net_Roslyn - Fatal编程技术网

C# Roslyn:如何确定表达式中的运算符优先级是否等于或低于某个运算符?

C# Roslyn:如何确定表达式中的运算符优先级是否等于或低于某个运算符?,c#,.net,roslyn,C#,.net,Roslyn,我正在编写一个Roslyn分析器,用&&和|运算符替换三元运算符中布尔文本的用法。以下是代码修复提供程序应执行的操作: expr1 ? true : expr2 -> expr1 || expr2 expr1 ? false : expr2 -> !expr1 && expr2 expr1 ? expr2 : true -> !expr1 || expr2 expr1 ? expr2 : false -> expr1 && expr2 以

我正在编写一个Roslyn分析器,用
&&
|
运算符替换三元运算符中布尔文本的用法。以下是代码修复提供程序应执行的操作:

expr1 ? true : expr2 -> expr1 || expr2
expr1 ? false : expr2 -> !expr1 && expr2
expr1 ? expr2 : true -> !expr1 || expr2
expr1 ? expr2 : false -> expr1 && expr2
以下是我的代码的相关部分:

// In MA0002Analyzer.cs
internal static bool? ValueOfBoolLiteral(ExpressionSyntax expr)
{
    switch (expr.Kind())
    {
        case SyntaxKind.TrueLiteralExpression:
            return true;
        case SyntaxKind.FalseLiteralExpression:
            return false;
    }

    if (expr is ParenthesizedExpressionSyntax parenExpr)
    {
        return ValueOfBoolLiteral(parenExpr.Expression);
    }

    return null;
}

// In MA0002CodeFixProvider.cs
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

private static Task<Document> UseLogicalOperatorAsync(Document document, ConditionalExpressionSyntax ternary, CancellationToken ct)
{
    // expr1 ? true : expr2 -> expr1 || expr2
    // expr1 ? false : expr2 -> !expr1 && expr2
    // expr1 ? expr2 : true -> !expr1 || expr2
    // expr1 ? expr2 : false -> expr1 && expr2

    async Task<Document> UseBinaryOperatorAsync(SyntaxKind operatorKind, ExpressionSyntax left, ExpressionSyntax right)
    {
        var logicalExpr = BinaryExpression(operatorKind, left, right);
        var syntaxRoot = await document.GetSyntaxRootAsync(ct);
        return document.WithSyntaxRoot(syntaxRoot.ReplaceNode(ternary, logicalExpr));
    }

    bool? literalValue = MA0002Analyzer.ValueOfBoolLiteral(ternary.WhenTrue);
    if (literalValue != null)
    {
        // ? has lower precedence than ||, so it's possible we could run into a situation where stuff in expr1 binds more
        // tightly to stuff in expr2 than before. For example, b1 ? true : b2 && b3 equals b1 || (b2 && b3), but not b1 || b2 && b3.
        // We want to prevent this by wrapping expr2 in parentheses when necessary, but only when expr2 contains operators that
        // have equal/less precendence than ||.
        ExpressionSyntax right = MaybeParenthesize(ternary.WhenFalse);

        return literalValue == true ?
            // It's never necessary to parenthesize expr1 here because boolean operators are left-associative.
            UseBinaryOperatorAsync(SyntaxKind.LogicalOrExpression, ternary.Condition, right) :
            // However, it might be necessary to parenthesize expr1 here because it is being negated.
            UseBinaryOperatorAsync(SyntaxKind.LogicalAndExpression, Negate(ternary.Condition), right);
    }

    literalValue = MA0002Analyzer.ValueOfBoolLiteral(ternary.WhenFalse);
    if (literalValue != null)
    {
        // In "b1 ? b2 : true;", calling ToFullString() on b2's node will give "b2 ". This is bc the space to the right of b2 is counted as part
        // of its trivia. Therefore, we must remove trailing trivia before moving b2 to the end of a new expression, or we'll get "!b1 || b2 ;".
        // This is not an issue for the node on the false branch of the conditional.
        ExpressionSyntax right = MaybeParenthesize(ternary.WhenTrue.WithoutTrailingTrivia());

        return literalValue == true ?
            UseBinaryOperatorAsync(SyntaxKind.LogicalOrExpression, Negate(ternary.Condition), right) :
            UseBinaryOperatorAsync(SyntaxKind.LogicalAndExpression, ternary.Condition, right);
    }

    return Task.FromResult(document);
}

private static ExpressionSyntax MaybeParenthesize(ExpressionSyntax expr)
{
    // What goes here?
}

private static ExpressionSyntax Negate(ExpressionSyntax expr)
{
    if (expr.IsKind(SyntaxKind.LogicalNotExpression))
    {
        var pue = (PrefixUnaryExpressionSyntax)expr;
        return pue.Operand;
    }

    if (expr is ParenthesizedExpressionSyntax parenExpr)
    {
        return parenExpr.WithExpression(Negate(parenExpr.Expression));
    }

    return PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, expr);
}
//在MA0002Analyzer.cs中
内部静电波?BoolLiteral的值(表达式Syntax expr)
{
开关(expr.Kind())
{
大小写SyntaxKind.TrueLiteralExpression:
返回true;
大小写SyntaxKind.FalseLiteralExpression:
返回false;
}
if(expr用括号括起来expressionsyntax parenExpr)
{
返回boolLiteral(parenExpr.Expression)的值;
}
返回null;
}
//在MA0002CodeFixProvider.cs中
使用静态Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
专用静态任务UseLogicalOperatorAsync(文档文档、条件表达式Syntax、取消令牌ct)
{
//expr1?true:expr2->expr1 | | expr2
//expr1?false:expr2->!expr1&&expr2
//expr1?expr2:true->!expr1 | | expr2
//expr1?expr2:false->expr1&&expr2
异步任务UseBaryOperatorAsync(SyntaxKind operatorKind,ExpressionSyntax左,ExpressionSyntax右)
{
var logicalExpr=二进制表达式(运算符ind,左,右);
var syntaxRoot=await document.GetSyntaxRootAsync(ct);
返回文档.WithSyntaxRoot(syntaxRoot.ReplaceNode(三元,logicalExpr));
}
bool?literalValue=MA0002分析器。boolLiteral的值(三元。WhenTrue);
if(literalValue!=null)
{
//?的优先级低于| |,因此我们可能会遇到expr1中的内容绑定更多的情况
//在expr2中比以前更紧密地填充。例如,b1?true:b2&&b3等于b1 | |(b2&&b3),但不是b1 | | b2&&b3。
//我们希望在必要时通过将expr2括在括号中来防止这种情况,但仅当expr2包含
//具有与| |相等/小于的频率。
表达式Syntax right=可插入括号(三元.WhenFalse);
返回literalValue==true?
//这里不必用括号括住expr1,因为布尔运算符是左关联的。
UseBaryOperatorAsync(SyntaxKind.LogicalOrExpression,Trialum.Condition,右侧):
//但是,可能需要在此处将expr1括起来,因为它被取反。
使用BinaryOperatorAsync(SyntaxKind.LogicalAndExpression,Negate(Trialum.Condition),右键);
}
literalValue=MA0002分析器。布尔文本的值(三元。当值为时);
if(literalValue!=null)
{
//在“b1?b2:true;”中,在b2的节点上调用ToFullString()将得到“b2”。这是bc,b2右侧的空间被计为一部分
//因此,在将b2移动到新表达式的末尾之前,我们必须删除尾部的琐事,否则我们将得到“!b1 | | b2;”。
//这不是条件的false分支上的节点的问题。
表达式syntax right=可以是括号(三元.WhenTrue.WithoutTrailingTrivia());
返回literalValue==true?
UseBaryOperatorAsync(SyntaxKind.LogicalOrExpression,Negate(trialy.Condition),右):
使用BinaryOperatorAsync(SyntaxKind.LogicalAndExpression,Trialum.Condition,右侧);
}
返回任务.FromResult(文档);
}
专用静态表达式Syntax可插入括号(表达式Syntax expr)
{
//这里有什么?
}
专用静态表达式syntax否定(表达式syntax expr)
{
if(expr.IsKind(SyntaxKind.LogicalNotExpression))
{
var pue=(PrefixUnaryExpressionSyntax)expr;
返回pue.操作数;
}
if(expr用括号括起来expressionsyntax parenExpr)
{
返回parenExpr.WithExpression(否定(parenExpr.Expression));
}
返回PrefixUnaryExpression(SyntaxKind.LogicalNotExpression,expr);
}
正如您所看到的,我一直在研究如何实现
maybeebranchize
。问题在于,由于三元运算符的优先级低于
| |
,因此从
expr1更改为?true:expr2
expr1 | | expr2
并不总是生成等价的表达式。例如,
b1?正确:b2和b3
等于
b1 | | |(b2和b3)
但不等于
b1 | | b2和b3
。具体来说,每当
expr2
包含任何优先级等于/低于
|
的运算符时,就会发生这种情况


如何检测
expr2
是否包含此类运算符,以便知道何时需要将其括起来,何时不需要?在Roslyn中是否有一种特殊的API/优雅的方法来实现这一点?

如果您试图解决的问题是决定代码修复提供程序中生成的表达式是否应该有括号,那么标准的解决方案是让Roslyn为您决定

这意味着您总是生成括号,但如果安全的话,请Roslyn再次删除括号

我过去曾使用此扩展方法进行过此操作:

public static ExpressionSyntax AddParentheses(this ExpressionSyntax expression)
{
    switch (expression.RawKind)
    {
        case (int)SyntaxKind.ParenthesizedExpression:
        case (int)SyntaxKind.IdentifierName:
        case (int)SyntaxKind.QualifiedName:
        case (int)SyntaxKind.SimpleMemberAccessExpression:
        case (int)SyntaxKind.InterpolatedStringExpression:
        case (int)SyntaxKind.NumericLiteralExpression:
        case (int)SyntaxKind.StringLiteralExpression:
        case (int)SyntaxKind.CharacterLiteralExpression:
        case (int)SyntaxKind.TrueLiteralExpression:
        case (int)SyntaxKind.FalseLiteralExpression:
        case (int)SyntaxKind.NullLiteralExpression:
            return expression;

        default:
            return SyntaxFactory
                .ParenthesizedExpression(expression)
                .WithAdditionalAnnotations(Simplifier.Annotation);
    }
}
开关
和第一组外壳不是必需的。它们有一些成本,但在某些情况下可以消除一些不必要的分配

真正的魔力在于default子句。
.WithAdditionalAnnotations(Simplifier.Annotation)
告诉代码修复基础结构,如果这样做不会改变代码的含义(在上下文中),则可以简化括号,即删除括号

这种方法的主要优点当然是简单,并且可以显著降低代码复杂度。因为您正在重用现有的、经过测试的逻辑,所以在不必为其编写大量测试的情况下,它也很有可能是正确的。此外,如果未来版本的C#添加额外的运算符或其他语法,它可能会保持正确。

实际上,
b1|