更新: 似乎我不清楚我究竟要问的是什么(随着时间的推移,我也失去了一些问题),所以这里是一个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
Run Code Online (Sandbox Code Playgroud)
这意味着 - 据我所知 - ?类型修饰符的优先级较低(因为它不是运算符,这可能不是最好的单词)而不是&运算符,但高于&&运算符.是这样吗?标准中描述了哪里?
原来的问题是:
在尝试从Jon Skeet的优秀博客文章"两个谜题的故事"中找出第二个谜题的答案时,我遇到了一个问题:
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
}
Run Code Online (Sandbox Code Playgroud)
在这里,我使用unsafe上下文,因为我的实际目标需要它(例如:第三行),但没有必要再现我提出的问题.(但它可能会产生影响,因为它引入了地址运算符作为&符号的替代.)
第一行不编译(其他人都这样做),它给出以下错误消息:
Syntax error, ':' expected
Run Code Online (Sandbox Code Playgroud)
这意味着在这种情况下,编译器将该行视为
var test1 = (a is byte) ? &b [: missing part that it complains about];
Run Code Online (Sandbox Code Playgroud)
在第二行中,它将其视为:
var test2 = (a is byte?) && (b);
Run Code Online (Sandbox Code Playgroud)
我检查了运算符优先级(这里),顺序(从最高到最低)是以下:&, &&, ?:,所以这并不能解释为什么第一行不编译而第二行不编译(或者至少不适合我 - 也许这是在哪里我错了...)编辑:我理解为什么第二次编译,所以请不要在你的答案中集中精力.
我的下一个预感是,不知何故,类型修饰符的优先级(如果有这样的东西)?介于这两个(或实际上是三个)运算符(&和&&)之间.它可以吗?如果没有,请有人解释我遇到的确切行为??类型修饰符的评估顺序是否在标准中明确描述?
编辑:我也知道有一个一元的地址运算符(实际上这是我试图用于解决方案的技巧......),它在这里起作用,但我的问题仍然保持不变.
在同样的unsafe背景下,那些愉快的编译:
var test1 = true & b;
var test2 = true && b;
// or
var test1 = a is byte & b;
var test2 = a is byte && b;
Run Code Online (Sandbox Code Playgroud)
所以我认为它必须与?修饰符/运算符有关,而不仅仅与优先级的运算符地址相关(否则两test1行不会编译).
PS:我知道我可以在我的代码中添加括号,因此它会编译,但我想避免这样做:
var test = (a is byte?) & b; // compiles
Run Code Online (Sandbox Code Playgroud)
更新: 我稍微尝试了Roslyn,我认为为各种语句附加AST可能是个好主意:
var test1 = a is byte & b;
Run Code Online (Sandbox Code Playgroud)

var test2 = a is byte? & b;
Run Code Online (Sandbox Code Playgroud)

var test3 = a is byte? && b;
Run Code Online (Sandbox Code Playgroud)

我想强调的是,我并不是在寻找链接文章中的原始问题的解决方案(当然我正在寻找一个,但我请你不要在这里给出答案,因为我想找到它我自己.)另外,如果我在寻找解决方案时遇到完全错误的轨道,请不要评论,我只提到了为我的具体问题提供一些背景的难题,并提供了一个足够好的借口来编写像这个.
老实说,我不太确定是否应该将此作为答案发布,还是将这些信息添加到已经相当冗长的问题中,但我终于找到了为什么它会这样。(但我仍然认为标准中没有明确描述,它实际上是编译器当前实现的限制。)
另外,我暂时不会接受自己的答案,希望有人能够给出更好的答案。
我花了一点时间研究Roslyn,并通过词法分析和解析此代码中的各种语句进行了调试:
var test1 = a is byte & b;
var test2 = a is byte? & b;
var test3 = a is byte? && b;
Run Code Online (Sandbox Code Playgroud)
确切的语法树已经添加到问题中,所以我不会在这里重复它们。
语句之间的差异来自于这部分编译过程(来自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;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
所以问题是,在is(或as)运算符之后,当我们面对一个?标记时,我们检查下一个标记是否可以解释为一元运算符,如果是的话:我们不关心该?标记是一个的可能性type 修饰符,我们只需返回它之前的类型,并相应地解析其余部分(还有更多条件需要满足,但这是有关我的问题的相关信息)。具有讽刺意味的是,该&符号甚至不能是一元运算符,只能在不安全的上下文中,但从未考虑到这一点。
正如其他人在评论中指出的那样,如果我们多看一点:,也许这个问题可以解决,例如:在这种特殊情况下,我们可以检查令牌是否匹配?,如果没有,则忽略以下可能性一元&运算符并将 the?视为类型修饰符。如果我有时间,我会尝试实施一个解决方法,看看它会在哪里引起更大的问题:)(幸运的是,Roslyn 解决方案中有很多测试......)
感谢大家的反馈。