2014-06-19 163 views
10

更新: 看來我對於我所問的問題並不清楚(而隨着時間的推移,失去了跟蹤位),所以這裏是一個TL;博士版本:「?」類型修飾符優先與邏輯和運算符(&)vs地址運算符(&)

var test1 = a is byte & b; // compiles 
var test2 = a is byte? & b; // does not compile 
var test3 = a is byte? && b; // compiles 

這意味着 - 我的理解 - 即?型改性劑具有較低優先(因爲它不是一個運營商,這可能不是最好的詞)比&運營商,但高於&&歌劇TOR。是這樣嗎?標準中描述了哪些內容?


而原題:

雖然試圖找出答案,從喬恩斯基特的優秀博文,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),並且順序(從最高到最低)如下:&, &&, ?:,所以這並不能解釋爲什麼第一行不能編譯,而第二行卻不能編譯(或者至少不是我 - 也許這是我錯了...)編輯:我明白爲什麼第二次編譯,所以請不要專注於你的答案。

我的下一個直覺是,不知何故優先(如果有這樣的事情)爲?類型修飾符是介於這兩個(或實際上爲三個)運營商(&&&)之間。它可以這樣嗎?如果沒有人可以解釋我正在經歷的確切行爲? ?類型修飾符的評估順序是否清晰地在標準的某個位置進行了描述?

編輯:我也意識到有一個一元的地址 - 操作符(實際上這是我試圖用於解決方案的技巧...),這在這裏扮演一個角色,但我的問題仍然存在一樣。

同樣unsafe背景下對這些興高采烈地編譯:

var test1 = true & b; 
var test2 = true && b; 
// or 
var test1 = a is byte & b; 
var test2 = a is byte && b; 

所以,我認爲,必須進行相關的?改性劑/運營商,而不是僅僅以運營商地址服用優先級(另有兩個test1行不會編譯)。

PS:我知道,我可以加括號我的代碼,所以它會編譯,但我想避免這種情況:

var test = (a is byte?) & b; // compiles 

更新: 我羅斯林一點,我嘗試認爲它可能是附加的AST的各種報表是一個好主意:

var test1 = a is byte & b; 

AST for test1 line

var test2 = a is byte? & b; 

AST for test2 line

var test3 = a is byte? && b; 

AST for test3 line


我想強調,我尋找鏈接的文章中對原問題的解決方案(當然我要找一個,但我請你不要在這裏給出答案,因爲我希望自己找到答案。) 另外,如果我在完全錯誤的tra上,請不要評論在找到解決方案時,我只提到了爲我的具體問題提供一些背景的難題,併爲編寫這樣的代碼提供了一個足夠好的藉口。

+2

不錯的問題。現在我放棄了 - 語言規範中的「語法歧義」部分。不包括這種情況。你可能會覺得有趣的一件事是,互聯網似乎在思考這個問題[大約8年](http://www.progtown.com/topic15933-grammar-ambiguities-in-c-20.html); ) – BartoszKP

+0

這是猜測,但我懷疑問題在於解析器只展望幾個標記,而當它解析'?'時,它只能看到'&b'或更少。 'a是字節? &b'不明確,它可能意味着可爲空類型或三元運算符。如果它能夠向前看並看到分號,那麼歧義將被解決。這可能是由於[lookahead]有限(http://en.wikipedia.org/wiki/Parsing#Lookahead),而不是本身的運算符優先級。 –

+0

@KendallFrey這顯然不是「運算符優先級本身」,因爲'byte?'中的'?'是一個類型修飾符,而不是運算符。我認爲你是正確的,問題要早得多,在詞法層面更多。 – BartoszKP

回答

2

說實話,我不太確定我是否應該將此作爲答案發布或將此信息添加到已經非常詳細的問題中,但我終於找到了爲什麼它的行爲如此。 (但我仍然認爲它在標準中沒有明確描述,而且它實際上是編譯器當前實現的限制。)

此外,我不會接受我自己的答案了一段時間,希望有人也許能夠給出更好的答案的選擇。

我花了一點時間Roslyn,我通過這個代碼的詞法和各種報表的分析調試:

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)操作後,當我們面對一個?令牌,然後我們檢查,如果下一個標記可以被解釋爲一元運算符如果是這樣:我們不關心?令牌是一個類型修飾符的可能性,我們只是才返回類型,並會相應地解析其餘部分(還有更多條件可以滿足,但這是關於我的問題的相關信息)。具有諷刺意味的是,&符號甚至不可能是一元操作符,只能在不安全的情況下使用,但這從未被考慮。

正如其他人在評論中所指出的那樣,也許這個問題可以解決,如果我們放眼望去更一點點,例如:在特定情況下,我們可以檢查是否存在對?令牌匹配的:,和如果不是,則忽略一元運算符&的可能性並將?作爲類型修飾符。如果我有時間,我會嘗試實施一種解決方法,並且看看它會在哪裏引起更大的問題:)(幸運的是,在Roslyn解決方案中有很多測試...)

感謝大家他們的反饋。

+0

偉大的工作!我希望每個人都有這麼多的分析決心,深入到問題的底部並解決它:) – BartoszKP

6

這裏的線索是unsafe關鍵字。實際上有兩種不同的運營商 - 你所熟悉的按位運算符AND &運算符,但也是運算符&的地址,它不僅具有更高的優先級,而且像所有一元運算符一樣是evaluated right-to-left

這意味着&b首先被評估,併產生一個指針值。正如編譯器所抱怨的,聲明的其餘部分是不可解析的。它可能是(a is byte?) (address),或者(因爲編譯器試圖解析它)(a is byte) ? (address)並且缺少:

當將&替換爲+-時,我會得到相同的編譯錯誤,這兩個符號可以是一元或二元運算符。

第二條語句編譯得很好的原因是沒有一個一元的從右到左的高優先級運算符。

+0

我認爲你走在正確的軌道上,但它與'unsafe'關鍵字沒有任何關係。刪除它會給出同樣的錯誤。 –

+0

謝謝,但是如果我改變'a是字節?'部分簡單地'真',那麼它會很高興地編譯。例如:var test = true&b;一元運營商不應該在這裏也優先嗎? – qqbenq

+0

我以爲這種情況一開始就是這樣,但如果我將運算符更改爲'+',它同時也是一元和二元的,它給運算符本身帶來了不同的錯誤。 「;預計」 –