更新 似乎我不清楚我究竟要问的是什么(随着时间的推移,我也失去了一些问题),所以这里有一个 tl; dr 版本:
var test1 = a is byte & b; // compiles
var test2 = a is byte? & b; // does not compile
var test3 = a is byte? && b; // compiles
这意味着 - 据我所知 - ?
类型修饰符具有较低的优先级(因为它不是运算符,这可能不是最佳单词)而不是&
运算符,但高于&&
运算符。是这样吗?这在标准中描述了什么?
原来的问题:
在试图从Jon Skeet的优秀博客A Tale of two puzzles中找出第二个谜题的答案时,我遇到了一个问题:
unsafe private void Test<T>(T a, bool b)
{
var test1 = a is byte? & b; // does not compile
var test2 = a is byte? && b; // compiles
var test3 = a is byte ? & b : & b; // compiles
}
这里我使用unsafe
上下文,因为我的实际目标需要它(例如:第三行),但是没有必要再现我提出的问题。 (但它可能会产生影响,因为它引入了地址运算符作为&
符号的替代。)
第一行不编译(其他行),它给出以下错误消息:
Syntax error, ':' expected
这意味着在这种情况下,编译器将该行视为
var test1 = (a is byte) ? &b [: missing part that it complains about];
在第二行中,它将其视为:
var test2 = (a is byte?) && (b);
我检查了运算符优先级(here),顺序(从最高到最低)是以下:&, &&, ?:
,所以单独这一点并不解释为什么第一行不编译而第二行(或者至少不适合我 - 也许这就是我错的地方......)编辑:我理解为什么第二次编译,所以请不要在你的答案中专注于此。
我的下一个预感是,?
类型修饰符的优先级(如果有这样的事情)介于这两个(或实际上是三个)运算符之间({{1 }和&
)。它可以吗?如果没有,请有人解释我遇到的确切行为? &&
类型修饰符的评估顺序是否在标准中明确描述?
编辑:我也知道有一个一元的地址操作符(实际上这是我尝试用于解决方案的技巧......),它在这里起作用,但是我的问题仍然保持不变。
在相同的?
上下文中,我们很乐意编译:
unsafe
所以我认为它必须与var test1 = true & b;
var test2 = true && b;
// or
var test1 = a is byte & b;
var test2 = a is byte && b;
修饰符/运算符相关,而不仅仅与优先级的运算符地址相关(否则两个?
行将无法编译)。
P.S。:我知道我可以在我的代码中添加括号,因此它会编译,但我想避免这样做:
test1
更新 我稍微尝试了Roslyn,我认为为各种语句附加AST可能是一个好主意:
var test = (a is byte?) & b; // compiles
var test1 = a is byte & b;
var test2 = a is byte? & b;
我想强调,我不在链接文章中寻找原始问题的解决方案(当然我正在寻找一个,但我请你不要在这里给出答案,因为我想自己找到它。) 此外,如果我在寻找解决方案的过程中完全错误,请不要发表评论,我只提到了为我的具体问题提供一些背景的难题,并为编写这样的代码提供了一个足够好的借口。
答案 0 :(得分:6)
这里的线索是unsafe
关键字。实际上有两个不同的&amp;运算符 - 按位AND和&amp;您习惯的运营商,还有地址和&amp;运算符,它不仅具有更高的优先级,而且与所有一元运算符一样evaluated right-to-left。
这意味着首先评估&b
,并产生指针值。正如编译器抱怨的那样,声明的其余部分是不可解析的。它可能是(a is byte?) (address)
,或者(当编译器尝试解析它时)(a is byte) ? (address)
并且缺少:
。
更换&amp;时遇到相同的编译错误使用+
或-
,这两个符号可以是一元或二元运算符。
第二个声明编译好的原因是没有一元,从右到左的高优先级&amp;&amp;操作
答案 1 :(得分:2)
说实话,我不太确定我是否应该将此作为答案发布或将此信息添加到 - 已经相当冗长 - 的问题中,但我终于找到了为什么它的行为方式。 (但我仍然认为它没有在标准中明确描述,并且它实际上是编译器当前实现的限制。)
此外,我暂时不会接受我自己的答案,希望有人能够提供更好的答案。
我花了一点时间在Roslyn,我通过lexing和解析这段代码中的各种语句来调试:
var test1 = a is byte & b;
var test2 = a is byte? & b;
var test3 = a is byte? && b;
确切的语法树已经添加到问题中了,所以我不打算在这里重复它们。
这些陈述之间的差异来自编译过程的这一部分(来自LanguageParser.cs):
private TypeSyntax ParseTypeCore(
bool parentIsParameter,
bool isOrAs,
bool expectSizes,
bool isArrayCreation)
{
var type = this.ParseUnderlyingType(parentIsParameter);
if (this.CurrentToken.Kind == SyntaxKind.QuestionToken)
{
var resetPoint = this.GetResetPoint();
try
{
var question = this.EatToken();
// Comment added by me
// This is where the difference occurs
// (as for '&' the IsAnyUnaryExpression() returns true)
if (isOrAs && (IsTerm() || IsPredefinedType(this.CurrentToken.Kind) || SyntaxFacts.IsAnyUnaryExpression(this.CurrentToken.Kind)))
{
this.Reset(ref resetPoint);
Debug.Assert(type != null);
return type;
}
question = CheckFeatureAvailability(question, MessageID.IDS_FeatureNullable);
type = syntaxFactory.NullableType(type, question);
}
finally
{
this.Release(ref resetPoint);
}
}
// Check for pointer types (only if pType is NOT an array type)
type = this.ParsePointerTypeMods(type);
// Now check for arrays.
if (this.IsPossibleRankAndDimensionSpecifier())
{
var ranks = this.pool.Allocate<ArrayRankSpecifierSyntax>();
try
{
while (this.IsPossibleRankAndDimensionSpecifier())
{
bool unused;
var rank = this.ParseArrayRankSpecifier(isArrayCreation, expectSizes, out unused);
ranks.Add(rank);
expectSizes = false;
}
type = syntaxFactory.ArrayType(type, ranks);
}
finally
{
this.pool.Free(ranks);
}
}
Debug.Assert(type != null);
return type;
}
如果byte?
部分后面的符号除了SyntaxKind.None
之外还返回任何内容,则会出现相同的结果:
public static SyntaxKind GetPrefixUnaryExpression(SyntaxKind token)
{
switch (token)
{
case SyntaxKind.PlusToken:
return SyntaxKind.UnaryPlusExpression;
case SyntaxKind.MinusToken:
return SyntaxKind.UnaryMinusExpression;
case SyntaxKind.TildeToken:
return SyntaxKind.BitwiseNotExpression;
case SyntaxKind.ExclamationToken:
return SyntaxKind.LogicalNotExpression;
case SyntaxKind.PlusPlusToken:
return SyntaxKind.PreIncrementExpression;
case SyntaxKind.MinusMinusToken:
return SyntaxKind.PreDecrementExpression;
case SyntaxKind.AmpersandToken:
return SyntaxKind.AddressOfExpression;
case SyntaxKind.AsteriskToken:
return SyntaxKind.PointerIndirectionExpression;
default:
return SyntaxKind.None;
}
}
问题是在is
(或as
)运算符之后,当我们面对?
令牌时,我们会检查下一个令牌是否可以被解释为一元运算符,如果是这样的话:我们不关心?
令牌是类型修饰符的可能性,我们只是在它之前返回类型,并相应地解析其余的(有更多的条件要满足,但这是关于我的问题的相关信息)。具有讽刺意味的是,&
符号甚至不能是一元运算符,只能在不安全的环境中运行,但这一点从未被考虑过。
正如其他人在评论中指出的那样,如果我们向前看更多,也许可以解决这个问题,例如:在那种特殊情况下,我们可以检查是否存在匹配的{{1对于:
令牌,如果没有,则忽略一元?
运算符的可能性,并将&
视为类型修饰符。如果我有时间,我会尝试实施一个解决方法,看看它会导致更大的问题:)(幸运的是,Roslyn解决方案中有很多测试...)
感谢大家的反馈。