diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..aaa6e8fef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Report an issue with the C# standard +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. Does the standard disallow code that should be valid? Does the standard allow code that should produce a compiler error? Does the language in the standard leave an ambiguity where the behavior should be mandated? + +Include a reference to the clause in the standard containing the violation. + +Include the version of C# where you encounter this error. The committee is working on the updated standard for C# 7.3. We have lists for features that will be added in later versions. Bugs that describe changes to behavior that existed in 7.3 are useful. Adding issues for features that aren't covered are less helpful. + +**Example** +Provide an example that shows the problem in the standard. A small code snippet that should be allowed or disallowed, or code that behaves differently than the standard. + +```csharp + // Add code here +``` + +**Expected behavior** +What should the standard declare differently? + +**Additional context** +Add any other context about the problem here. If you have links to a reference implementation, or feature specification, or issues recorded in an implementation, those details would be very helpful. diff --git a/.github/workflows/dependencies/EcmaTC49.BuildGrammar.1.0.0-alpha.1.nupkg b/.github/workflows/dependencies/EcmaTC49.BuildGrammar.1.0.0-alpha.2.nupkg similarity index 89% rename from .github/workflows/dependencies/EcmaTC49.BuildGrammar.1.0.0-alpha.1.nupkg rename to .github/workflows/dependencies/EcmaTC49.BuildGrammar.1.0.0-alpha.2.nupkg index ba1461fe5..13deaf400 100644 Binary files a/.github/workflows/dependencies/EcmaTC49.BuildGrammar.1.0.0-alpha.1.nupkg and b/.github/workflows/dependencies/EcmaTC49.BuildGrammar.1.0.0-alpha.2.nupkg differ diff --git a/.github/workflows/grammar-validator.yaml b/.github/workflows/grammar-validator.yaml index e1223bf52..243677862 100644 --- a/.github/workflows/grammar-validator.yaml +++ b/.github/workflows/grammar-validator.yaml @@ -34,7 +34,7 @@ jobs: # Install build grammar global tool - name: Install BuildGrammar tool run: | - dotnet tool install --version 1.0.0-alpha.1 --global --add-source ./.github/workflows/dependencies/ EcmaTC49.BuildGrammar + dotnet tool install --version 1.0.0-alpha.2 --global --add-source ./.github/workflows/dependencies/ EcmaTC49.BuildGrammar - name: run validate diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index 66c9716df..1bbfa4f59 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -5,6 +5,7 @@ on: branches: - draft-v6 - draft-v7 + - draft-v8 paths: - "standard/*.md" - ".markdownlint.json" @@ -27,11 +28,11 @@ jobs: statuses: write steps: - - uses: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45769f953675 #@v2 + - uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f - name: Use Node.js - uses: actions/setup-node@56899e050abffc08c2b3b61f3ec6a79a9dc3223d #@v1 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c with: - node-version: 14.x + node-version: 16.x - name: Run Markdownlint run: | echo "::add-matcher::.github/workflows/markdownlint-problem-matcher.json" diff --git a/.github/workflows/quest-bulk.yml b/.github/workflows/quest-bulk.yml new file mode 100644 index 000000000..37a051072 --- /dev/null +++ b/.github/workflows/quest-bulk.yml @@ -0,0 +1,36 @@ +name: "bulk quest import" +on: + schedule: + - cron: '0 10 * * *' # UTC time, that's 5:00 am EST, 2:00 am PST. + workflow_dispatch: + inputs: + reason: + description: "The reason for running the bulk import workflow" + required: true + default: "Initial import into Quest (Azure DevOps)" + +jobs: + bulk-import: + runs-on: ubuntu-latest + permissions: + issues: write + if: ${{ github.repository_owner == 'dotnet' }} + + steps: + - name: "Print manual bulk import run reason" + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "Reason: ${{ github.event.inputs.reason }}" + + - name: bulk-sequester + id: bulk-sequester + uses: dotnet/docs-tools/actions/sequester@main + env: + ImportOptions__ApiKeys__GitHubToken: ${{ secrets.GITHUB_TOKEN }} + ImportOptions__ApiKeys__OSPOKey: ${{ secrets.OSPO_KEY }} + ImportOptions__ApiKeys__QuestKey: ${{ secrets.QUEST_KEY }} + with: + org: ${{ github.repository_owner }} + repo: ${{ github.repository }} + issue: '-1' + branch: ${{ github.ref_name }} diff --git a/.github/workflows/quest.yml b/.github/workflows/quest.yml index 94e348472..f66484040 100644 --- a/.github/workflows/quest.yml +++ b/.github/workflows/quest.yml @@ -1,11 +1,5 @@ name: "quest import" on: - issues: - types: - [ labeled, closed, reopened, assigned, unassigned ] - pull_request: - types: - [ labeled, closed, assigned, unassigned ] workflow_dispatch: inputs: reason: @@ -26,7 +20,6 @@ jobs: contains(github.event.issue.labels.*.name, 'seQUESTered') runs-on: ubuntu-latest permissions: - contents: write issues: write steps: @@ -65,4 +58,3 @@ jobs: repo: ${{ github.repository }} issue: ${{ github.event.issue.number }} branch: ${{ github.ref_name }} - diff --git a/.github/workflows/test-examples.yaml b/.github/workflows/test-examples.yaml index 10fa2759d..06f167655 100644 --- a/.github/workflows/test-examples.yaml +++ b/.github/workflows/test-examples.yaml @@ -5,7 +5,7 @@ on: pull_request: types: [opened, synchronize, reopened] paths: - - '/standard/*.md' + - "standard/*.md" workflow_dispatch: inputs: reason: diff --git a/README.md b/README.md index 6412e95a5..3e69b35da 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This project has adopted the code of conduct defined by the Contributor Covenant ### C# 7.0 draft -The branch `draft-v7` has the draft text for C# 7.0. It has not been submitted as a formal standard to ECMA. This version is a working draft that contains the features for C# 7.0. +The branch `standard-v7` has the text for C# 7.0. It has been submitted as a formal standard to ECMA. ### C# 6.0 standard @@ -43,7 +43,7 @@ For now, it contains separate logs for the work going on to add V6 (and then V7) ## Tools folder -This folder contains tools related to maintaining and converting the ECMA C# spec (ECMA-354). +This folder contains tools related to maintaining and converting the ECMA C# spec (ECMA-334). ### GetGrammar @@ -77,4 +77,4 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org) ## Table of contents - C# standard -The [README.md](standard/README.md) file in the `standard` folder contains a detailed table of contents for the C# standard. \ No newline at end of file +The [README.md](standard/README.md) file in the `standard` folder contains a detailed table of contents for the C# standard. diff --git a/admin/v7-feature-tracker.md b/admin/v7-feature-tracker.md index f6e360c1c..a09884d9b 100644 --- a/admin/v7-feature-tracker.md +++ b/admin/v7-feature-tracker.md @@ -10,35 +10,35 @@ For any given feature, Rex actually wrote his version of the final proposal, in Version | Feature | PR | Status | Effort | Notes ------- | ------- | -- | ------ | ------ | ----- -7.0 | binary integer literals ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/binary-literals.md)) | [548](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/548) | Completed | Small | Feature Group A. -7.0 | embedded digit separators in numeric literals ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/digit-separators.md)) | [548](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/548) | Completed | Small | Feature Group A. -7.0 | `out` variables ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/out-var.md)) | [44](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/44) | Open | Small | Feature Group B. -7.0 | Discards ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/out-var.md)) | [44](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/44), [596](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/596) | Open | Small | Feature Group B. -7.0 | Tuples ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/tuples.md)) | [63](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/63) **but needs some work**, [596](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/596) | Open | Large | Feature Group C. -7.0 | Pattern Matching ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/pattern-matching.md)) | [61](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/61), [596](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/596) | Open | Medium | Feature Group D. -7.0 | `ref` locals and returns ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/ref-locals-returns.md)) | [213](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/213) **but needs work** | Open | Medium | Feature Group E. -7.0 | Local Functions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/local-functions.md)) | [104](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/104) | Completed | Small | -7.0 | More expression-bodied members ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/expression-bodied-everything.md)) | [69](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/69) | Completed | Small | -7.0 | `throw` Expressions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/throw-expression.md)) | [65](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/65) | Completed | Small | -7.0 | Generalized `async` return types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/task-types.md), [tutorial](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/async-return-types)) | [556](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/556) **but needs some work** | Open | Small | -7.1 | `async Main` method ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/async-main.md)) | [70](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/70) | Completed | Small | -7.1 | `default` literal expressions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/target-typed-default.md)) | [236](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/236) | Completed | Small | -7.1 | Inferred tuple element names ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/infer-tuple-names.md)) | [63](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/63) **but needs some work** | Open | Large | Feature Group C. -7.1 | Pattern matching on generic type parameters ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/generics-pattern-match.md)) | [61](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/61) | Open | Medium | Feature Group D. -7.2 | leading digit separators in bin/hex integer literals ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/leading-separator.md)) | [548](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/548) | Completed | Small | Feature Group A. -7.2 | Non-trailing named arguments ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/non-trailing-named-arguments.md)) | [216](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/216) | Open | Small | -7.2 | `private protected` access modifier ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/private-protected.md)) | [215](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/215) | Completed | Small | -7.2 | Conditional `ref` expressions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/conditional-ref.md)) | [213](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/213) **but needs work** | Open | Medium | Feature Group E. -7.2 | `in` parameter modifier ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-ref.md)) | [219](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/219) | Open | Medium | Feature Group F. -7.2 | `ref` with `this` in extension methods ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/ref-extension-methods.md)) | [219](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/219) | Open | Medium | Feature Group F. -7.2 | `readonly` structs ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-struct.md)) | [333](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/333) | Completed | Small | -7.2 | `ref` structs ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/span-safety.md)) | TBD | Open | Medium | -7.3 | indexing movable fixed buffer without pinning ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/indexing-movable-fixed-fields.md)) | [239](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/239) | Open | Small | -7.3 | reassign `ref` local variables ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/ref-local-reassignment.md)) | [213](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/213) **but needs work** | Open | Medium | Feature Group E. -7.3 | use initializers on `stackalloc` arrays ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/stackalloc-array-initializers.md)) | [238](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/238) | Open | Small | -7.3 | Support for Pattern-Based `fixed` Statements ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/pattern-based-fixed.md)) | [240](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/240) | Open | Small | -7.3 | use additional generic constraints ([MS Proposal 1](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/blittable.md)) ([MS proposal 2](https://github.com/dotnet/csharplang/issues/104)) ([MS proposal 3](https://github.com/dotnet/csharplang/issues/103)) | [244](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/244) | Open | Small | -7.3 | test `==` and `!=` with tuple types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/tuple-equality.md)) | [63](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/63) **but needs some work** | Open | Large | Feature Group C. -7.3 | use expression variables in more locations ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/expression-variables-in-initializers.md)) | [44](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/44), [61](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/61) | Open | Medium | Feature Group B and D. -7.3 | attach attributes to the backing field of auto-implemented properties ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/auto-prop-field-attrs.md)) | [262](https://github.com/dotnet/csharpstandard/pull/262) | Completed | Small | -7.3 | overload resolution now has fewer ambiguous cases ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/improved-overload-candidates.md)) | [263](https://github.com/dotnet/csharpstandard/pull/263) | Completed | Small | +7.0 | binary integer literals ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/binary-literals.md)) | [548](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/548) | Merged | Small | Feature Group A. +7.0 | embedded digit separators in numeric literals ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/digit-separators.md)) | [548](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/548) | Merged | Small | Feature Group A. +7.0 | `out` variables ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/out-var.md)) | [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [44](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/44) | Merged | Small | Feature Group B. +7.0 | Discards ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/out-var.md)) | [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [44](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/44) and [596](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/596) | Merged | Small | Feature Group B. +7.0 | Tuples ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/tuples.md)) | [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [63](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/63) and [596](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/596) | Merged | Large | Feature Group C. +7.0 | Pattern Matching ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/pattern-matching.md)) | [61](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/61), [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [596](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/596); [757](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/757), | Merged | Medium | Feature Group D. +7.0 | `ref` locals and returns ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/ref-locals-returns.md)) | [213](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/213), [742](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/742) | Merged | Medium | Feature Group E. +7.0 | Local Functions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/local-functions.md)) | [104](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/104) | Merged | Small | +7.0 | More expression-bodied members ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/expression-bodied-everything.md)) | [69](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/69) | Merged | Small | +7.0 | `throw` Expressions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/throw-expression.md)) | [65](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/65) | Merged | Small | +7.0 | Generalized `async` return types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/task-types.md), [tutorial](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/async-return-types)) | [556](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/556) | Merged | Small | +7.1 | `async Main` method ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/async-main.md)) | [70](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/70) | Merged | Small | +7.1 | `default` literal expressions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/target-typed-default.md)) | [236](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/236) | Merged | Small | +7.1 | Inferred tuple element names ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/infer-tuple-names.md)) | [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [63](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/63) | Merged | Large | Feature Group C. +7.1 | Pattern matching on generic type parameters ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.1/generics-pattern-match.md)) | [61](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/61) | Merged | Medium | Feature Group D. +7.2 | leading digit separators in bin/hex integer literals ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/leading-separator.md)) | [548](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/548) | Merged | Small | Feature Group A. +7.2 | Non-trailing named arguments ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/non-trailing-named-arguments.md)) | [216](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/216) | Merged | Small | +7.2 | `private protected` access modifier ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/private-protected.md)) | [215](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/215) | Merged | Small | +7.2 | Conditional `ref` expressions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/conditional-ref.md)) | [213](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/213) | Merged | Medium | Feature Group E. +7.2 | `in` parameter modifier ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-ref.md)) | [219](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/219) | Merged | Medium | Feature Group F. +7.2 | `ref` with `this` in extension methods ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/ref-extension-methods.md)) | [219](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/219) | Merged | Medium | Feature Group F. +7.2 | `readonly` structs ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-struct.md)) | [333](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/333) | Merged | Small | +7.2 | `ref` structs ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/span-safety.md)) | [601](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/601) | Merged | Medium | +7.3 | indexing movable fixed buffer without pinning ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/indexing-movable-fixed-fields.md)) | [239](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/239) | Merged | Small | +7.3 | reassign `ref` local variables ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/ref-local-reassignment.md)) | [213](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/213) | Merged | Medium | Feature Group E. +7.3 | use initializers on `stackalloc` arrays ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/stackalloc-array-initializers.md)) | [238](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/238) | Merged | Small | +7.3 | Support for Pattern-Based `fixed` Statements ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/pattern-based-fixed.md)) | [240](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/240) | Merged | Small | +7.3 | use additional generic constraints ([MS Proposal 1](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/blittable.md)) ([MS proposal 2](https://github.com/dotnet/csharplang/issues/104)) ([MS proposal 3](https://github.com/dotnet/csharplang/issues/103)) | [244](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/244) | Merged | Small | +7.3 | test `==` and `!=` with tuple types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/tuple-equality.md)) | [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [63](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/63) | Merged | Large | Feature Group C. +7.3 | use expression variables in more locations ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/expression-variables-in-initializers.md)) | [664](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/664), which replaced [44](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/44), [61](https://github.com/ECMA-TC49-TG2/csharpstandard/pull/61) | Merged | Medium | Feature Group B and D. +7.3 | attach attributes to the backing field of auto-implemented properties ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/auto-prop-field-attrs.md)) | [262](https://github.com/dotnet/csharpstandard/pull/262) | Merged | Small | +7.3 | overload resolution now has fewer ambiguous cases ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.3/improved-overload-candidates.md)) | [263](https://github.com/dotnet/csharpstandard/pull/263) | Merged | Small | diff --git a/admin/v8-feature-tracker.md b/admin/v8-feature-tracker.md index 617b041a5..8381ee14d 100644 --- a/admin/v8-feature-tracker.md +++ b/admin/v8-feature-tracker.md @@ -4,30 +4,22 @@ This file identifies the known V8 features, and tracks their status. The *Effort* column is an attempt to show the size/complexity of the proposal, such as *small*, *medium*, or *large*, allowing TG2 members to pick-and-chose the ones they'll work on next. -For V7, we started with a set of [MS proposals](https://github.com/dotnet/csharplang/tree/main/proposals), some of which were placeholders **for which text was never provided**. Where they existed, Rex took these proposals, wrote tests, looked at MS (and other) tutorial pages, and created a branch, and for most features, a corresponding (Draft) PR, with the edits he thought were needed. **It is quite possible that not everything in any given MS proposal was in fact implemented in that version, and it is also possible that things implemented in a version later on were not spec'd back into the proposal.** - -For any given feature, Rex actually wrote his version of the final proposal, in a Word file, but using md syntax. This allowed him to order the sets of edits by clause and subclause, and to exploit Word's comment, tracked-change, color, and other features, in order to make the proposal more readable and consistant. A Table-of-Contents was also added, so the reader can see at a glance the areas of the standard impacted by the proposal. This Word document was eventually turned into the branch edits and corresponding PR. For at least some features, it likely will be useful for TG2 members to have access to these Word files, as they may be helpful in seeing the actual changes made, along with "Notes to TG2" that Rex made for consideration come processing time. (These Word documents and their associated test currently reside in a DropBox folder to which Bill Wagner has access, as well as on Rex's machine.) - - -Feature | PR | Status | Effort | Notes -------- | -- | ------ | ------ | ------ -alternative interpolated verbatim strings ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/alternative-interpolated-verbatim.md)) | small | Completed | | -async streams ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/async-streams.md)) | | Completed | small | -async using declaration ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/async-using.md)) | | Open | | -override with constraints ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/constraints-in-overrides.md)) | | Open | -unmanaged constructed types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/constructed-unmanaged.md)) | small | Completed | | -default interface methods ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/default-interface-methods.md)) | | Largely complete | medium | Needs some Q's answered -permit `stackalloc` in nested contexts ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/nested-stackalloc.md)) | small | In-progress | | -`notnull` constraint ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/notnull-constraint.md)) | | Almost complete | small | -null coalescing assignment ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/null-coalescing-assignment.md)) | small | Completed | | -nullable reference types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/nullable-reference-types.md)) | | Almost complete | large | Needs many Q's answered -Obsolete on property accessor ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/obsolete-accessor.md)) | no change | Postponed | | See Issue #375 -recursive pattern matching ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/patterns.md)) | | medium | | -ranges and indices ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/ranges.md)) | | Completed | medium | -readonly instance members ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/readonly-instance-members.md)) | small | Completed | | -name shadowing in nested functions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/shadowing-in-nested-functions.md)) | small | Open | | -static local functions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/static-local-functions.md)) | | Almost complete | small | Pending final words for V7.0 addition of non-static local functions -unconstrained type parameter in null coalescing operator ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/unconstrained-null-coalescing.md)) | | Completed | | -using declarations ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/using.md)) | | Completed | small | -disposable ref structs/pattern-based using ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/using.md)) | | Almost complete | small | Q. on Teams re extension method support - +Feature | PR | Status | Effort | Annotation | Notes +------- | -- | ------ | ------ | ---------- | ------ +alternative interpolated verbatim strings ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/alternative-interpolated-verbatim.md)) | [607](https://github.com/dotnet/csharpstandard/pull/607) | Completed | small | N/A | +async streams ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/async-streams.md)) | [606](https://github.com/dotnet/csharpstandard/pull/606) | Completed | small | Done | PR [672](https://github.com/dotnet/csharpstandard/pull/672) will be layered on top of this +using declarations and async using ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/using.md)), ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/async-using.md)) | [672](https://github.com/dotnet/csharpstandard/pull/672) | Completed | small | Done | This PR will be layered on top of PR [606](https://github.com/dotnet/csharpstandard/pull/606); reconcile any overlap +override with constraints ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/constraints-in-overrides.md)) | [671](https://github.com/dotnet/csharpstandard/pull/671) | Completed | small | Done | +unmanaged constructed types ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/constructed-unmanaged.md)) | [604](https://github.com/dotnet/csharpstandard/pull/604) | Completed | small | N/A | +default interface methods ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/default-interface-methods.md)) | [681](https://github.com/dotnet/csharpstandard/pull/681) | Completed | medium | Done | +permit `stackalloc` in nested contexts ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/nested-stackalloc.md)) | | In-progress | small | N/A | can be completed once draft-V8 has been rebased on final V7 +`notnull` constraint ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/notnull-constraint.md)) | [830](https://github.com/dotnet/csharpstandard/pull/830) | Completed | small | Done | +null coalescing assignment ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/null-coalescing-assignment.md)) | [609](https://github.com/dotnet/csharpstandard/pull/609) | In-progress | small | N/A | See Issue [#737](https://github.com/dotnet/csharpstandard/issues/737) +nullable reference types ([MS Proposal (from V9)](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/nullable-reference-types-specification.md) which supercedes ([MS Proposal (from V8)](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/nullable-reference-types.md)) |[700](https://github.com/dotnet/csharpstandard/pull/700) | Completed | large | Done | related to V8 "notnull" feature +Obsolete on property accessor ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/obsolete-accessor.md)) | **no change needed** | Postponed | | N/A | See Issue [#375](https://github.com/dotnet/csharpstandard/issues/375) +New kinds of pattern matching ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/patterns.md)) | [873](https://github.com/dotnet/csharpstandard/pull/873) | In-progress | medium | Done | can be completed once draft-V8 has been rebased on final V7 +ranges and indices ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/ranges.md)) | [605](https://github.com/dotnet/csharpstandard/pull/605) | Completed | medium | Done | +readonly instance members ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/readonly-instance-members.md)) | [673](https://github.com/dotnet/csharpstandard/pull/673) | Completed | small | N/A | **Needs a small tweak once draft-v8 rebased with draft-v7** +name shadowing in nested functions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/shadowing-in-nested-functions.md)) | [608](https://github.com/dotnet/csharpstandard/pull/608) | Completed | small | N/A | +static local functions ([MS Proposal](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/static-local-functions.md)) | [869](https://github.com/dotnet/csharpstandard/pull/869)| Completed | small | N/A | +Disposable ref structs [672](https://github.com/dotnet/csharpstandard/pull/672) | | | | | Included in PR [606](https://github.com/dotnet/csharpstandard/pull/606) **Check this** diff --git a/standard/README.md b/standard/README.md index 2cbda631e..e30c6541d 100644 --- a/standard/README.md +++ b/standard/README.md @@ -100,8 +100,9 @@ - [§8.3.8](types.md#838-the-decimal-type) The Decimal type - [§8.3.9](types.md#839-the-bool-type) The Bool type - [§8.3.10](types.md#8310-enumeration-types) Enumeration types - - [§8.3.11](types.md#8311-nullable-value-types) Nullable value types - - [§8.3.12](types.md#8312-boxing-and-unboxing) Boxing and unboxing + - [§8.3.11](types.md#8311-tuple-types) Tuple types + - [§8.3.12](types.md#8312-nullable-value-types) Nullable value types + - [§8.3.13](types.md#8313-boxing-and-unboxing) Boxing and unboxing - [§8.4](types.md#84-constructed-types) Constructed types - [§8.4.1](types.md#841-general) General - [§8.4.2](types.md#842-type-arguments) Type arguments @@ -125,7 +126,9 @@ - [§9.2.5](variables.md#925-value-parameters) Value parameters - [§9.2.6](variables.md#926-reference-parameters) Reference parameters - [§9.2.7](variables.md#927-output-parameters) Output parameters - - [§9.2.8](variables.md#928-local-variables) Local variables + - [§9.2.8](variables.md#928-input-parameters) Input parameters + - [§9.2.9](variables.md#929-local-variables) Local variables + - [§9.2.9.1](variables.md#9291-discards) Discards - [§9.3](variables.md#93-default-values) Default values - [§9.4](variables.md#94-definite-assignment) Definite assignment - [§9.4.1](variables.md#941-general) General @@ -165,8 +168,21 @@ - [§9.4.4.31](variables.md#94431-anonymous-functions) Anonymous functions - [§9.4.4.32](variables.md#94432-throw-expressions) Throw expressions - [§9.4.4.33](variables.md#94433-rules-for-variables-in-local-functions) Rules for variables in local functions + - [§9.4.4.34](variables.md#94434-is-pattern-expressions) is-pattern expressions - [§9.5](variables.md#95-variable-references) Variable references - [§9.6](variables.md#96-atomicity-of-variable-references) Atomicity of variable references + - [§9.7](variables.md#97-reference-variables-and-returns) Reference variables and returns + - [§9.7.1](variables.md#971-general) General + - [§9.7.2](variables.md#972-ref-safe-contexts) Ref safe contexts + - [§9.7.2.1](variables.md#9721-general) General + - [§9.7.2.2](variables.md#9722-local-variable-ref-safe-context) Local variable ref safe context + - [§9.7.2.3](variables.md#9723-parameter-ref-safe-context) Parameter ref safe context + - [§9.7.2.4](variables.md#9724-field-ref-safe-context) Field ref safe context + - [§9.7.2.5](variables.md#9725-operators) Operators + - [§9.7.2.6](variables.md#9726-function-invocation) Function invocation + - [§9.7.2.7](variables.md#9727-values) Values + - [§9.7.2.8](variables.md#9728-constructor-invocations) Constructor invocations + - [§9.7.2.9](variables.md#9729-limitations-on-reference-variables) Limitations on reference variables - [§10](conversions.md#10-conversions) Conversions - [§10.1](conversions.md#101-general) General - [§10.2](conversions.md#102-implicit-conversions) Implicit conversions @@ -182,20 +198,22 @@ - [§10.2.10](conversions.md#10210-implicit-dynamic-conversions) Implicit dynamic conversions - [§10.2.11](conversions.md#10211-implicit-constant-expression-conversions) Implicit constant expression conversions - [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters) Implicit conversions involving type parameters - - [§10.2.13](conversions.md#10213-user-defined-implicit-conversions) User-defined implicit conversions - - [§10.2.14](conversions.md#10214-anonymous-function-conversions-and-method-group-conversions) Anonymous function conversions and method group conversions - - [§10.2.15](conversions.md#10215-default-literal-conversions) Default literal conversions - - [§10.2.16](conversions.md#10216-implicit-throw-conversions) Implicit throw conversions + - [§10.2.13](conversions.md#10213-implicit-tuple-conversions) Implicit tuple conversions + - [§10.2.14](conversions.md#10214-user-defined-implicit-conversions) User-defined implicit conversions + - [§10.2.15](conversions.md#10215-anonymous-function-conversions-and-method-group-conversions) Anonymous function conversions and method group conversions + - [§10.2.16](conversions.md#10216-default-literal-conversions) Default literal conversions + - [§10.2.17](conversions.md#10217-implicit-throw-conversions) Implicit throw conversions - [§10.3](conversions.md#103-explicit-conversions) Explicit conversions - [§10.3.1](conversions.md#1031-general) General - [§10.3.2](conversions.md#1032-explicit-numeric-conversions) Explicit numeric conversions - [§10.3.3](conversions.md#1033-explicit-enumeration-conversions) Explicit enumeration conversions - [§10.3.4](conversions.md#1034-explicit-nullable-conversions) Explicit nullable conversions - [§10.3.5](conversions.md#1035-explicit-reference-conversions) Explicit reference conversions - - [§10.3.6](conversions.md#1036-unboxing-conversions) Unboxing conversions - - [§10.3.7](conversions.md#1037-explicit-dynamic-conversions) Explicit dynamic conversions - - [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters) Explicit conversions involving type parameters - - [§10.3.9](conversions.md#1039-user-defined-explicit-conversions) User-defined explicit conversions + - [§10.3.6](conversions.md#1036-explicit-tuple-conversions) Explicit tuple conversions + - [§10.3.7](conversions.md#1037-unboxing-conversions) Unboxing conversions + - [§10.3.8](conversions.md#1038-explicit-dynamic-conversions) Explicit dynamic conversions + - [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters) Explicit conversions involving type parameters + - [§10.3.10](conversions.md#10310-user-defined-explicit-conversions) User-defined explicit conversions - [§10.4](conversions.md#104-standard-conversions) Standard conversions - [§10.4.1](conversions.md#1041-general) General - [§10.4.2](conversions.md#1042-standard-implicit-conversions) Standard implicit conversions @@ -214,482 +232,517 @@ - [§10.7.2](conversions.md#1072-evaluation-of-anonymous-function-conversions-to-delegate-types) Evaluation of anonymous function conversions to delegate types - [§10.7.3](conversions.md#1073-evaluation-of-lambda-expression-conversions-to-expression-tree-types) Evaluation of lambda expression conversions to expression tree types - [§10.8](conversions.md#108-method-group-conversions) Method group conversions -- [§11](expressions.md#11-expressions) Expressions - - [§11.1](expressions.md#111-general) General - - [§11.2](expressions.md#112-expression-classifications) Expression classifications - - [§11.2.1](expressions.md#1121-general) General - - [§11.2.2](expressions.md#1122-values-of-expressions) Values of expressions - - [§11.3](expressions.md#113-static-and-dynamic-binding) Static and Dynamic Binding - - [§11.3.1](expressions.md#1131-general) General - - [§11.3.2](expressions.md#1132-binding-time) Binding-time - - [§11.3.3](expressions.md#1133-dynamic-binding) Dynamic binding - - [§11.3.4](expressions.md#1134-types-of-subexpressions) Types of subexpressions - - [§11.4](expressions.md#114-operators) Operators - - [§11.4.1](expressions.md#1141-general) General - - [§11.4.2](expressions.md#1142-operator-precedence-and-associativity) Operator precedence and associativity - - [§11.4.3](expressions.md#1143-operator-overloading) Operator overloading - - [§11.4.4](expressions.md#1144-unary-operator-overload-resolution) Unary operator overload resolution - - [§11.4.5](expressions.md#1145-binary-operator-overload-resolution) Binary operator overload resolution - - [§11.4.6](expressions.md#1146-candidate-user-defined-operators) Candidate user-defined operators - - [§11.4.7](expressions.md#1147-numeric-promotions) Numeric promotions - - [§11.4.7.1](expressions.md#11471-general) General - - [§11.4.7.2](expressions.md#11472-unary-numeric-promotions) Unary numeric promotions - - [§11.4.7.3](expressions.md#11473-binary-numeric-promotions) Binary numeric promotions - - [§11.4.8](expressions.md#1148-lifted-operators) Lifted operators - - [§11.5](expressions.md#115-member-lookup) Member lookup - - [§11.5.1](expressions.md#1151-general) General - - [§11.5.2](expressions.md#1152-base-types) Base types - - [§11.6](expressions.md#116-function-members) Function members - - [§11.6.1](expressions.md#1161-general) General - - [§11.6.2](expressions.md#1162-argument-lists) Argument lists - - [§11.6.2.1](expressions.md#11621-general) General - - [§11.6.2.2](expressions.md#11622-corresponding-parameters) Corresponding parameters - - [§11.6.2.3](expressions.md#11623-run-time-evaluation-of-argument-lists) Run-time evaluation of argument lists - - [§11.6.3](expressions.md#1163-type-inference) Type inference - - [§11.6.3.1](expressions.md#11631-general) General - - [§11.6.3.2](expressions.md#11632-the-first-phase) The first phase - - [§11.6.3.3](expressions.md#11633-the-second-phase) The second phase - - [§11.6.3.4](expressions.md#11634-input-types) Input types - - [§11.6.3.5](expressions.md#11635-output-types) Output types - - [§11.6.3.6](expressions.md#11636-dependence) Dependence - - [§11.6.3.7](expressions.md#11637-output-type-inferences) Output type inferences - - [§11.6.3.8](expressions.md#11638-explicit-parameter-type-inferences) Explicit parameter type inferences - - [§11.6.3.9](expressions.md#11639-exact-inferences) Exact inferences - - [§11.6.3.10](expressions.md#116310-lower-bound-inferences) Lower-bound inferences - - [§11.6.3.11](expressions.md#116311-upper-bound-inferences) Upper-bound inferences - - [§11.6.3.12](expressions.md#116312-fixing) Fixing - - [§11.6.3.13](expressions.md#116313-inferred-return-type) Inferred return type - - [§11.6.3.14](expressions.md#116314-type-inference-for-conversion-of-method-groups) Type inference for conversion of method groups - - [§11.6.3.15](expressions.md#116315-finding-the-best-common-type-of-a-set-of-expressions) Finding the best common type of a set of expressions - - [§11.6.4](expressions.md#1164-overload-resolution) Overload resolution - - [§11.6.4.1](expressions.md#11641-general) General - - [§11.6.4.2](expressions.md#11642-applicable-function-member) Applicable function member - - [§11.6.4.3](expressions.md#11643-better-function-member) Better function member - - [§11.6.4.4](expressions.md#11644-better-conversion-from-expression) Better conversion from expression - - [§11.6.4.5](expressions.md#11645-exactly-matching-expression) Exactly matching expression - - [§11.6.4.6](expressions.md#11646-better-conversion-target) Better conversion target - - [§11.6.4.7](expressions.md#11647-overloading-in-generic-classes) Overloading in generic classes - - [§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation) Compile-time checking of dynamic member invocation - - [§11.6.6](expressions.md#1166-function-member-invocation) Function member invocation - - [§11.6.6.1](expressions.md#11661-general) General - - [§11.6.6.2](expressions.md#11662-invocations-on-boxed-instances) Invocations on boxed instances - - [§11.7](expressions.md#117-primary-expressions) Primary expressions - - [§11.7.1](expressions.md#1171-general) General - - [§11.7.2](expressions.md#1172-literals) Literals - - [§11.7.3](expressions.md#1173-interpolated-string-expressions) Interpolated string expressions - - [§11.7.4](expressions.md#1174-simple-names) Simple names - - [§11.7.5](expressions.md#1175-parenthesized-expressions) Parenthesized expressions - - [§11.7.6](expressions.md#1176-member-access) Member access - - [§11.7.6.1](expressions.md#11761-general) General - - [§11.7.6.2](expressions.md#11762-identical-simple-names-and-type-names) Identical simple names and type names - - [§11.7.7](expressions.md#1177-null-conditional-member-access) Null Conditional Member Access - - [§11.7.8](expressions.md#1178-invocation-expressions) Invocation expressions - - [§11.7.8.1](expressions.md#11781-general) General - - [§11.7.8.2](expressions.md#11782-method-invocations) Method invocations - - [§11.7.8.3](expressions.md#11783-extension-method-invocations) Extension method invocations - - [§11.7.8.4](expressions.md#11784-delegate-invocations) Delegate invocations - - [§11.7.9](expressions.md#1179-null-conditional-invocation-expression) Null Conditional Invocation Expression - - [§11.7.10](expressions.md#11710-element-access) Element access - - [§11.7.10.1](expressions.md#117101-general) General - - [§11.7.10.2](expressions.md#117102-array-access) Array access - - [§11.7.10.3](expressions.md#117103-indexer-access) Indexer access - - [§11.7.11](expressions.md#11711-null-conditional-element-access) Null Conditional Element Access - - [§11.7.12](expressions.md#11712-this-access) This access - - [§11.7.13](expressions.md#11713-base-access) Base access - - [§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators) Postfix increment and decrement operators - - [§11.7.15](expressions.md#11715-the-new-operator) The new operator - - [§11.7.15.1](expressions.md#117151-general) General - - [§11.7.15.2](expressions.md#117152-object-creation-expressions) Object creation expressions - - [§11.7.15.3](expressions.md#117153-object-initializers) Object initializers - - [§11.7.15.4](expressions.md#117154-collection-initializers) Collection initializers - - [§11.7.15.5](expressions.md#117155-array-creation-expressions) Array creation expressions - - [§11.7.15.6](expressions.md#117156-delegate-creation-expressions) Delegate creation expressions - - [§11.7.15.7](expressions.md#117157-anonymous-object-creation-expressions) Anonymous object creation expressions - - [§11.7.16](expressions.md#11716-the-typeof-operator) The typeof operator - - [§11.7.17](expressions.md#11717-the-sizeof-operator) The sizeof operator - - [§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators) The checked and unchecked operators - - [§11.7.19](expressions.md#11719-default-value-expressions) Default value expressions - - [§11.7.20](expressions.md#11720-nameof-expressions) Nameof expressions - - [§11.7.21](expressions.md#11721-anonymous-method-expressions) Anonymous method expressions - - [§11.8](expressions.md#118-unary-operators) Unary operators - - [§11.8.1](expressions.md#1181-general) General - - [§11.8.2](expressions.md#1182-unary-plus-operator) Unary plus operator - - [§11.8.3](expressions.md#1183-unary-minus-operator) Unary minus operator - - [§11.8.4](expressions.md#1184-logical-negation-operator) Logical negation operator - - [§11.8.5](expressions.md#1185-bitwise-complement-operator) Bitwise complement operator - - [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators) Prefix increment and decrement operators - - [§11.8.7](expressions.md#1187-cast-expressions) Cast expressions - - [§11.8.8](expressions.md#1188-await-expressions) Await expressions - - [§11.8.8.1](expressions.md#11881-general) General - - [§11.8.8.2](expressions.md#11882-awaitable-expressions) Awaitable expressions - - [§11.8.8.3](expressions.md#11883-classification-of-await-expressions) Classification of await expressions - - [§11.8.8.4](expressions.md#11884-run-time-evaluation-of-await-expressions) Run-time evaluation of await expressions - - [§11.9](expressions.md#119-arithmetic-operators) Arithmetic operators - - [§11.9.1](expressions.md#1191-general) General - - [§11.9.2](expressions.md#1192-multiplication-operator) Multiplication operator - - [§11.9.3](expressions.md#1193-division-operator) Division operator - - [§11.9.4](expressions.md#1194-remainder-operator) Remainder operator - - [§11.9.5](expressions.md#1195-addition-operator) Addition operator - - [§11.9.6](expressions.md#1196-subtraction-operator) Subtraction operator - - [§11.10](expressions.md#1110-shift-operators) Shift operators - - [§11.11](expressions.md#1111-relational-and-type-testing-operators) Relational and type-testing operators - - [§11.11.1](expressions.md#11111-general) General - - [§11.11.2](expressions.md#11112-integer-comparison-operators) Integer comparison operators - - [§11.11.3](expressions.md#11113-floating-point-comparison-operators) Floating-point comparison operators - - [§11.11.4](expressions.md#11114-decimal-comparison-operators) Decimal comparison operators - - [§11.11.5](expressions.md#11115-boolean-equality-operators) Boolean equality operators - - [§11.11.6](expressions.md#11116-enumeration-comparison-operators) Enumeration comparison operators - - [§11.11.7](expressions.md#11117-reference-type-equality-operators) Reference type equality operators - - [§11.11.8](expressions.md#11118-string-equality-operators) String equality operators - - [§11.11.9](expressions.md#11119-delegate-equality-operators) Delegate equality operators - - [§11.11.10](expressions.md#111110-equality-operators-between-nullable-value-types-and-the-null-literal) Equality operators between nullable value types and the null literal - - [§11.11.11](expressions.md#111111-the-is-operator) The is operator - - [§11.11.12](expressions.md#111112-the-as-operator) The as operator - - [§11.12](expressions.md#1112-logical-operators) Logical operators - - [§11.12.1](expressions.md#11121-general) General - - [§11.12.2](expressions.md#11122-integer-logical-operators) Integer logical operators - - [§11.12.3](expressions.md#11123-enumeration-logical-operators) Enumeration logical operators - - [§11.12.4](expressions.md#11124-boolean-logical-operators) Boolean logical operators - - [§11.12.5](expressions.md#11125-nullable-boolean--and--operators) Nullable Boolean & and | operators - - [§11.13](expressions.md#1113-conditional-logical-operators) Conditional logical operators - - [§11.13.1](expressions.md#11131-general) General - - [§11.13.2](expressions.md#11132-boolean-conditional-logical-operators) Boolean conditional logical operators - - [§11.13.3](expressions.md#11133-user-defined-conditional-logical-operators) User-defined conditional logical operators - - [§11.14](expressions.md#1114-the-null-coalescing-operator) The null coalescing operator - - [§11.15](expressions.md#1115-the-throw-expression-operator) The throw expression operator - - [§11.16](expressions.md#1116-conditional-operator) Conditional operator - - [§11.17](expressions.md#1117-anonymous-function-expressions) Anonymous function expressions - - [§11.17.1](expressions.md#11171-general) General - - [§11.17.2](expressions.md#11172-anonymous-function-signatures) Anonymous function signatures - - [§11.17.3](expressions.md#11173-anonymous-function-bodies) Anonymous function bodies - - [§11.17.4](expressions.md#11174-overload-resolution) Overload resolution - - [§11.17.5](expressions.md#11175-anonymous-functions-and-dynamic-binding) Anonymous functions and dynamic binding - - [§11.17.6](expressions.md#11176-outer-variables) Outer variables - - [§11.17.6.1](expressions.md#111761-general) General - - [§11.17.6.2](expressions.md#111762-captured-outer-variables) Captured outer variables - - [§11.17.6.3](expressions.md#111763-instantiation-of-local-variables) Instantiation of local variables - - [§11.17.7](expressions.md#11177-evaluation-of-anonymous-function-expressions) Evaluation of anonymous function expressions - - [§11.17.8](expressions.md#11178-implementation-example) Implementation Example - - [§11.18](expressions.md#1118-query-expressions) Query expressions - - [§11.18.1](expressions.md#11181-general) General - - [§11.18.2](expressions.md#11182-ambiguities-in-query-expressions) Ambiguities in query expressions - - [§11.18.3](expressions.md#11183-query-expression-translation) Query expression translation - - [§11.18.3.1](expressions.md#111831-general) General - - [§11.18.3.2](expressions.md#111832-select-and-group--by-clauses-with-continuations) select and group … by clauses with continuations - - [§11.18.3.3](expressions.md#111833-explicit-range-variable-types) Explicit range variable types - - [§11.18.3.4](expressions.md#111834-degenerate-query-expressions) Degenerate query expressions - - [§11.18.3.5](expressions.md#111835-from-let-where-join-and-orderby-clauses) From, let, where, join and orderby clauses - - [§11.18.3.6](expressions.md#111836-select-clauses) Select clauses - - [§11.18.3.7](expressions.md#111837-group-clauses) Group clauses - - [§11.18.3.8](expressions.md#111838-transparent-identifiers) Transparent identifiers - - [§11.18.4](expressions.md#11184-the-query-expression-pattern) The query-expression pattern - - [§11.19](expressions.md#1119-assignment-operators) Assignment operators - - [§11.19.1](expressions.md#11191-general) General - - [§11.19.2](expressions.md#11192-simple-assignment) Simple assignment - - [§11.19.3](expressions.md#11193-compound-assignment) Compound assignment - - [§11.19.4](expressions.md#11194-event-assignment) Event assignment - - [§11.20](expressions.md#1120-expression) Expression - - [§11.21](expressions.md#1121-constant-expressions) Constant expressions - - [§11.22](expressions.md#1122-boolean-expressions) Boolean expressions -- [§12](statements.md#12-statements) Statements - - [§12.1](statements.md#121-general) General - - [§12.2](statements.md#122-end-points-and-reachability) End points and reachability - - [§12.3](statements.md#123-blocks) Blocks - - [§12.3.1](statements.md#1231-general) General - - [§12.3.2](statements.md#1232-statement-lists) Statement lists - - [§12.4](statements.md#124-the-empty-statement) The empty statement - - [§12.5](statements.md#125-labeled-statements) Labeled statements - - [§12.6](statements.md#126-declaration-statements) Declaration statements - - [§12.6.1](statements.md#1261-general) General - - [§12.6.2](statements.md#1262-local-variable-declarations) Local variable declarations - - [§12.6.3](statements.md#1263-local-constant-declarations) Local constant declarations - - [§12.6.4](statements.md#1264-local-function-declarations) Local function declarations - - [§12.7](statements.md#127-expression-statements) Expression statements - - [§12.8](statements.md#128-selection-statements) Selection statements - - [§12.8.1](statements.md#1281-general) General - - [§12.8.2](statements.md#1282-the-if-statement) The if statement - - [§12.8.3](statements.md#1283-the-switch-statement) The switch statement - - [§12.9](statements.md#129-iteration-statements) Iteration statements - - [§12.9.1](statements.md#1291-general) General - - [§12.9.2](statements.md#1292-the-while-statement) The while statement - - [§12.9.3](statements.md#1293-the-do-statement) The do statement - - [§12.9.4](statements.md#1294-the-for-statement) The for statement - - [§12.9.5](statements.md#1295-the-foreach-statement) The foreach statement - - [§12.10](statements.md#1210-jump-statements) Jump statements - - [§12.10.1](statements.md#12101-general) General - - [§12.10.2](statements.md#12102-the-break-statement) The break statement - - [§12.10.3](statements.md#12103-the-continue-statement) The continue statement - - [§12.10.4](statements.md#12104-the-goto-statement) The goto statement - - [§12.10.5](statements.md#12105-the-return-statement) The return statement - - [§12.10.6](statements.md#12106-the-throw-statement) The throw statement - - [§12.11](statements.md#1211-the-try-statement) The try statement - - [§12.12](statements.md#1212-the-checked-and-unchecked-statements) The checked and unchecked statements - - [§12.13](statements.md#1213-the-lock-statement) The lock statement - - [§12.14](statements.md#1214-the-using-statement) The using statement - - [§12.15](statements.md#1215-the-yield-statement) The yield statement -- [§13](namespaces.md#13-namespaces) Namespaces - - [§13.1](namespaces.md#131-general) General - - [§13.2](namespaces.md#132-compilation-units) Compilation units - - [§13.3](namespaces.md#133-namespace-declarations) Namespace declarations - - [§13.4](namespaces.md#134-extern-alias-directives) Extern alias directives - - [§13.5](namespaces.md#135-using-directives) Using directives - - [§13.5.1](namespaces.md#1351-general) General - - [§13.5.2](namespaces.md#1352-using-alias-directives) Using alias directives - - [§13.5.3](namespaces.md#1353-using-namespace-directives) Using namespace directives - - [§13.5.4](namespaces.md#1354-using-static-directives) Using static directives - - [§13.6](namespaces.md#136-namespace-member-declarations) Namespace member declarations - - [§13.7](namespaces.md#137-type-declarations) Type declarations - - [§13.8](namespaces.md#138-qualified-alias-member) Qualified alias member - - [§13.8.1](namespaces.md#1381-general) General - - [§13.8.2](namespaces.md#1382-uniqueness-of-aliases) Uniqueness of aliases -- [§14](classes.md#14-classes) Classes - - [§14.1](classes.md#141-general) General - - [§14.2](classes.md#142-class-declarations) Class declarations - - [§14.2.1](classes.md#1421-general) General - - [§14.2.2](classes.md#1422-class-modifiers) Class modifiers - - [§14.2.2.1](classes.md#14221-general) General - - [§14.2.2.2](classes.md#14222-abstract-classes) Abstract classes - - [§14.2.2.3](classes.md#14223-sealed-classes) Sealed classes - - [§14.2.2.4](classes.md#14224-static-classes) Static classes - - [§14.2.2.4.1](classes.md#142241-general) General - - [§14.2.2.4.2](classes.md#142242-referencing-static-class-types) Referencing static class types - - [§14.2.3](classes.md#1423-type-parameters) Type parameters - - [§14.2.4](classes.md#1424-class-base-specification) Class base specification - - [§14.2.4.1](classes.md#14241-general) General - - [§14.2.4.2](classes.md#14242-base-classes) Base classes - - [§14.2.4.3](classes.md#14243-interface-implementations) Interface implementations - - [§14.2.5](classes.md#1425-type-parameter-constraints) Type parameter constraints - - [§14.2.6](classes.md#1426-class-body) Class body - - [§14.2.7](classes.md#1427-partial-declarations) Partial declarations - - [§14.3](classes.md#143-class-members) Class members - - [§14.3.1](classes.md#1431-general) General - - [§14.3.2](classes.md#1432-the-instance-type) The instance type - - [§14.3.3](classes.md#1433-members-of-constructed-types) Members of constructed types - - [§14.3.4](classes.md#1434-inheritance) Inheritance - - [§14.3.5](classes.md#1435-the-new-modifier) The new modifier - - [§14.3.6](classes.md#1436-access-modifiers) Access modifiers - - [§14.3.7](classes.md#1437-constituent-types) Constituent types - - [§14.3.8](classes.md#1438-static-and-instance-members) Static and instance members - - [§14.3.9](classes.md#1439-nested-types) Nested types - - [§14.3.9.1](classes.md#14391-general) General - - [§14.3.9.2](classes.md#14392-fully-qualified-name) Fully qualified name - - [§14.3.9.3](classes.md#14393-declared-accessibility) Declared accessibility - - [§14.3.9.4](classes.md#14394-hiding) Hiding - - [§14.3.9.5](classes.md#14395-this-access) this access - - [§14.3.9.6](classes.md#14396-access-to-private-and-protected-members-of-the-containing-type) Access to private and protected members of the containing type - - [§14.3.9.7](classes.md#14397-nested-types-in-generic-classes) Nested types in generic classes - - [§14.3.10](classes.md#14310-reserved-member-names) Reserved member names - - [§14.3.10.1](classes.md#143101-general) General - - [§14.3.10.2](classes.md#143102-member-names-reserved-for-properties) Member names reserved for properties - - [§14.3.10.3](classes.md#143103-member-names-reserved-for-events) Member names reserved for events - - [§14.3.10.4](classes.md#143104-member-names-reserved-for-indexers) Member names reserved for indexers - - [§14.3.10.5](classes.md#143105-member-names-reserved-for-finalizers) Member names reserved for finalizers - - [§14.4](classes.md#144-constants) Constants - - [§14.5](classes.md#145-fields) Fields - - [§14.5.1](classes.md#1451-general) General - - [§14.5.2](classes.md#1452-static-and-instance-fields) Static and instance fields - - [§14.5.3](classes.md#1453-readonly-fields) Readonly fields - - [§14.5.3.1](classes.md#14531-general) General - - [§14.5.3.2](classes.md#14532-using-static-readonly-fields-for-constants) Using static readonly fields for constants - - [§14.5.3.3](classes.md#14533-versioning-of-constants-and-static-readonly-fields) Versioning of constants and static readonly fields - - [§14.5.4](classes.md#1454-volatile-fields) Volatile fields - - [§14.5.5](classes.md#1455-field-initialization) Field initialization - - [§14.5.6](classes.md#1456-variable-initializers) Variable initializers - - [§14.5.6.1](classes.md#14561-general) General - - [§14.5.6.2](classes.md#14562-static-field-initialization) Static field initialization - - [§14.5.6.3](classes.md#14563-instance-field-initialization) Instance field initialization - - [§14.6](classes.md#146-methods) Methods - - [§14.6.1](classes.md#1461-general) General - - [§14.6.2](classes.md#1462-method-parameters) Method parameters - - [§14.6.2.1](classes.md#14621-general) General - - [§14.6.2.2](classes.md#14622-value-parameters) Value parameters - - [§14.6.2.3](classes.md#14623-reference-parameters) Reference parameters - - [§14.6.2.4](classes.md#14624-output-parameters) Output parameters - - [§14.6.2.5](classes.md#14625-parameter-arrays) Parameter arrays - - [§14.6.3](classes.md#1463-static-and-instance-methods) Static and instance methods - - [§14.6.4](classes.md#1464-virtual-methods) Virtual methods - - [§14.6.5](classes.md#1465-override-methods) Override methods - - [§14.6.6](classes.md#1466-sealed-methods) Sealed methods - - [§14.6.7](classes.md#1467-abstract-methods) Abstract methods - - [§14.6.8](classes.md#1468-external-methods) External methods - - [§14.6.9](classes.md#1469-partial-methods) Partial methods - - [§14.6.10](classes.md#14610-extension-methods) Extension methods - - [§14.6.11](classes.md#14611-method-body) Method body - - [§14.7](classes.md#147-properties) Properties - - [§14.7.1](classes.md#1471-general) General - - [§14.7.2](classes.md#1472-static-and-instance-properties) Static and instance properties - - [§14.7.3](classes.md#1473-accessors) Accessors - - [§14.7.4](classes.md#1474-automatically-implemented-properties) Automatically implemented properties - - [§14.7.5](classes.md#1475-accessibility) Accessibility - - [§14.7.6](classes.md#1476-virtual-sealed-override-and-abstract-accessors) Virtual, sealed, override, and abstract accessors - - [§14.8](classes.md#148-events) Events - - [§14.8.1](classes.md#1481-general) General - - [§14.8.2](classes.md#1482-field-like-events) Field-like events - - [§14.8.3](classes.md#1483-event-accessors) Event accessors - - [§14.8.4](classes.md#1484-static-and-instance-events) Static and instance events - - [§14.8.5](classes.md#1485-virtual-sealed-override-and-abstract-accessors) Virtual, sealed, override, and abstract accessors - - [§14.9](classes.md#149-indexers) Indexers - - [§14.10](classes.md#1410-operators) Operators - - [§14.10.1](classes.md#14101-general) General - - [§14.10.2](classes.md#14102-unary-operators) Unary operators - - [§14.10.3](classes.md#14103-binary-operators) Binary operators - - [§14.10.4](classes.md#14104-conversion-operators) Conversion operators - - [§14.11](classes.md#1411-instance-constructors) Instance constructors - - [§14.11.1](classes.md#14111-general) General - - [§14.11.2](classes.md#14112-constructor-initializers) Constructor initializers - - [§14.11.3](classes.md#14113-instance-variable-initializers) Instance variable initializers - - [§14.11.4](classes.md#14114-constructor-execution) Constructor execution - - [§14.11.5](classes.md#14115-default-constructors) Default constructors - - [§14.12](classes.md#1412-static-constructors) Static constructors - - [§14.13](classes.md#1413-finalizers) Finalizers - - [§14.14](classes.md#1414-iterators) Iterators - - [§14.14.1](classes.md#14141-general) General - - [§14.14.2](classes.md#14142-enumerator-interfaces) Enumerator interfaces - - [§14.14.3](classes.md#14143-enumerable-interfaces) Enumerable interfaces - - [§14.14.4](classes.md#14144-yield-type) Yield type - - [§14.14.5](classes.md#14145-enumerator-objects) Enumerator objects - - [§14.14.5.1](classes.md#141451-general) General - - [§14.14.5.2](classes.md#141452-the-movenext-method) The MoveNext method - - [§14.14.5.3](classes.md#141453-the-current-property) The Current property - - [§14.14.5.4](classes.md#141454-the-dispose-method) The Dispose method - - [§14.14.6](classes.md#14146-enumerable-objects) Enumerable objects - - [§14.14.6.1](classes.md#141461-general) General - - [§14.14.6.2](classes.md#141462-the-getenumerator-method) The GetEnumerator method - - [§14.15](classes.md#1415-async-functions) Async Functions - - [§14.15.1](classes.md#14151-general) General - - [§14.15.2](classes.md#14152-evaluation-of-a-task-returning-async-function) Evaluation of a task-returning async function - - [§14.15.3](classes.md#14153-evaluation-of-a-void-returning-async-function) Evaluation of a void-returning async function -- [§15](structs.md#15-structs) Structs - - [§15.1](structs.md#151-general) General - - [§15.2](structs.md#152-struct-declarations) Struct declarations - - [§15.2.1](structs.md#1521-general) General - - [§15.2.2](structs.md#1522-struct-modifiers) Struct modifiers - - [§15.2.3](structs.md#1523-partial-modifier) Partial modifier - - [§15.2.4](structs.md#1524-struct-interfaces) Struct interfaces - - [§15.2.5](structs.md#1525-struct-body) Struct body - - [§15.3](structs.md#153-struct-members) Struct members - - [§15.4](structs.md#154-class-and-struct-differences) Class and struct differences - - [§15.4.1](structs.md#1541-general) General - - [§15.4.2](structs.md#1542-value-semantics) Value semantics - - [§15.4.3](structs.md#1543-inheritance) Inheritance - - [§15.4.4](structs.md#1544-assignment) Assignment - - [§15.4.5](structs.md#1545-default-values) Default values - - [§15.4.6](structs.md#1546-boxing-and-unboxing) Boxing and unboxing - - [§15.4.7](structs.md#1547-meaning-of-this) Meaning of this - - [§15.4.8](structs.md#1548-field-initializers) Field initializers - - [§15.4.9](structs.md#1549-constructors) Constructors - - [§15.4.10](structs.md#15410-static-constructors) Static constructors - - [§15.4.11](structs.md#15411-automatically-implemented-properties) Automatically implemented properties -- [§16](arrays.md#16-arrays) Arrays - - [§16.1](arrays.md#161-general) General - - [§16.2](arrays.md#162-array-types) Array types - - [§16.2.1](arrays.md#1621-general) General - - [§16.2.2](arrays.md#1622-the-systemarray-type) The System.Array type - - [§16.2.3](arrays.md#1623-arrays-and-the-generic-collection-interfaces) Arrays and the generic collection interfaces - - [§16.3](arrays.md#163-array-creation) Array creation - - [§16.4](arrays.md#164-array-element-access) Array element access - - [§16.5](arrays.md#165-array-members) Array members - - [§16.6](arrays.md#166-array-covariance) Array covariance - - [§16.7](arrays.md#167-array-initializers) Array initializers -- [§17](interfaces.md#17-interfaces) Interfaces - - [§17.1](interfaces.md#171-general) General - - [§17.2](interfaces.md#172-interface-declarations) Interface declarations - - [§17.2.1](interfaces.md#1721-general) General - - [§17.2.2](interfaces.md#1722-interface-modifiers) Interface modifiers - - [§17.2.3](interfaces.md#1723-variant-type-parameter-lists) Variant type parameter lists - - [§17.2.3.1](interfaces.md#17231-general) General - - [§17.2.3.2](interfaces.md#17232-variance-safety) Variance safety - - [§17.2.3.3](interfaces.md#17233-variance-conversion) Variance conversion - - [§17.2.4](interfaces.md#1724-base-interfaces) Base interfaces - - [§17.3](interfaces.md#173-interface-body) Interface body - - [§17.4](interfaces.md#174-interface-members) Interface members - - [§17.4.1](interfaces.md#1741-general) General - - [§17.4.2](interfaces.md#1742-interface-methods) Interface methods - - [§17.4.3](interfaces.md#1743-interface-properties) Interface properties - - [§17.4.4](interfaces.md#1744-interface-events) Interface events - - [§17.4.5](interfaces.md#1745-interface-indexers) Interface indexers - - [§17.4.6](interfaces.md#1746-interface-member-access) Interface member access - - [§17.5](interfaces.md#175-qualified-interface-member-names) Qualified interface member names - - [§17.6](interfaces.md#176-interface-implementations) Interface implementations - - [§17.6.1](interfaces.md#1761-general) General - - [§17.6.2](interfaces.md#1762-explicit-interface-member-implementations) Explicit interface member implementations - - [§17.6.3](interfaces.md#1763-uniqueness-of-implemented-interfaces) Uniqueness of implemented interfaces - - [§17.6.4](interfaces.md#1764-implementation-of-generic-methods) Implementation of generic methods - - [§17.6.5](interfaces.md#1765-interface-mapping) Interface mapping - - [§17.6.6](interfaces.md#1766-interface-implementation-inheritance) Interface implementation inheritance - - [§17.6.7](interfaces.md#1767-interface-re-implementation) Interface re-implementation - - [§17.6.8](interfaces.md#1768-abstract-classes-and-interfaces) Abstract classes and interfaces -- [§18](enums.md#18-enums) Enums - - [§18.1](enums.md#181-general) General - - [§18.2](enums.md#182-enum-declarations) Enum declarations - - [§18.3](enums.md#183-enum-modifiers) Enum modifiers - - [§18.4](enums.md#184-enum-members) Enum members - - [§18.5](enums.md#185-the-systemenum-type) The System.Enum type - - [§18.6](enums.md#186-enum-values-and-operations) Enum values and operations -- [§19](delegates.md#19-delegates) Delegates - - [§19.1](delegates.md#191-general) General - - [§19.2](delegates.md#192-delegate-declarations) Delegate declarations - - [§19.3](delegates.md#193-delegate-members) Delegate members - - [§19.4](delegates.md#194-delegate-compatibility) Delegate compatibility - - [§19.5](delegates.md#195-delegate-instantiation) Delegate instantiation - - [§19.6](delegates.md#196-delegate-invocation) Delegate invocation -- [§20](exceptions.md#20-exceptions) Exceptions - - [§20.1](exceptions.md#201-general) General - - [§20.2](exceptions.md#202-causes-of-exceptions) Causes of exceptions - - [§20.3](exceptions.md#203-the-systemexception-class) The System.Exception class - - [§20.4](exceptions.md#204-how-exceptions-are-handled) How exceptions are handled - - [§20.5](exceptions.md#205-common-exception-classes) Common exception classes -- [§21](attributes.md#21-attributes) Attributes - - [§21.1](attributes.md#211-general) General - - [§21.2](attributes.md#212-attribute-classes) Attribute classes - - [§21.2.1](attributes.md#2121-general) General - - [§21.2.2](attributes.md#2122-attribute-usage) Attribute usage - - [§21.2.3](attributes.md#2123-positional-and-named-parameters) Positional and named parameters - - [§21.2.4](attributes.md#2124-attribute-parameter-types) Attribute parameter types - - [§21.3](attributes.md#213-attribute-specification) Attribute specification - - [§21.4](attributes.md#214-attribute-instances) Attribute instances - - [§21.4.1](attributes.md#2141-general) General - - [§21.4.2](attributes.md#2142-compilation-of-an-attribute) Compilation of an attribute - - [§21.4.3](attributes.md#2143-run-time-retrieval-of-an-attribute-instance) Run-time retrieval of an attribute instance - - [§21.5](attributes.md#215-reserved-attributes) Reserved attributes - - [§21.5.1](attributes.md#2151-general) General - - [§21.5.2](attributes.md#2152-the-attributeusage-attribute) The AttributeUsage attribute - - [§21.5.3](attributes.md#2153-the-conditional-attribute) The Conditional attribute - - [§21.5.3.1](attributes.md#21531-general) General - - [§21.5.3.2](attributes.md#21532-conditional-methods) Conditional methods - - [§21.5.3.3](attributes.md#21533-conditional-attribute-classes) Conditional attribute classes - - [§21.5.4](attributes.md#2154-the-obsolete-attribute) The Obsolete attribute - - [§21.5.5](attributes.md#2155-caller-info-attributes) Caller-info attributes - - [§21.5.5.1](attributes.md#21551-general) General - - [§21.5.5.2](attributes.md#21552-the-callerlinenumber-attribute) The CallerLineNumber attribute - - [§21.5.5.3](attributes.md#21553-the-callerfilepath-attribute) The CallerFilePath attribute - - [§21.5.5.4](attributes.md#21554-the-callermembername-attribute) The CallerMemberName attribute - - [§21.6](attributes.md#216-attributes-for-interoperation) Attributes for interoperation -- [§22](unsafe-code.md#22-unsafe-code) Unsafe code - - [§22.1](unsafe-code.md#221-general) General - - [§22.2](unsafe-code.md#222-unsafe-contexts) Unsafe contexts - - [§22.3](unsafe-code.md#223-pointer-types) Pointer types - - [§22.4](unsafe-code.md#224-fixed-and-moveable-variables) Fixed and moveable variables - - [§22.5](unsafe-code.md#225-pointer-conversions) Pointer conversions - - [§22.5.1](unsafe-code.md#2251-general) General - - [§22.5.2](unsafe-code.md#2252-pointer-arrays) Pointer arrays - - [§22.6](unsafe-code.md#226-pointers-in-expressions) Pointers in expressions - - [§22.6.1](unsafe-code.md#2261-general) General - - [§22.6.2](unsafe-code.md#2262-pointer-indirection) Pointer indirection - - [§22.6.3](unsafe-code.md#2263-pointer-member-access) Pointer member access - - [§22.6.4](unsafe-code.md#2264-pointer-element-access) Pointer element access - - [§22.6.5](unsafe-code.md#2265-the-address-of-operator) The address-of operator - - [§22.6.6](unsafe-code.md#2266-pointer-increment-and-decrement) Pointer increment and decrement - - [§22.6.7](unsafe-code.md#2267-pointer-arithmetic) Pointer arithmetic - - [§22.6.8](unsafe-code.md#2268-pointer-comparison) Pointer comparison - - [§22.6.9](unsafe-code.md#2269-the-sizeof-operator) The sizeof operator - - [§22.7](unsafe-code.md#227-the-fixed-statement) The fixed statement - - [§22.8](unsafe-code.md#228-fixed-size-buffers) Fixed-size buffers - - [§22.8.1](unsafe-code.md#2281-general) General - - [§22.8.2](unsafe-code.md#2282-fixed-size-buffer-declarations) Fixed-size buffer declarations - - [§22.8.3](unsafe-code.md#2283-fixed-size-buffers-in-expressions) Fixed-size buffers in expressions - - [§22.8.4](unsafe-code.md#2284-definite-assignment-checking) Definite assignment checking - - [§22.9](unsafe-code.md#229-stack-allocation) Stack allocation +- [§11](patterns.md#11-patterns-and-pattern-matching) Patterns and pattern matching + - [§11.1](patterns.md#111-general) General + - [§11.2](patterns.md#112-pattern-forms) Pattern forms + - [§11.2.1](patterns.md#1121-general) General + - [§11.2.2](patterns.md#1122-declaration-pattern) Declaration pattern + - [§11.2.3](patterns.md#1123-constant-pattern) Constant pattern + - [§11.2.4](patterns.md#1124-var-pattern) Var pattern + - [§11.3](patterns.md#113-pattern-subsumption) Pattern subsumption + - [§11.4](patterns.md#114-pattern-exhaustiveness) Pattern exhaustiveness +- [§12](expressions.md#12-expressions) Expressions + - [§12.1](expressions.md#121-general) General + - [§12.2](expressions.md#122-expression-classifications) Expression classifications + - [§12.2.1](expressions.md#1221-general) General + - [§12.2.2](expressions.md#1222-values-of-expressions) Values of expressions + - [§12.3](expressions.md#123-static-and-dynamic-binding) Static and Dynamic Binding + - [§12.3.1](expressions.md#1231-general) General + - [§12.3.2](expressions.md#1232-binding-time) Binding-time + - [§12.3.3](expressions.md#1233-dynamic-binding) Dynamic binding + - [§12.3.4](expressions.md#1234-types-of-subexpressions) Types of subexpressions + - [§12.4](expressions.md#124-operators) Operators + - [§12.4.1](expressions.md#1241-general) General + - [§12.4.2](expressions.md#1242-operator-precedence-and-associativity) Operator precedence and associativity + - [§12.4.3](expressions.md#1243-operator-overloading) Operator overloading + - [§12.4.4](expressions.md#1244-unary-operator-overload-resolution) Unary operator overload resolution + - [§12.4.5](expressions.md#1245-binary-operator-overload-resolution) Binary operator overload resolution + - [§12.4.6](expressions.md#1246-candidate-user-defined-operators) Candidate user-defined operators + - [§12.4.7](expressions.md#1247-numeric-promotions) Numeric promotions + - [§12.4.7.1](expressions.md#12471-general) General + - [§12.4.7.2](expressions.md#12472-unary-numeric-promotions) Unary numeric promotions + - [§12.4.7.3](expressions.md#12473-binary-numeric-promotions) Binary numeric promotions + - [§12.4.8](expressions.md#1248-lifted-operators) Lifted operators + - [§12.5](expressions.md#125-member-lookup) Member lookup + - [§12.5.1](expressions.md#1251-general) General + - [§12.5.2](expressions.md#1252-base-types) Base types + - [§12.6](expressions.md#126-function-members) Function members + - [§12.6.1](expressions.md#1261-general) General + - [§12.6.2](expressions.md#1262-argument-lists) Argument lists + - [§12.6.2.1](expressions.md#12621-general) General + - [§12.6.2.2](expressions.md#12622-corresponding-parameters) Corresponding parameters + - [§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists) Run-time evaluation of argument lists + - [§12.6.3](expressions.md#1263-type-inference) Type inference + - [§12.6.3.1](expressions.md#12631-general) General + - [§12.6.3.2](expressions.md#12632-the-first-phase) The first phase + - [§12.6.3.3](expressions.md#12633-the-second-phase) The second phase + - [§12.6.3.4](expressions.md#12634-input-types) Input types + - [§12.6.3.5](expressions.md#12635-output-types) Output types + - [§12.6.3.6](expressions.md#12636-dependence) Dependence + - [§12.6.3.7](expressions.md#12637-output-type-inferences) Output type inferences + - [§12.6.3.8](expressions.md#12638-explicit-parameter-type-inferences) Explicit parameter type inferences + - [§12.6.3.9](expressions.md#12639-exact-inferences) Exact inferences + - [§12.6.3.10](expressions.md#126310-lower-bound-inferences) Lower-bound inferences + - [§12.6.3.11](expressions.md#126311-upper-bound-inferences) Upper-bound inferences + - [§12.6.3.12](expressions.md#126312-fixing) Fixing + - [§12.6.3.13](expressions.md#126313-inferred-return-type) Inferred return type + - [§12.6.3.14](expressions.md#126314-type-inference-for-conversion-of-method-groups) Type inference for conversion of method groups + - [§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions) Finding the best common type of a set of expressions + - [§12.6.4](expressions.md#1264-overload-resolution) Overload resolution + - [§12.6.4.1](expressions.md#12641-general) General + - [§12.6.4.2](expressions.md#12642-applicable-function-member) Applicable function member + - [§12.6.4.3](expressions.md#12643-better-function-member) Better function member + - [§12.6.4.4](expressions.md#12644-better-parameter-passing-mode) Better parameter-passing mode + - [§12.6.4.5](expressions.md#12645-better-conversion-from-expression) Better conversion from expression + - [§12.6.4.6](expressions.md#12646-exactly-matching-expression) Exactly matching expression + - [§12.6.4.7](expressions.md#12647-better-conversion-target) Better conversion target + - [§12.6.4.8](expressions.md#12648-overloading-in-generic-classes) Overloading in generic classes + - [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation) Compile-time checking of dynamic member invocation + - [§12.6.6](expressions.md#1266-function-member-invocation) Function member invocation + - [§12.6.6.1](expressions.md#12661-general) General + - [§12.6.6.2](expressions.md#12662-invocations-on-boxed-instances) Invocations on boxed instances + - [§12.7](expressions.md#127-deconstruction) Deconstruction + - [§12.8](expressions.md#128-primary-expressions) Primary expressions + - [§12.8.1](expressions.md#1281-general) General + - [§12.8.2](expressions.md#1282-literals) Literals + - [§12.8.3](expressions.md#1283-interpolated-string-expressions) Interpolated string expressions + - [§12.8.4](expressions.md#1284-simple-names) Simple names + - [§12.8.5](expressions.md#1285-parenthesized-expressions) Parenthesized expressions + - [§12.8.6](expressions.md#1286-tuple-expressions) Tuple expressions + - [§12.8.7](expressions.md#1287-member-access) Member access + - [§12.8.7.1](expressions.md#12871-general) General + - [§12.8.7.2](expressions.md#12872-identical-simple-names-and-type-names) Identical simple names and type names + - [§12.8.8](expressions.md#1288-null-conditional-member-access) Null Conditional Member Access + - [§12.8.9](expressions.md#1289-invocation-expressions) Invocation expressions + - [§12.8.9.1](expressions.md#12891-general) General + - [§12.8.9.2](expressions.md#12892-method-invocations) Method invocations + - [§12.8.9.3](expressions.md#12893-extension-method-invocations) Extension method invocations + - [§12.8.9.4](expressions.md#12894-delegate-invocations) Delegate invocations + - [§12.8.10](expressions.md#12810-null-conditional-invocation-expression) Null Conditional Invocation Expression + - [§12.8.11](expressions.md#12811-element-access) Element access + - [§12.8.11.1](expressions.md#128111-general) General + - [§12.8.11.2](expressions.md#128112-array-access) Array access + - [§12.8.11.3](expressions.md#128113-indexer-access) Indexer access + - [§12.8.12](expressions.md#12812-null-conditional-element-access) Null Conditional Element Access + - [§12.8.13](expressions.md#12813-this-access) This access + - [§12.8.14](expressions.md#12814-base-access) Base access + - [§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators) Postfix increment and decrement operators + - [§12.8.16](expressions.md#12816-the-new-operator) The new operator + - [§12.8.16.1](expressions.md#128161-general) General + - [§12.8.16.2](expressions.md#128162-object-creation-expressions) Object creation expressions + - [§12.8.16.3](expressions.md#128163-object-initializers) Object initializers + - [§12.8.16.4](expressions.md#128164-collection-initializers) Collection initializers + - [§12.8.16.5](expressions.md#128165-array-creation-expressions) Array creation expressions + - [§12.8.16.6](expressions.md#128166-delegate-creation-expressions) Delegate creation expressions + - [§12.8.16.7](expressions.md#128167-anonymous-object-creation-expressions) Anonymous object creation expressions + - [§12.8.17](expressions.md#12817-the-typeof-operator) The typeof operator + - [§12.8.18](expressions.md#12818-the-sizeof-operator) The sizeof operator + - [§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators) The checked and unchecked operators + - [§12.8.20](expressions.md#12820-default-value-expressions) Default value expressions + - [§12.8.21](expressions.md#12821-stack-allocation) Stack allocation + - [§12.8.22](expressions.md#12822-nameof-expressions) Nameof expressions + - [§12.8.23](expressions.md#12823-anonymous-method-expressions) Anonymous method expressions + - [§12.9](expressions.md#129-unary-operators) Unary operators + - [§12.9.1](expressions.md#1291-general) General + - [§12.9.2](expressions.md#1292-unary-plus-operator) Unary plus operator + - [§12.9.3](expressions.md#1293-unary-minus-operator) Unary minus operator + - [§12.9.4](expressions.md#1294-logical-negation-operator) Logical negation operator + - [§12.9.5](expressions.md#1295-bitwise-complement-operator) Bitwise complement operator + - [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators) Prefix increment and decrement operators + - [§12.9.7](expressions.md#1297-cast-expressions) Cast expressions + - [§12.9.8](expressions.md#1298-await-expressions) Await expressions + - [§12.9.8.1](expressions.md#12981-general) General + - [§12.9.8.2](expressions.md#12982-awaitable-expressions) Awaitable expressions + - [§12.9.8.3](expressions.md#12983-classification-of-await-expressions) Classification of await expressions + - [§12.9.8.4](expressions.md#12984-run-time-evaluation-of-await-expressions) Run-time evaluation of await expressions + - [§12.10](expressions.md#1210-arithmetic-operators) Arithmetic operators + - [§12.10.1](expressions.md#12101-general) General + - [§12.10.2](expressions.md#12102-multiplication-operator) Multiplication operator + - [§12.10.3](expressions.md#12103-division-operator) Division operator + - [§12.10.4](expressions.md#12104-remainder-operator) Remainder operator + - [§12.10.5](expressions.md#12105-addition-operator) Addition operator + - [§12.10.6](expressions.md#12106-subtraction-operator) Subtraction operator + - [§12.11](expressions.md#1211-shift-operators) Shift operators + - [§12.12](expressions.md#1212-relational-and-type-testing-operators) Relational and type-testing operators + - [§12.12.1](expressions.md#12121-general) General + - [§12.12.2](expressions.md#12122-integer-comparison-operators) Integer comparison operators + - [§12.12.3](expressions.md#12123-floating-point-comparison-operators) Floating-point comparison operators + - [§12.12.4](expressions.md#12124-decimal-comparison-operators) Decimal comparison operators + - [§12.12.5](expressions.md#12125-boolean-equality-operators) Boolean equality operators + - [§12.12.6](expressions.md#12126-enumeration-comparison-operators) Enumeration comparison operators + - [§12.12.7](expressions.md#12127-reference-type-equality-operators) Reference type equality operators + - [§12.12.8](expressions.md#12128-string-equality-operators) String equality operators + - [§12.12.9](expressions.md#12129-delegate-equality-operators) Delegate equality operators + - [§12.12.10](expressions.md#121210-equality-operators-between-nullable-value-types-and-the-null-literal) Equality operators between nullable value types and the null literal + - [§12.12.11](expressions.md#121211-tuple-equality-operators) Tuple equality operators + - [§12.12.12](expressions.md#121212-the-is-operator) The is operator + - [§12.12.12.1](expressions.md#1212121-the-is-type-operator) The is-type operator + - [§12.12.12.2](expressions.md#1212122-the-is-pattern-operator) The is-pattern operator + - [§12.12.13](expressions.md#121213-the-as-operator) The as operator + - [§12.13](expressions.md#1213-logical-operators) Logical operators + - [§12.13.1](expressions.md#12131-general) General + - [§12.13.2](expressions.md#12132-integer-logical-operators) Integer logical operators + - [§12.13.3](expressions.md#12133-enumeration-logical-operators) Enumeration logical operators + - [§12.13.4](expressions.md#12134-boolean-logical-operators) Boolean logical operators + - [§12.13.5](expressions.md#12135-nullable-boolean--and--operators) Nullable Boolean & and | operators + - [§12.14](expressions.md#1214-conditional-logical-operators) Conditional logical operators + - [§12.14.1](expressions.md#12141-general) General + - [§12.14.2](expressions.md#12142-boolean-conditional-logical-operators) Boolean conditional logical operators + - [§12.14.3](expressions.md#12143-user-defined-conditional-logical-operators) User-defined conditional logical operators + - [§12.15](expressions.md#1215-the-null-coalescing-operator) The null coalescing operator + - [§12.16](expressions.md#1216-the-throw-expression-operator) The throw expression operator + - [§12.17](expressions.md#1217-declaration-expressions) Declaration expressions + - [§12.18](expressions.md#1218-conditional-operator) Conditional operator + - [§12.19](expressions.md#1219-anonymous-function-expressions) Anonymous function expressions + - [§12.19.1](expressions.md#12191-general) General + - [§12.19.2](expressions.md#12192-anonymous-function-signatures) Anonymous function signatures + - [§12.19.3](expressions.md#12193-anonymous-function-bodies) Anonymous function bodies + - [§12.19.4](expressions.md#12194-overload-resolution) Overload resolution + - [§12.19.5](expressions.md#12195-anonymous-functions-and-dynamic-binding) Anonymous functions and dynamic binding + - [§12.19.6](expressions.md#12196-outer-variables) Outer variables + - [§12.19.6.1](expressions.md#121961-general) General + - [§12.19.6.2](expressions.md#121962-captured-outer-variables) Captured outer variables + - [§12.19.6.3](expressions.md#121963-instantiation-of-local-variables) Instantiation of local variables + - [§12.19.7](expressions.md#12197-evaluation-of-anonymous-function-expressions) Evaluation of anonymous function expressions + - [§12.19.8](expressions.md#12198-implementation-example) Implementation Example + - [§12.20](expressions.md#1220-query-expressions) Query expressions + - [§12.20.1](expressions.md#12201-general) General + - [§12.20.2](expressions.md#12202-ambiguities-in-query-expressions) Ambiguities in query expressions + - [§12.20.3](expressions.md#12203-query-expression-translation) Query expression translation + - [§12.20.3.1](expressions.md#122031-general) General + - [§12.20.3.2](expressions.md#122032-query-expressions-with-continuations) Query expressions with continuations + - [§12.20.3.3](expressions.md#122033-explicit-range-variable-types) Explicit range variable types + - [§12.20.3.4](expressions.md#122034-degenerate-query-expressions) Degenerate query expressions + - [§12.20.3.5](expressions.md#122035-from-let-where-join-and-orderby-clauses) From, let, where, join and orderby clauses + - [§12.20.3.6](expressions.md#122036-select-clauses) Select clauses + - [§12.20.3.7](expressions.md#122037-group-clauses) Group clauses + - [§12.20.3.8](expressions.md#122038-transparent-identifiers) Transparent identifiers + - [§12.20.4](expressions.md#12204-the-query-expression-pattern) The query-expression pattern + - [§12.21](expressions.md#1221-assignment-operators) Assignment operators + - [§12.21.1](expressions.md#12211-general) General + - [§12.21.2](expressions.md#12212-simple-assignment) Simple assignment + - [§12.21.3](expressions.md#12213-ref-assignment) Ref assignment + - [§12.21.4](expressions.md#12214-compound-assignment) Compound assignment + - [§12.21.5](expressions.md#12215-event-assignment) Event assignment + - [§12.22](expressions.md#1222-expression) Expression + - [§12.23](expressions.md#1223-constant-expressions) Constant expressions + - [§12.24](expressions.md#1224-boolean-expressions) Boolean expressions +- [§13](statements.md#13-statements) Statements + - [§13.1](statements.md#131-general) General + - [§13.2](statements.md#132-end-points-and-reachability) End points and reachability + - [§13.3](statements.md#133-blocks) Blocks + - [§13.3.1](statements.md#1331-general) General + - [§13.3.2](statements.md#1332-statement-lists) Statement lists + - [§13.4](statements.md#134-the-empty-statement) The empty statement + - [§13.5](statements.md#135-labeled-statements) Labeled statements + - [§13.6](statements.md#136-declaration-statements) Declaration statements + - [§13.6.1](statements.md#1361-general) General + - [§13.6.2](statements.md#1362-local-variable-declarations) Local variable declarations + - [§13.6.2.1](statements.md#13621-implicitly-typed-local-variable-declarations) Implicitly typed local variable declarations + - [§13.6.2.2](statements.md#13622-explicitly-typed-local-variable-declarations) Explicitly typed local variable declarations + - [§13.6.2.3](statements.md#13623-ref-local-variable-declarations) Ref local variable declarations + - [§13.6.3](statements.md#1363-local-constant-declarations) Local constant declarations + - [§13.6.4](statements.md#1364-local-function-declarations) Local function declarations + - [§13.7](statements.md#137-expression-statements) Expression statements + - [§13.8](statements.md#138-selection-statements) Selection statements + - [§13.8.1](statements.md#1381-general) General + - [§13.8.2](statements.md#1382-the-if-statement) The if statement + - [§13.8.3](statements.md#1383-the-switch-statement) The switch statement + - [§13.9](statements.md#139-iteration-statements) Iteration statements + - [§13.9.1](statements.md#1391-general) General + - [§13.9.2](statements.md#1392-the-while-statement) The while statement + - [§13.9.3](statements.md#1393-the-do-statement) The do statement + - [§13.9.4](statements.md#1394-the-for-statement) The for statement + - [§13.9.5](statements.md#1395-the-foreach-statement) The foreach statement + - [§13.10](statements.md#1310-jump-statements) Jump statements + - [§13.10.1](statements.md#13101-general) General + - [§13.10.2](statements.md#13102-the-break-statement) The break statement + - [§13.10.3](statements.md#13103-the-continue-statement) The continue statement + - [§13.10.4](statements.md#13104-the-goto-statement) The goto statement + - [§13.10.5](statements.md#13105-the-return-statement) The return statement + - [§13.10.6](statements.md#13106-the-throw-statement) The throw statement + - [§13.11](statements.md#1311-the-try-statement) The try statement + - [§13.12](statements.md#1312-the-checked-and-unchecked-statements) The checked and unchecked statements + - [§13.13](statements.md#1313-the-lock-statement) The lock statement + - [§13.14](statements.md#1314-the-using-statement) The using statement + - [§13.15](statements.md#1315-the-yield-statement) The yield statement +- [§14](namespaces.md#14-namespaces) Namespaces + - [§14.1](namespaces.md#141-general) General + - [§14.2](namespaces.md#142-compilation-units) Compilation units + - [§14.3](namespaces.md#143-namespace-declarations) Namespace declarations + - [§14.4](namespaces.md#144-extern-alias-directives) Extern alias directives + - [§14.5](namespaces.md#145-using-directives) Using directives + - [§14.5.1](namespaces.md#1451-general) General + - [§14.5.2](namespaces.md#1452-using-alias-directives) Using alias directives + - [§14.5.3](namespaces.md#1453-using-namespace-directives) Using namespace directives + - [§14.5.4](namespaces.md#1454-using-static-directives) Using static directives + - [§14.6](namespaces.md#146-namespace-member-declarations) Namespace member declarations + - [§14.7](namespaces.md#147-type-declarations) Type declarations + - [§14.8](namespaces.md#148-qualified-alias-member) Qualified alias member + - [§14.8.1](namespaces.md#1481-general) General + - [§14.8.2](namespaces.md#1482-uniqueness-of-aliases) Uniqueness of aliases +- [§15](classes.md#15-classes) Classes + - [§15.1](classes.md#151-general) General + - [§15.2](classes.md#152-class-declarations) Class declarations + - [§15.2.1](classes.md#1521-general) General + - [§15.2.2](classes.md#1522-class-modifiers) Class modifiers + - [§15.2.2.1](classes.md#15221-general) General + - [§15.2.2.2](classes.md#15222-abstract-classes) Abstract classes + - [§15.2.2.3](classes.md#15223-sealed-classes) Sealed classes + - [§15.2.2.4](classes.md#15224-static-classes) Static classes + - [§15.2.2.4.1](classes.md#152241-general) General + - [§15.2.2.4.2](classes.md#152242-referencing-static-class-types) Referencing static class types + - [§15.2.3](classes.md#1523-type-parameters) Type parameters + - [§15.2.4](classes.md#1524-class-base-specification) Class base specification + - [§15.2.4.1](classes.md#15241-general) General + - [§15.2.4.2](classes.md#15242-base-classes) Base classes + - [§15.2.4.3](classes.md#15243-interface-implementations) Interface implementations + - [§15.2.5](classes.md#1525-type-parameter-constraints) Type parameter constraints + - [§15.2.6](classes.md#1526-class-body) Class body + - [§15.2.7](classes.md#1527-partial-declarations) Partial declarations + - [§15.3](classes.md#153-class-members) Class members + - [§15.3.1](classes.md#1531-general) General + - [§15.3.2](classes.md#1532-the-instance-type) The instance type + - [§15.3.3](classes.md#1533-members-of-constructed-types) Members of constructed types + - [§15.3.4](classes.md#1534-inheritance) Inheritance + - [§15.3.5](classes.md#1535-the-new-modifier) The new modifier + - [§15.3.6](classes.md#1536-access-modifiers) Access modifiers + - [§15.3.7](classes.md#1537-constituent-types) Constituent types + - [§15.3.8](classes.md#1538-static-and-instance-members) Static and instance members + - [§15.3.9](classes.md#1539-nested-types) Nested types + - [§15.3.9.1](classes.md#15391-general) General + - [§15.3.9.2](classes.md#15392-fully-qualified-name) Fully qualified name + - [§15.3.9.3](classes.md#15393-declared-accessibility) Declared accessibility + - [§15.3.9.4](classes.md#15394-hiding) Hiding + - [§15.3.9.5](classes.md#15395-this-access) this access + - [§15.3.9.6](classes.md#15396-access-to-private-and-protected-members-of-the-containing-type) Access to private and protected members of the containing type + - [§15.3.9.7](classes.md#15397-nested-types-in-generic-classes) Nested types in generic classes + - [§15.3.10](classes.md#15310-reserved-member-names) Reserved member names + - [§15.3.10.1](classes.md#153101-general) General + - [§15.3.10.2](classes.md#153102-member-names-reserved-for-properties) Member names reserved for properties + - [§15.3.10.3](classes.md#153103-member-names-reserved-for-events) Member names reserved for events + - [§15.3.10.4](classes.md#153104-member-names-reserved-for-indexers) Member names reserved for indexers + - [§15.3.10.5](classes.md#153105-member-names-reserved-for-finalizers) Member names reserved for finalizers + - [§15.4](classes.md#154-constants) Constants + - [§15.5](classes.md#155-fields) Fields + - [§15.5.1](classes.md#1551-general) General + - [§15.5.2](classes.md#1552-static-and-instance-fields) Static and instance fields + - [§15.5.3](classes.md#1553-readonly-fields) Readonly fields + - [§15.5.3.1](classes.md#15531-general) General + - [§15.5.3.2](classes.md#15532-using-static-readonly-fields-for-constants) Using static readonly fields for constants + - [§15.5.3.3](classes.md#15533-versioning-of-constants-and-static-readonly-fields) Versioning of constants and static readonly fields + - [§15.5.4](classes.md#1554-volatile-fields) Volatile fields + - [§15.5.5](classes.md#1555-field-initialization) Field initialization + - [§15.5.6](classes.md#1556-variable-initializers) Variable initializers + - [§15.5.6.1](classes.md#15561-general) General + - [§15.5.6.2](classes.md#15562-static-field-initialization) Static field initialization + - [§15.5.6.3](classes.md#15563-instance-field-initialization) Instance field initialization + - [§15.6](classes.md#156-methods) Methods + - [§15.6.1](classes.md#1561-general) General + - [§15.6.2](classes.md#1562-method-parameters) Method parameters + - [§15.6.2.1](classes.md#15621-general) General + - [§15.6.2.2](classes.md#15622-value-parameters) Value parameters + - [§15.6.2.3](classes.md#15623-input-parameters) Input parameters + - [§15.6.2.4](classes.md#15624-reference-parameters) Reference parameters + - [§15.6.2.5](classes.md#15625-output-parameters) Output parameters + - [§15.6.2.6](classes.md#15626-parameter-arrays) Parameter arrays + - [§15.6.3](classes.md#1563-static-and-instance-methods) Static and instance methods + - [§15.6.4](classes.md#1564-virtual-methods) Virtual methods + - [§15.6.5](classes.md#1565-override-methods) Override methods + - [§15.6.6](classes.md#1566-sealed-methods) Sealed methods + - [§15.6.7](classes.md#1567-abstract-methods) Abstract methods + - [§15.6.8](classes.md#1568-external-methods) External methods + - [§15.6.9](classes.md#1569-partial-methods) Partial methods + - [§15.6.10](classes.md#15610-extension-methods) Extension methods + - [§15.6.11](classes.md#15611-method-body) Method body + - [§15.7](classes.md#157-properties) Properties + - [§15.7.1](classes.md#1571-general) General + - [§15.7.2](classes.md#1572-static-and-instance-properties) Static and instance properties + - [§15.7.3](classes.md#1573-accessors) Accessors + - [§15.7.4](classes.md#1574-automatically-implemented-properties) Automatically implemented properties + - [§15.7.5](classes.md#1575-accessibility) Accessibility + - [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors) Virtual, sealed, override, and abstract accessors + - [§15.8](classes.md#158-events) Events + - [§15.8.1](classes.md#1581-general) General + - [§15.8.2](classes.md#1582-field-like-events) Field-like events + - [§15.8.3](classes.md#1583-event-accessors) Event accessors + - [§15.8.4](classes.md#1584-static-and-instance-events) Static and instance events + - [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors) Virtual, sealed, override, and abstract accessors + - [§15.9](classes.md#159-indexers) Indexers + - [§15.9.1](classes.md#1591-general) General + - [§15.9.2](classes.md#1592-indexer-and-property-differences) Indexer and Property Differences + - [§15.10](classes.md#1510-operators) Operators + - [§15.10.1](classes.md#15101-general) General + - [§15.10.2](classes.md#15102-unary-operators) Unary operators + - [§15.10.3](classes.md#15103-binary-operators) Binary operators + - [§15.10.4](classes.md#15104-conversion-operators) Conversion operators + - [§15.11](classes.md#1511-instance-constructors) Instance constructors + - [§15.11.1](classes.md#15111-general) General + - [§15.11.2](classes.md#15112-constructor-initializers) Constructor initializers + - [§15.11.3](classes.md#15113-instance-variable-initializers) Instance variable initializers + - [§15.11.4](classes.md#15114-constructor-execution) Constructor execution + - [§15.11.5](classes.md#15115-default-constructors) Default constructors + - [§15.12](classes.md#1512-static-constructors) Static constructors + - [§15.13](classes.md#1513-finalizers) Finalizers + - [§15.14](classes.md#1514-iterators) Iterators + - [§15.14.1](classes.md#15141-general) General + - [§15.14.2](classes.md#15142-enumerator-interfaces) Enumerator interfaces + - [§15.14.3](classes.md#15143-enumerable-interfaces) Enumerable interfaces + - [§15.14.4](classes.md#15144-yield-type) Yield type + - [§15.14.5](classes.md#15145-enumerator-objects) Enumerator objects + - [§15.14.5.1](classes.md#151451-general) General + - [§15.14.5.2](classes.md#151452-the-movenext-method) The MoveNext method + - [§15.14.5.3](classes.md#151453-the-current-property) The Current property + - [§15.14.5.4](classes.md#151454-the-dispose-method) The Dispose method + - [§15.14.6](classes.md#15146-enumerable-objects) Enumerable objects + - [§15.14.6.1](classes.md#151461-general) General + - [§15.14.6.2](classes.md#151462-the-getenumerator-method) The GetEnumerator method + - [§15.15](classes.md#1515-async-functions) Async Functions + - [§15.15.1](classes.md#15151-general) General + - [§15.15.2](classes.md#15152-task-type-builder-pattern) Task-type builder pattern + - [§15.15.3](classes.md#15153-evaluation-of-a-task-returning-async-function) Evaluation of a task-returning async function + - [§15.15.4](classes.md#15154-evaluation-of-a-void-returning-async-function) Evaluation of a void-returning async function +- [§16](structs.md#16-structs) Structs + - [§16.1](structs.md#161-general) General + - [§16.2](structs.md#162-struct-declarations) Struct declarations + - [§16.2.1](structs.md#1621-general) General + - [§16.2.2](structs.md#1622-struct-modifiers) Struct modifiers + - [§16.2.3](structs.md#1623-ref-modifier) Ref modifier + - [§16.2.4](structs.md#1624-partial-modifier) Partial modifier + - [§16.2.5](structs.md#1625-struct-interfaces) Struct interfaces + - [§16.2.6](structs.md#1626-struct-body) Struct body + - [§16.3](structs.md#163-struct-members) Struct members + - [§16.4](structs.md#164-class-and-struct-differences) Class and struct differences + - [§16.4.1](structs.md#1641-general) General + - [§16.4.2](structs.md#1642-value-semantics) Value semantics + - [§16.4.3](structs.md#1643-inheritance) Inheritance + - [§16.4.4](structs.md#1644-assignment) Assignment + - [§16.4.5](structs.md#1645-default-values) Default values + - [§16.4.6](structs.md#1646-boxing-and-unboxing) Boxing and unboxing + - [§16.4.7](structs.md#1647-meaning-of-this) Meaning of this + - [§16.4.8](structs.md#1648-field-initializers) Field initializers + - [§16.4.9](structs.md#1649-constructors) Constructors + - [§16.4.10](structs.md#16410-static-constructors) Static constructors + - [§16.4.11](structs.md#16411-automatically-implemented-properties) Automatically implemented properties + - [§16.4.12](structs.md#16412-safe-context-constraint) Safe context constraint + - [§16.4.12.1](structs.md#164121-general) General + - [§16.4.12.2](structs.md#164122-parameter-safe-context) Parameter safe context + - [§16.4.12.3](structs.md#164123-local-variable-safe-context) Local variable safe context + - [§16.4.12.4](structs.md#164124-field-safe-context) Field safe context + - [§16.4.12.5](structs.md#164125-operators) Operators + - [§16.4.12.6](structs.md#164126-method-and-property-invocation) Method and property invocation + - [§16.4.12.7](structs.md#164127-stackalloc) stackalloc + - [§16.4.12.8](structs.md#164128-constructor-invocations) Constructor invocations +- [§17](arrays.md#17-arrays) Arrays + - [§17.1](arrays.md#171-general) General + - [§17.2](arrays.md#172-array-types) Array types + - [§17.2.1](arrays.md#1721-general) General + - [§17.2.2](arrays.md#1722-the-systemarray-type) The System.Array type + - [§17.2.3](arrays.md#1723-arrays-and-the-generic-collection-interfaces) Arrays and the generic collection interfaces + - [§17.3](arrays.md#173-array-creation) Array creation + - [§17.4](arrays.md#174-array-element-access) Array element access + - [§17.5](arrays.md#175-array-members) Array members + - [§17.6](arrays.md#176-array-covariance) Array covariance + - [§17.7](arrays.md#177-array-initializers) Array initializers +- [§18](interfaces.md#18-interfaces) Interfaces + - [§18.1](interfaces.md#181-general) General + - [§18.2](interfaces.md#182-interface-declarations) Interface declarations + - [§18.2.1](interfaces.md#1821-general) General + - [§18.2.2](interfaces.md#1822-interface-modifiers) Interface modifiers + - [§18.2.3](interfaces.md#1823-variant-type-parameter-lists) Variant type parameter lists + - [§18.2.3.1](interfaces.md#18231-general) General + - [§18.2.3.2](interfaces.md#18232-variance-safety) Variance safety + - [§18.2.3.3](interfaces.md#18233-variance-conversion) Variance conversion + - [§18.2.4](interfaces.md#1824-base-interfaces) Base interfaces + - [§18.3](interfaces.md#183-interface-body) Interface body + - [§18.4](interfaces.md#184-interface-members) Interface members + - [§18.4.1](interfaces.md#1841-general) General + - [§18.4.2](interfaces.md#1842-interface-methods) Interface methods + - [§18.4.3](interfaces.md#1843-interface-properties) Interface properties + - [§18.4.4](interfaces.md#1844-interface-events) Interface events + - [§18.4.5](interfaces.md#1845-interface-indexers) Interface indexers + - [§18.4.6](interfaces.md#1846-interface-member-access) Interface member access + - [§18.5](interfaces.md#185-qualified-interface-member-names) Qualified interface member names + - [§18.6](interfaces.md#186-interface-implementations) Interface implementations + - [§18.6.1](interfaces.md#1861-general) General + - [§18.6.2](interfaces.md#1862-explicit-interface-member-implementations) Explicit interface member implementations + - [§18.6.3](interfaces.md#1863-uniqueness-of-implemented-interfaces) Uniqueness of implemented interfaces + - [§18.6.4](interfaces.md#1864-implementation-of-generic-methods) Implementation of generic methods + - [§18.6.5](interfaces.md#1865-interface-mapping) Interface mapping + - [§18.6.6](interfaces.md#1866-interface-implementation-inheritance) Interface implementation inheritance + - [§18.6.7](interfaces.md#1867-interface-re-implementation) Interface re-implementation + - [§18.6.8](interfaces.md#1868-abstract-classes-and-interfaces) Abstract classes and interfaces +- [§19](enums.md#19-enums) Enums + - [§19.1](enums.md#191-general) General + - [§19.2](enums.md#192-enum-declarations) Enum declarations + - [§19.3](enums.md#193-enum-modifiers) Enum modifiers + - [§19.4](enums.md#194-enum-members) Enum members + - [§19.5](enums.md#195-the-systemenum-type) The System.Enum type + - [§19.6](enums.md#196-enum-values-and-operations) Enum values and operations +- [§20](delegates.md#20-delegates) Delegates + - [§20.1](delegates.md#201-general) General + - [§20.2](delegates.md#202-delegate-declarations) Delegate declarations + - [§20.3](delegates.md#203-delegate-members) Delegate members + - [§20.4](delegates.md#204-delegate-compatibility) Delegate compatibility + - [§20.5](delegates.md#205-delegate-instantiation) Delegate instantiation + - [§20.6](delegates.md#206-delegate-invocation) Delegate invocation +- [§21](exceptions.md#21-exceptions) Exceptions + - [§21.1](exceptions.md#211-general) General + - [§21.2](exceptions.md#212-causes-of-exceptions) Causes of exceptions + - [§21.3](exceptions.md#213-the-systemexception-class) The System.Exception class + - [§21.4](exceptions.md#214-how-exceptions-are-handled) How exceptions are handled + - [§21.5](exceptions.md#215-common-exception-classes) Common exception classes +- [§22](attributes.md#22-attributes) Attributes + - [§22.1](attributes.md#221-general) General + - [§22.2](attributes.md#222-attribute-classes) Attribute classes + - [§22.2.1](attributes.md#2221-general) General + - [§22.2.2](attributes.md#2222-attribute-usage) Attribute usage + - [§22.2.3](attributes.md#2223-positional-and-named-parameters) Positional and named parameters + - [§22.2.4](attributes.md#2224-attribute-parameter-types) Attribute parameter types + - [§22.3](attributes.md#223-attribute-specification) Attribute specification + - [§22.4](attributes.md#224-attribute-instances) Attribute instances + - [§22.4.1](attributes.md#2241-general) General + - [§22.4.2](attributes.md#2242-compilation-of-an-attribute) Compilation of an attribute + - [§22.4.3](attributes.md#2243-run-time-retrieval-of-an-attribute-instance) Run-time retrieval of an attribute instance + - [§22.5](attributes.md#225-reserved-attributes) Reserved attributes + - [§22.5.1](attributes.md#2251-general) General + - [§22.5.2](attributes.md#2252-the-attributeusage-attribute) The AttributeUsage attribute + - [§22.5.3](attributes.md#2253-the-conditional-attribute) The Conditional attribute + - [§22.5.3.1](attributes.md#22531-general) General + - [§22.5.3.2](attributes.md#22532-conditional-methods) Conditional methods + - [§22.5.3.3](attributes.md#22533-conditional-attribute-classes) Conditional attribute classes + - [§22.5.4](attributes.md#2254-the-obsolete-attribute) The Obsolete attribute + - [§22.5.5](attributes.md#2255-caller-info-attributes) Caller-info attributes + - [§22.5.5.1](attributes.md#22551-general) General + - [§22.5.5.2](attributes.md#22552-the-callerlinenumber-attribute) The CallerLineNumber attribute + - [§22.5.5.3](attributes.md#22553-the-callerfilepath-attribute) The CallerFilePath attribute + - [§22.5.5.4](attributes.md#22554-the-callermembername-attribute) The CallerMemberName attribute + - [§22.6](attributes.md#226-attributes-for-interoperation) Attributes for interoperation +- [§23](unsafe-code.md#23-unsafe-code) Unsafe code + - [§23.1](unsafe-code.md#231-general) General + - [§23.2](unsafe-code.md#232-unsafe-contexts) Unsafe contexts + - [§23.3](unsafe-code.md#233-pointer-types) Pointer types + - [§23.4](unsafe-code.md#234-fixed-and-moveable-variables) Fixed and moveable variables + - [§23.5](unsafe-code.md#235-pointer-conversions) Pointer conversions + - [§23.5.1](unsafe-code.md#2351-general) General + - [§23.5.2](unsafe-code.md#2352-pointer-arrays) Pointer arrays + - [§23.6](unsafe-code.md#236-pointers-in-expressions) Pointers in expressions + - [§23.6.1](unsafe-code.md#2361-general) General + - [§23.6.2](unsafe-code.md#2362-pointer-indirection) Pointer indirection + - [§23.6.3](unsafe-code.md#2363-pointer-member-access) Pointer member access + - [§23.6.4](unsafe-code.md#2364-pointer-element-access) Pointer element access + - [§23.6.5](unsafe-code.md#2365-the-address-of-operator) The address-of operator + - [§23.6.6](unsafe-code.md#2366-pointer-increment-and-decrement) Pointer increment and decrement + - [§23.6.7](unsafe-code.md#2367-pointer-arithmetic) Pointer arithmetic + - [§23.6.8](unsafe-code.md#2368-pointer-comparison) Pointer comparison + - [§23.6.9](unsafe-code.md#2369-the-sizeof-operator) The sizeof operator + - [§23.7](unsafe-code.md#237-the-fixed-statement) The fixed statement + - [§23.8](unsafe-code.md#238-fixed-size-buffers) Fixed-size buffers + - [§23.8.1](unsafe-code.md#2381-general) General + - [§23.8.2](unsafe-code.md#2382-fixed-size-buffer-declarations) Fixed-size buffer declarations + - [§23.8.3](unsafe-code.md#2383-fixed-size-buffers-in-expressions) Fixed-size buffers in expressions + - [§23.8.4](unsafe-code.md#2384-definite-assignment-checking) Definite assignment checking + - [§23.9](unsafe-code.md#239-stack-allocation) Stack allocation - [§A](grammar.md#annex-a-grammar) Grammar - [§A.1](grammar.md#a1-general) General - [§A.2](grammar.md#a2-lexical-grammar) Lexical grammar @@ -700,7 +753,7 @@ - [§B.2](portability-issues.md#b2-undefined-behavior) Undefined behavior - [§B.3](portability-issues.md#b3-implementation-defined-behavior) Implementation-defined behavior - [§B.4](portability-issues.md#b4-unspecified-behavior) Unspecified behavior - - [§B.5](portability-issues.md#b5-other-issues) Other Issues + - [§B.5](portability-issues.md#b5-other-issues) Other issues - [§C](standard-library.md#annex-c-standard-library) Standard library - [§C.1](standard-library.md#c1-general) General - [§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271) Standard Library Types defined in ISO/IEC 23271 diff --git a/standard/arrays.md b/standard/arrays.md index 432886bd8..7d10142b1 100644 --- a/standard/arrays.md +++ b/standard/arrays.md @@ -1,12 +1,12 @@ -# 16 Arrays +# 17 Arrays -## 16.1 General +## 17.1 General An array is a data structure that contains a number of variables that are accessed through computed indices. The variables contained in an array, also called the ***elements*** of the array, are all of the same type, and this type is called the ***element type*** of the array. An array has a rank that determines the number of indices associated with each array element. The rank of an array is also referred to as the dimensions of the array. An array with a rank of one is called a ***single-dimensional array***. An array with a rank greater than one is called a ***multi-dimensional array***. Specific sized multi-dimensional arrays are often referred to as two-dimensional arrays, three-dimensional arrays, and so on. Each dimension of an array has an associated length that is an integral number greater than or equal to zero. The dimension lengths are not part of the type of the array, but rather are established when an instance of the array type is created at run-time. The length of a dimension determines the valid range of indices for that dimension: For a dimension of length `N`, indices can range from `0` to `N – 1` inclusive. The total number of elements in an array is the product of the lengths of each dimension in the array. If one or more of the dimensions of an array have a length of zero, the array is said to be empty. -The element type of an array can itself be an array type ([§16.2.1](arrays.md#1621-general)). Such arrays of arrays are distinct from multi-dimensional arrays and can be used to represent “jagged arrays”. +The element type of an array can itself be an array type ([§17.2.1](arrays.md#1721-general)). Such arrays of arrays are distinct from multi-dimensional arrays and can be used to represent “jagged arrays”. > *Example*: > @@ -25,9 +25,9 @@ The element type of an array can itself be an array type ([§16.2.1](arrays.md#1 Every array type is a reference type ([§8.2](types.md#82-reference-types)). The element type of an array can be any type, including value types and array types. -## 16.2 Array types +## 17.2 Array types -### 16.2.1 General +### 17.2.1 General The grammar productions for array types are provided in [§8.2.1](types.md#821-general). @@ -48,15 +48,15 @@ In effect, the *rank_specifier*s are read from left to right *before* the final At run-time, a value of an array type can be `null` or a reference to an instance of that array type. -> *Note*: Following the rules of [§16.6](arrays.md#166-array-covariance), the value may also be a reference to a covariant array type. *end note* +> *Note*: Following the rules of [§17.6](arrays.md#176-array-covariance), the value may also be a reference to a covariant array type. *end note* -### 16.2.2 The System.Array type +### 17.2.2 The System.Array type The type `System.Array` is the abstract base type of all array types. An implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exists from any array type to `System.Array` and to any interface type implemented by `System.Array`. An explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exists from `System.Array` and any interface type implemented by `System.Array` to any array type. `System.Array` is not itself an *array_type*. Rather, it is a *class_type* from which all *array_type*s are derived. At run-time, a value of type `System.Array` can be `null` or a reference to an instance of any array type. -### 16.2.3 Arrays and the generic collection interfaces +### 17.2.3 Arrays and the generic collection interfaces A single-dimensional array `T[]` implements the interface System.Collections.Generic.`IList` (`IList` for short) and its base interfaces. Accordingly, there is an implicit conversion from `T[]` to `IList` and its base interfaces. In addition, if there is an implicit reference conversion from `S` to `T` then `S[]` implements `IList` and there is an implicit reference conversion from `S[]` to `IList` and its base interfaces ([§10.2.8](conversions.md#1028-implicit-reference-conversions)). If there is an explicit reference conversion from `S` to `T` then there is an explicit reference conversion from `S[]` to `IList` and its base interfaces ([§10.3.5](conversions.md#1035-explicit-reference-conversions)). @@ -100,9 +100,9 @@ Whenever there is an implicit or explicit reference conversion from `S[]` to `IL When an array type `S[]` implements `IList`, some of the members of the implemented interface may throw exceptions. The precise behavior of the implementation of the interface is beyond the scope of this specification. -## 16.3 Array creation +## 17.3 Array creation -Array instances are created by *array_creation_expression*s ([§11.7.15.5](expressions.md#117155-array-creation-expressions)) or by field or local variable declarations that include an *array_initializer* ([§16.7](arrays.md#167-array-initializers)). Array instances can also be created implicitly as part of evaluating an argument list involving a parameter array ([§14.6.2.5](classes.md#14625-parameter-arrays)). +Array instances are created by *array_creation_expression*s ([§12.8.16.5](expressions.md#128165-array-creation-expressions)) or by field or local variable declarations that include an *array_initializer* ([§17.7](arrays.md#177-array-initializers)). Array instances can also be created implicitly as part of evaluating an argument list involving a parameter array ([§15.6.2.6](classes.md#15626-parameter-arrays)). When an array instance is created, the rank and length of each dimension are established and then remain constant for the entire lifetime of the instance. In other words, it is not possible to change the rank of an existing array instance, nor is it possible to resize its dimensions. @@ -110,21 +110,21 @@ An array instance is always of an array type. The `System.Array` type is an abst Elements of arrays created by *array_creation_expression*s are always initialized to their default value ([§9.3](variables.md#93-default-values)). -## 16.4 Array element access +## 17.4 Array element access -Array elements are accessed using *element_access* expressions ([§11.7.10.2](expressions.md#117102-array-access)) of the form `A[I₁, I₂, ..., Iₓ]`, where `A` is an expression of an array type and each `Iₑ` is an expression of type `int`, `uint`, `long`, `ulong`, or can be implicitly converted to one or more of these types. The result of an array element access is a variable, namely the array element selected by the indices. +Array elements are accessed using *element_access* expressions ([§12.8.11.2](expressions.md#128112-array-access)) of the form `A[I₁, I₂, ..., Iₓ]`, where `A` is an expression of an array type and each `Iₑ` is an expression of type `int`, `uint`, `long`, `ulong`, or can be implicitly converted to one or more of these types. The result of an array element access is a variable, namely the array element selected by the indices. -The elements of an array can be enumerated using a `foreach` statement ([§12.9.5](statements.md#1295-the-foreach-statement)). +The elements of an array can be enumerated using a `foreach` statement ([§13.9.5](statements.md#1395-the-foreach-statement)). -## 16.5 Array members +## 17.5 Array members Every array type inherits the members declared by the `System.Array` type. -## 16.6 Array covariance +## 17.6 Array covariance For any two *reference_type*s `A` and `B`, if an implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) or explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exists from `A` to `B`, then the same reference conversion also exists from the array type `A[R]` to the array type `B[R]`, where `R` is any given *rank_specifier* (but the same for both array types). This relationship is known as ***array covariance***. Array covariance, in particular, means that a value of an array type `A[R]` might actually be a reference to an instance of an array type `B[R]`, provided an implicit reference conversion exists from `B` to `A`. -Because of array covariance, assignments to elements of reference type arrays include a run-time check which ensures that the value being assigned to the array element is actually of a permitted type ([§11.19.2](expressions.md#11192-simple-assignment)). +Because of array covariance, assignments to elements of reference type arrays include a run-time check which ensures that the value being assigned to the array element is actually of a permitted type ([§12.21.2](expressions.md#12212-simple-assignment)). > *Example*: > @@ -156,9 +156,9 @@ Because of array covariance, assignments to elements of reference type arrays in Array covariance specifically does not extend to arrays of *value_type*s. For example, no conversion exists that permits an `int[]` to be treated as an `object[]`. -## 16.7 Array initializers +## 17.7 Array initializers -Array initializers may be specified in field declarations ([§14.5](classes.md#145-fields)), local variable declarations ([§12.6.2](statements.md#1262-local-variable-declarations)), and array creation expressions ([§11.7.15.5](expressions.md#117155-array-creation-expressions)): +Array initializers may be specified in field declarations ([§15.5](classes.md#155-fields)), local variable declarations ([§13.6.2](statements.md#1362-local-variable-declarations)), and array creation expressions ([§12.8.16.5](expressions.md#128165-array-creation-expressions)): ```ANTLR array_initializer diff --git a/standard/attributes.md b/standard/attributes.md index 86c5593e7..6afb317d8 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -1,6 +1,6 @@ -# 21 Attributes +# 22 Attributes -## 21.1 General +## 22.1 General Much of the C# language enables the programmer to specify declarative information about the entities defined in the program. For example, the accessibility of a method in a class is specified by decorating it with the *method_modifier*s `public`, `protected`, `internal`, and `private`. @@ -8,11 +8,11 @@ C# enables programmers to invent new kinds of declarative information, called ** > *Note*: For instance, a framework might define a `HelpAttribute` attribute that can be placed on certain program elements (such as classes and methods) to provide a mapping from those program elements to their documentation. *end note* -Attributes are defined through the declaration of attribute classes ([§21.2](attributes.md#212-attribute-classes)), which can have positional and named parameters ([§21.2.3](attributes.md#2123-positional-and-named-parameters)). Attributes are attached to entities in a C# program using attribute specifications ([§21.3](attributes.md#213-attribute-specification)), and can be retrieved at run-time as attribute instances ([§21.4](attributes.md#214-attribute-instances)). +Attributes are defined through the declaration of attribute classes ([§22.2](attributes.md#222-attribute-classes)), which can have positional and named parameters ([§22.2.3](attributes.md#2223-positional-and-named-parameters)). Attributes are attached to entities in a C# program using attribute specifications ([§22.3](attributes.md#223-attribute-specification)), and can be retrieved at run-time as attribute instances ([§22.4](attributes.md#224-attribute-instances)). -## 21.2 Attribute classes +## 22.2 Attribute classes -### 21.2.1 General +### 22.2.1 General A class that derives from the abstract class `System.Attribute`, whether directly or indirectly, is an ***attribute class***. The declaration of an attribute class defines a new kind of attribute that can be placed on program entities. By convention, attribute classes are named with a suffix of `Attribute`. Uses of an attribute may either include or omit this suffix. @@ -28,11 +28,11 @@ A generic class declaration shall not use `System.Attribute` as a direct or indi > > *end example* -### 21.2.2 Attribute usage +### 22.2.2 Attribute usage -The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage-attribute)) is used to describe how an attribute class can be used. +The attribute `AttributeUsage` ([§22.5.2](attributes.md#2252-the-attributeusage-attribute)) is used to describe how an attribute class can be used. -`AttributeUsage` has a positional parameter ([§21.2.3](attributes.md#2123-positional-and-named-parameters)) that enables an attribute class to specify the kinds of program entities on which it can be used. +`AttributeUsage` has a positional parameter ([§22.2.3](attributes.md#2223-positional-and-named-parameters)) that enables an attribute class to specify the kinds of program entities on which it can be used. > *Example*: The following example defines an attribute class named `SimpleAttribute` that can be placed on *class_declaration*s and *interface_declaration*s only, and shows several uses of the `Simple` attribute. > @@ -59,7 +59,7 @@ The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage > > *end example* -`AttributeUsage` has a named parameter ([§21.2.3](attributes.md#2123-positional-and-named-parameters)), called `AllowMultiple`, which indicates whether the attribute can be specified more than once for a given entity. If `AllowMultiple` for an attribute class is true, then that attribute class is a ***multi-use attribute class***, and can be specified more than once on an entity. If `AllowMultiple` for an attribute class is false or it is unspecified, then that attribute class is a ***single-use attribute class***, and can be specified at most once on an entity. +`AttributeUsage` has a named parameter ([§22.2.3](attributes.md#2223-positional-and-named-parameters)), called `AllowMultiple`, which indicates whether the attribute can be specified more than once for a given entity. If `AllowMultiple` for an attribute class is true, then that attribute class is a ***multi-use attribute class***, and can be specified more than once on an entity. If `AllowMultiple` for an attribute class is false or it is unspecified, then that attribute class is a ***single-use attribute class***, and can be specified at most once on an entity. > *Example*: The following example defines a multi-use attribute class named `AuthorAttribute` and shows a class declaration with two uses of the `Author` attribute: > @@ -82,7 +82,7 @@ The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage > > *end example* -`AttributeUsage` has another named parameter ([§21.2.3](attributes.md#2123-positional-and-named-parameters)), called `Inherited`, which indicates whether the attribute, when specified on a base class, is also inherited by classes that derive from that base class. If `Inherited` for an attribute class is true, then that attribute is inherited. If `Inherited` for an attribute class is false then that attribute is not inherited. If it is unspecified, its default value is true. +`AttributeUsage` has another named parameter ([§22.2.3](attributes.md#2223-positional-and-named-parameters)), called `Inherited`, which indicates whether the attribute, when specified on a base class, is also inherited by classes that derive from that base class. If `Inherited` for an attribute class is true, then that attribute is inherited. If `Inherited` for an attribute class is false then that attribute is not inherited. If it is unspecified, its default value is true. An attribute class `X` not having an `AttributeUsage` attribute attached to it, as in @@ -103,7 +103,7 @@ is equivalent to the following: class X : Attribute { ... } ``` -### 21.2.3 Positional and named parameters +### 22.2.3 Positional and named parameters Attribute classes can have ***positional parameters*** and ***named parameters***. Each public instance constructor for an attribute class defines a valid sequence of positional parameters for that attribute class. Each non-static public read-write field and property for an attribute class defines a named parameter for the attribute class. For a property to define a named parameter, that property shall have both a public get accessor and a public set accessor. @@ -143,7 +143,7 @@ Attribute classes can have ***positional parameters*** and ***named parameters** > > *end example* -### 21.2.4 Attribute parameter types +### 22.2.4 Attribute parameter types The types of positional and named parameters for an attribute class are limited to the ***attribute parameter types***, which are: @@ -154,9 +154,9 @@ The types of positional and named parameters for an attribute class are limited - Single-dimensional arrays of the above types. - A constructor argument or public field that does not have one of these types, shall not be used as a positional or named parameter in an attribute specification. -## 21.3 Attribute specification +## 22.3 Attribute specification -***Attribute specification*** is the application of a previously defined attribute to a program entity. An attribute is a piece of additional declarative information that is specified for a program entity. Attributes can be specified at global scope (to specify attributes on the containing assembly or module) and for *type_declaration*s ([§13.7](namespaces.md#137-type-declarations)), *class_member_declaration*s ([§14.3](classes.md#143-class-members)), *interface_member_declaration*s ([§17.4](interfaces.md#174-interface-members)), *struct_member_declaration*s ([§15.3](structs.md#153-struct-members)), *enum_member_declaration*s ([§18.2](enums.md#182-enum-declarations)), *accessor_declaration*s ([§14.7.3](classes.md#1473-accessors)), *event_accessor_declaration*s ([§14.8](classes.md#148-events)), elements of *formal_parameter_list*s ([§14.6.2](classes.md#1462-method-parameters)), and elements of *type_parameter_list*s ([§14.2.3](classes.md#1423-type-parameters)). +***Attribute specification*** is the application of a previously defined attribute to a program entity. An attribute is a piece of additional declarative information that is specified for a program entity. Attributes can be specified at global scope (to specify attributes on the containing assembly or module) and for *type_declaration*s ([§14.7](namespaces.md#147-type-declarations)), *class_member_declaration*s ([§15.3](classes.md#153-class-members)), *interface_member_declaration*s ([§18.4](interfaces.md#184-interface-members)), *struct_member_declaration*s ([§16.3](structs.md#163-struct-members)), *enum_member_declaration*s ([§19.2](enums.md#192-enum-declarations)), *accessor_declaration*s ([§15.7.3](classes.md#1573-accessors)), *event_accessor_declaration*s ([§15.8](classes.md#158-events)), elements of *formal_parameter_list*s ([§15.6.2](classes.md#1562-method-parameters)), and elements of *type_parameter_list*s ([§15.2.3](classes.md#1523-type-parameters)). Attributes are specified in ***attribute sections***. An attribute section consists of a pair of square brackets, which surround a comma-separated list of one or more attributes. The order in which attributes are specified in such a list, and the order in which sections attached to the same program entity are arranged, is not significant. For instance, the attribute specifications `[A][B]`, `[B][A]`, `[A, B]`, and `[B, A]` are equivalent. @@ -239,7 +239,7 @@ For the production *global_attribute_target*, and in the text below, *identifier An attribute consists of an *attribute_name* and an optional list of positional and named arguments. The positional arguments (if any) precede the named arguments. A positional argument consists of an *attribute_argument_expression*; a named argument consists of a name, followed by an equal sign, followed by an *attribute_argument_expression*, which, together, are constrained by the same rules as simple assignment. The order of named arguments is not significant. -> *Note*: For convenience, a trailing comma is allowed in a *global_attribute_section* and an *attribute_section*, just as one is allowed in an *array_initializer* ([§16.7](arrays.md#167-array-initializers)). +> *Note*: For convenience, a trailing comma is allowed in a *global_attribute_section* and an *attribute_section*, just as one is allowed in an *array_initializer* ([§17.7](arrays.md#177-array-initializers)). *end note* The *attribute_name* identifies an attribute class. @@ -253,7 +253,7 @@ No other values for *global_attribute_target* are allowed. The standardized *attribute_target* names are `event`, `field`, `method`, `param`, `property`, `return`, `type`, and `typevar`. These target names shall only be used in the following contexts: - `event` — an event. -- `field` — a field. A field-like event (i.e., one without accessors) ([§14.8.2](classes.md#1482-field-like-events)) and an automatically implemented property ([§14.7.4](classes.md#1474-automatically-implemented-properties)) can also have an attribute with this target. +- `field` — a field. A field-like event (i.e., one without accessors) ([§15.8.2](classes.md#1582-field-like-events)) and an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)) can also have an attribute with this target. - `method` — a constructor, finalizer, method, operator, property get and set accessors, indexer get and set accessors, and event add and remove accessors. A field-like event (i.e., one without accessors) can also have an attribute with this target. - `param` — a property set accessor, an indexer set accessor, event add and remove accessors, and a parameter in a constructor, method, and operator. - `property` — a property and an indexer. @@ -386,10 +386,10 @@ It is a compile-time error to use a single-use attribute class more than once on An expression `E` is an *attribute_argument_expression* if all of the following statements are true: -- The type of `E` is an attribute parameter type ([§21.2.4](attributes.md#2124-attribute-parameter-types)). +- The type of `E` is an attribute parameter type ([§22.2.4](attributes.md#2224-attribute-parameter-types)). - At compile-time, the value of `E` can be resolved to one of the following: - A constant value. - - A `System.Type` object obtained using a *typeof_expression* ([§11.7.16](expressions.md#11716-the-typeof-operator)) specifying a non-generic type, a closed constructed type ([§8.4.3](types.md#843-open-and-closed-types)), or an unbound generic type ([§8.4.4](types.md#844-bound-and-unbound-types)), but not an open type ([§8.4.3](types.md#843-open-and-closed-types)). + - A `System.Type` object obtained using a *typeof_expression* ([§12.8.17](expressions.md#12817-the-typeof-operator)) specifying a non-generic type, a closed constructed type ([§8.4.3](types.md#843-open-and-closed-types)), or an unbound generic type ([§8.4.4](types.md#844-bound-and-unbound-types)), but not an open type ([§8.4.3](types.md#843-open-and-closed-types)). - A single-dimensional array of *attribute_argument_expression*s. > *Example*: @@ -451,15 +451,15 @@ The attributes of a type declared in multiple parts are determined by combining, Attributes on type parameters combine in the same way. -## 21.4 Attribute instances +## 22.4 Attribute instances -### 21.4.1 General +### 22.4.1 General An ***attribute instance*** is an instance that represents an attribute at run-time. An attribute is defined with an attribute class, positional arguments, and named arguments. An attribute instance is an instance of the attribute class that is initialized with the positional and named arguments. Retrieval of an attribute instance involves both compile-time and run-time processing, as described in the following subclauses. -### 21.4.2 Compilation of an attribute +### 22.4.2 Compilation of an attribute The compilation of an *attribute* with attribute class `T`, *positional_argument_list* `P`, *named_argument_list* `N`, and specified on a program entity `E` is compiled into an assembly `A` via the following steps: @@ -468,11 +468,11 @@ The compilation of an *attribute* with attribute class `T`, *positional_argumen - For each *named_argument* `Arg` in `N`: - Let `Name` be the *identifier* of the *named_argument* `Arg`. - `Name` shall identify a non-static read-write public field or property on `T`. If `T` has no such field or property, then a compile-time error occurs. -- If any of the values within *positional_argument_list* `P` or one of the values within *named_argument_list* `N` is of type `System.String` and the value is not well-formed as defined by the Unicode Standard, it is implementation-defined whether the value compiled is equal to the run-time value retrieved ([§21.4.3](attributes.md#2143-run-time-retrieval-of-an-attribute-instance)). +- If any of the values within *positional_argument_list* `P` or one of the values within *named_argument_list* `N` is of type `System.String` and the value is not well-formed as defined by the Unicode Standard, it is implementation-defined whether the value compiled is equal to the run-time value retrieved ([§22.4.3](attributes.md#2243-run-time-retrieval-of-an-attribute-instance)). > *Note*: As an example, a string which contains a high surrogate UTF-16 code unit which isn’t immediately followed by a low surrogate code unit is not well-formed. *end note* - Store the following information (for run-time instantiation of the attribute) in the assembly output by the compiler as a result of compiling the program containing the attribute: the attribute class `T`, the instance constructor `C` on `T`, the *positional_argument_list* `P`, the *named_argument_list* `N`, and the associated program entity `E`, with the values resolved completely at compile-time. -### 21.4.3 Run-time retrieval of an attribute instance +### 22.4.3 Run-time retrieval of an attribute instance The attribute instance represented by `T`, `C`, `P`, and `N`, and associated with `E` can be retrieved at run-time from the assembly `A` using the following steps: @@ -484,14 +484,13 @@ The attribute instance represented by `T`, `C`, `P`, and `N`, and associated wi - Otherwise, Name identifies a property on `O`. Set this property to Value. - The result is `O`, an instance of the attribute class `T` that has been initialized with the *positional_argument_list* `P` and the *named_argument_list* `N`. -> *Note*: The format for storing `T`, `C`, `P`, `N` (and associating it with `E`) in `A` and the mechanism to specify `E` and retrieve `T`, `C`, `P`, `N` from `A` (and hence how an attribute instance is obtained at runtime) is beyond the scope of this standard. *end note* +> *Note*: The format for storing `T`, `C`, `P`, `N` (and associating it with `E`) in `A` and the mechanism to specify `E` and retrieve `T`, `C`, `P`, `N` from `A` (and hence how an attribute instance is obtained at runtime) is beyond the scope of this specification. *end note* -> *Example*: In an implementation of the CLI, the `Help` attribute instances in the assembly created by compiling the example program in [§21.2.3](attributes.md#2123-positional-and-named-parameters) can be retrieved with the following program: +> *Example*: In an implementation of the CLI, the `Help` attribute instances in the assembly created by compiling the example program in [§22.2.3](attributes.md#2223-positional-and-named-parameters) can be retrieved with the following program: > -> -> +> > ```csharp > public sealed class InterrogateHelpUrls > { @@ -502,11 +501,11 @@ The attribute instance represented by `T`, `C`, `P`, and `N`, and associated wi > foreach (Type t in Assembly.Load(assemblyName).GetTypes()) > { > Console.WriteLine($"Type : {t}"); -> HelpAttribute[] helpers = -> (HelpAttribute[])t.GetCustomAttributes(helpType, false); -> for (int at = 0; at != helpers.Length; at++) +> var attributes = t.GetCustomAttributes(helpType, false); +> var helpers = (HelpAttribute[]) attributes; +> foreach (var helper in helpers) > { -> Console.WriteLine($"\tUrl : {helpers[at].Url}"); +> Console.WriteLine($"\tUrl : {helper.Url}"); > } > } > } @@ -515,34 +514,34 @@ The attribute instance represented by `T`, `C`, `P`, and `N`, and associated wi > > *end example* -## 21.5 Reserved attributes +## 22.5 Reserved attributes -### 21.5.1 General +### 22.5.1 General A small number of attributes affect the language in some way. These attributes include: -- `System.AttributeUsageAttribute` ([§21.5.2](attributes.md#2152-the-attributeusage-attribute)), which is used to describe the ways in which an attribute class can be used. -- `System.Diagnostics.ConditionalAttribute` ([§21.5.3](attributes.md#2153-the-conditional-attribute)), is a multi-use attribute class which is used to define conditional methods and conditional attribute classes. This attribute indicates a condition by testing a conditional compilation symbol. -- `System.ObsoleteAttribute` ([§21.5.4](attributes.md#2154-the-obsolete-attribute)), which is used to mark a member as obsolete. -- `System.Runtime.CompilerServices.CallerLineNumberAttribute` ([§21.5.5.2](attributes.md#21552-the-callerlinenumber-attribute)), `System.Runtime.CompilerServices.CallerFilePathAttribute` ([§21.5.5.3](attributes.md#21553-the-callerfilepath-attribute)), and `System.Runtime.CompilerServices.CallerMemberNameAttribute` ([§21.5.5.4](attributes.md#21554-the-callermembername-attribute)), which are used to supply information about the calling context to optional parameters. +- `System.AttributeUsageAttribute` ([§22.5.2](attributes.md#2252-the-attributeusage-attribute)), which is used to describe the ways in which an attribute class can be used. +- `System.Diagnostics.ConditionalAttribute` ([§22.5.3](attributes.md#2253-the-conditional-attribute)), is a multi-use attribute class which is used to define conditional methods and conditional attribute classes. This attribute indicates a condition by testing a conditional compilation symbol. +- `System.ObsoleteAttribute` ([§22.5.4](attributes.md#2254-the-obsolete-attribute)), which is used to mark a member as obsolete. +- `System.Runtime.CompilerServices.CallerLineNumberAttribute` ([§22.5.5.2](attributes.md#22552-the-callerlinenumber-attribute)), `System.Runtime.CompilerServices.CallerFilePathAttribute` ([§22.5.5.3](attributes.md#22553-the-callerfilepath-attribute)), and `System.Runtime.CompilerServices.CallerMemberNameAttribute` ([§22.5.5.4](attributes.md#22554-the-callermembername-attribute)), which are used to supply information about the calling context to optional parameters. An execution environment may provide additional implementation-specific attributes that affect the execution of a C# program. -### 21.5.2 The AttributeUsage attribute +### 22.5.2 The AttributeUsage attribute The attribute `AttributeUsage` is used to describe the manner in which the attribute class can be used. A class that is decorated with the `AttributeUsage` attribute shall derive from `System.Attribute`, either directly or indirectly. Otherwise, a compile-time error occurs. -> *Note*: For an example of using this attribute, see [§21.2.2](attributes.md#2122-attribute-usage). *end note* +> *Note*: For an example of using this attribute, see [§22.2.2](attributes.md#2222-attribute-usage). *end note* -### 21.5.3 The Conditional attribute +### 22.5.3 The Conditional attribute -#### 21.5.3.1 General +#### 22.5.3.1 General The attribute `Conditional` enables the definition of ***conditional methods*** and ***conditional attribute classes***. -#### 21.5.3.2 Conditional methods +#### 22.5.3.2 Conditional methods A method decorated with the `Conditional` attribute is a conditional method. Each conditional method is thus associated with the conditional compilation symbols declared in its `Conditional` attributes. @@ -611,9 +610,10 @@ It is important to understand that the inclusion or exclusion of a call to a con > *Example*: In the following code > -> +> > ```csharp -> // File class1.cs: +> // File Class1.cs: +> using System.Diagnostics; > class Class1 > { > [Conditional("DEBUG")] @@ -623,7 +623,7 @@ It is important to understand that the inclusion or exclusion of a call to a con > } > } > -> // File class2.cs: +> // File Class2.cs: > #define DEBUG > class Class2 > { @@ -633,7 +633,7 @@ It is important to understand that the inclusion or exclusion of a call to a con > } > } > -> // File class3.cs: +> // File Class3.cs: > #undef DEBUG > class Class3 > { @@ -652,16 +652,17 @@ The use of conditional methods in an inheritance chain can be confusing. Calls m > *Example*: In the following code > -> +> > ```csharp -> // File class1.cs +> // File Class1.cs +> using System.Diagnostics; > class Class1 > { > [Conditional("DEBUG")] > public virtual void M() => Console.WriteLine("Class1.M executed"); > } > -> // File class2.cs +> // File Class2.cs > class Class2 : Class1 > { > public override void M() @@ -671,11 +672,11 @@ The use of conditional methods in an inheritance chain can be confusing. Calls m > } > } > -> // File class3.cs +> // File Class3.cs > #define DEBUG > class Class3 > { -> public static void Test() +> public static void Main() > { > Class2 c = new Class2(); > c.M(); // M is called @@ -687,9 +688,9 @@ The use of conditional methods in an inheritance chain can be confusing. Calls m > > *end example* -#### 21.5.3.3 Conditional attribute classes +#### 22.5.3.3 Conditional attribute classes -An attribute class ([§21.2](attributes.md#212-attribute-classes)) decorated with one or more `Conditional` attributes is a conditional attribute class. A conditional attribute class is thus associated with the conditional compilation symbols declared in its `Conditional` attributes. +An attribute class ([§22.2](attributes.md#222-attribute-classes)) decorated with one or more `Conditional` attributes is a conditional attribute class. A conditional attribute class is thus associated with the conditional compilation symbols declared in its `Conditional` attributes. > *Example*: > @@ -704,24 +705,25 @@ An attribute class ([§21.2](attributes.md#212-attribute-classes)) decorated wit > > *end example* -Attribute specifications ([§21.3](attributes.md#213-attribute-specification)) of a conditional attribute are included if one or more of its associated conditional compilation symbols is defined at the point of specification, otherwise the attribute specification is omitted. +Attribute specifications ([§22.3](attributes.md#223-attribute-specification)) of a conditional attribute are included if one or more of its associated conditional compilation symbols is defined at the point of specification, otherwise the attribute specification is omitted. It is important to note that the inclusion or exclusion of an attribute specification of a conditional attribute class is controlled by the conditional compilation symbols at the point of the specification. > *Example*: In the example > -> +> > ```csharp -> // File test.cs: +> // File Test.cs: +> using System.Diagnostics; > [Conditional("DEBUG")] > public class TestAttribute : Attribute {} > -> // File class1.cs: +> // File Class1.cs: > #define DEBUG > [Test] // TestAttribute is specified > class Class1 {} > -> // File class2.cs: +> // File Class2.cs: > #undef DEBUG > [Test] // TestAttribute is not specified > class Class2 {} @@ -731,7 +733,7 @@ It is important to note that the inclusion or exclusion of an attribute specific > > *end example* -### 21.5.4 The Obsolete attribute +### 22.5.4 The Obsolete attribute The attribute `Obsolete` is used to mark types and members of types that should no longer be used. @@ -766,9 +768,9 @@ If a program uses a type or member that is decorated with the `Obsolete` attribu > > *end example* -### 21.5.5 Caller-info attributes +### 22.5.5 Caller-info attributes -#### 21.5.5.1 General +#### 22.5.5.1 General For purposes such as logging and reporting, it is sometimes useful for a function member to obtain certain compile-time information about the calling code. The caller-info attributes provide a way to pass such information transparently. @@ -798,17 +800,23 @@ Caller-info attributes can occur on optional parameters anywhere, including in d It is an error to have the same caller-info attribute on a parameter of both the defining and implementing part of a partial method declaration. Only caller-info attributes in the defining part are applied, whereas caller-info attributes occurring only in the implementing part are ignored. -Caller information does not affect overload resolution. As the attributed optional parameters are still omitted from the source code of the caller, overload resolution ignores those parameters in the same way it ignores other omitted optional parameters ([§11.6.4](expressions.md#1164-overload-resolution)). +Caller information does not affect overload resolution. As the attributed optional parameters are still omitted from the source code of the caller, overload resolution ignores those parameters in the same way it ignores other omitted optional parameters ([§12.6.4](expressions.md#1264-overload-resolution)). Caller information is only substituted when a function is explicitly invoked in source code. Implicit invocations such as implicit parent constructor calls do not have a source location and will not substitute caller information. Also, calls that are dynamically bound will not substitute caller information. When a caller-info attributed parameter is omitted in such cases, the specified default value of the parameter is used instead. One exception is query expressions. These are considered syntactic expansions, and if the calls they expand to omit optional parameters with caller-info attributes, caller information will be substituted. The location used is the location of the query clause which the call was generated from. -If more than one caller-info attribute is specified on a given parameter, they are preferred in the following order: `CallerLineNumber`, `CallerFilePath`, `CallerMemberName`. +If more than one caller-info attribute is specified on a given parameter, they are recognized in the following order: `CallerLineNumber`, `CallerFilePath`, `CallerMemberName`. Consider the following parameter declaration: + +```csharp +[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ... +``` + +`CallerLineNumber` takes precedence, and the other two attributes are ignored. If `CallerLineNumber` were omitted, `CallerFilePath` would take precedence, and `CallerMemberName` would be ignored. The lexical ordering of these attributes is irrelevant. -#### 21.5.5.2 The CallerLineNumber attribute +#### 22.5.5.2 The CallerLineNumber attribute -The `System.Runtime.CompilerServices.CallerLineNumberAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from the constant value `int.MaxValue` to the parameter’s type. This ensures that any non-negative line number up to that value can be passed without error. +The attribute `System.Runtime.CompilerServices.CallerLineNumberAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from the constant value `int.MaxValue` to the parameter’s type. This ensures that any non-negative line number up to that value can be passed without error. If a function invocation from a location in source code omits an optional parameter with the `CallerLineNumberAttribute`, then a numeric literal representing that location’s line number is used as an argument to the invocation instead of the default parameter value. @@ -816,9 +824,9 @@ If the invocation spans multiple lines, the line chosen is implementation-depend The line number may be affected by `#line` directives ([§6.5.8](lexical-structure.md#658-line-directives)). -#### 21.5.5.3 The CallerFilePath attribute +#### 22.5.5.3 The CallerFilePath attribute -The `System.Runtime.CompilerServices.CallerFilePathAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from `string` to the parameter’s type. +The attribute `System.Runtime.CompilerServices.CallerFilePathAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from `string` to the parameter’s type. If a function invocation from a location in source code omits an optional parameter with the `CallerFilePathAttribute`, then a string literal representing that location’s file path is used as an argument to the invocation instead of the default parameter value. @@ -826,9 +834,9 @@ The format of the file path is implementation-dependent. The file path may be affected by `#line` directives ([§6.5.8](lexical-structure.md#658-line-directives)). -#### 21.5.5.4 The CallerMemberName attribute +#### 22.5.5.4 The CallerMemberName attribute -The `System.Runtime.CompilerServices.CallerMemberNameAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from `string` to the parameter’s type. +The attribute `System.Runtime.CompilerServices.CallerMemberNameAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from `string` to the parameter’s type. If a function invocation from a location within the body of a function member or within an attribute applied to the function member itself or its return type, parameters or type parameters in source code omits an optional parameter with the `CallerMemberNameAttribute`, then a string literal representing the name of that member is used as an argument to the invocation instead of the default parameter value. @@ -838,13 +846,13 @@ For invocations that occur within explicit interface member implementations, onl For invocations that occur within property or event accessors, the member name used is that of the property or event itself. -For invocations that occur within indexer accessors, the member name used is that supplied by an `IndexerNameAttribute` ([§21.6](attributes.md#216-attributes-for-interoperation)) on the indexer member, if present, or the default name `Item` otherwise. +For invocations that occur within indexer accessors, the member name used is that supplied by an `IndexerNameAttribute` ([§22.6](attributes.md#226-attributes-for-interoperation)) on the indexer member, if present, or the default name `Item` otherwise. For invocations that occur within field or event initializers, the member name used is the name of the field or event being initialized. For invocations that occur within declarations of instance constructors, static constructors, finalizers and operators the member name used is implementation-dependent. -## 21.6 Attributes for interoperation +## 22.6 Attributes for interoperation For interoperation with other languages, an indexer may be implemented using indexed properties. If no `IndexerName` attribute is present for an indexer, then the name `Item` is used by default. The `IndexerName` attribute enables a developer to override this default and specify a different name. diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 21322f096..97e72a319 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -12,14 +12,14 @@ A program compiled as an application shall contain at least one method qualifyin - It shall be declared in a non-generic type. If the type declaring the method is a nested type, none of its enclosing types may be generic. - It may have the `async` modifier provided the method’s return type is `System.Threading.Tasks.Task` or `System.Threading.Tasks.Task`. - The return type shall be `void`, `int`, `System.Threading.Tasks.Task`, or `System.Threading.Tasks.Task`. -- It shall not be a partial method ([§14.6.9](classes.md#1469-partial-methods)) without an implementation. +- It shall not be a partial method ([§15.6.9](classes.md#1569-partial-methods)) without an implementation. - The formal parameter list shall either be empty, or have a single value parameter of type `string[]`. > *Note*: Methods with the `async` modifier must have exactly one of the two return types specified above in order to qualify as an entry point. An `async void` method, or an `async` method returning a different awaitable type such as `ValueTask` or `ValueTask` does not qualify as an entry point. *end note* If more than one method qualifying as an entry point is declared within a program, an external mechanism may be used to specify which method is deemed to be the actual entry point for the application. If a qualifying method having a return type of `int` or `void` is found, any qualifying method having a return type of `System.Threading.Tasks.Task` or `System.Threading.Tasks.Task` is not considered an entry point method. It is a compile-time error for a program to be compiled as an application without exactly one entry point. A program compiled as a class library may contain methods that would qualify as application entry points, but the resulting library has no entry point. -Ordinarily, the declared accessibility ([§7.5.2](basic-concepts.md#752-declared-accessibility)) of a method is determined by the access modifiers ([§14.3.6](classes.md#1436-access-modifiers)) specified in its declaration, and similarly the declared accessibility of a type is determined by the access modifiers specified in its declaration. In order for a given method of a given type to be callable, both the type and the member shall be accessible. However, the application entry point is a special case. Specifically, the execution environment can access the application’s entry point regardless of its declared accessibility and regardless of the declared accessibility of its enclosing type declarations. +Ordinarily, the declared accessibility ([§7.5.2](basic-concepts.md#752-declared-accessibility)) of a method is determined by the access modifiers ([§15.3.6](classes.md#1536-access-modifiers)) specified in its declaration, and similarly the declared accessibility of a type is determined by the access modifiers specified in its declaration. In order for a given method of a given type to be callable, both the type and the member shall be accessible. However, the application entry point is a special case. Specifically, the execution environment can access the application’s entry point regardless of its declared accessibility and regardless of the declared accessibility of its enclosing type declarations. When the entry point method has a return type of `System.Threading.Tasks.Task` or `System.Threading.Tasks.Task`, the compiler synthesizes a synchronous entry-point method that calls the corresponding `Main` method. The synthesized method has parameters and return types based on the `Main` method: @@ -53,15 +53,15 @@ Other than the situations listed above, entry point methods behave like those th If the return type of the application’s effective entry point method is `int` and execution completes without resulting in an exception, the value of the `int` returned serves as the application’s ***termination status code***. The purpose of this code is to allow communication of success or failure to the execution environment. If the return type of the effective entry point method is `void` and execution completes without resulting in an exception, the termination status code is `0`. -If the effective entry point method terminates due to an exception ([§20.4](exceptions.md#204-how-exceptions-are-handled)), the exit code is implementation-specific. Additionally, the implementation may provide alternative APIs for specifying the exit code. +If the effective entry point method terminates due to an exception ([§21.4](exceptions.md#214-how-exceptions-are-handled)), the exit code is implementation-specific. Additionally, the implementation may provide alternative APIs for specifying the exit code. -Whether or not finalizers ([§14.13](classes.md#1413-finalizers)) are run as part of application termination is implementation-specific. +Whether or not finalizers ([§15.13](classes.md#1513-finalizers)) are run as part of application termination is implementation-specific. -> *Note*: The .NET Framework implementation makes every reasonable effort to call finalizers ([§14.13](classes.md#1413-finalizers)) for all of its objects that have not yet been garbage collected, unless such cleanup has been suppressed (by a call to the library method `GC.SuppressFinalize`, for example). *end note* +> *Note*: The .NET Framework implementation makes every reasonable effort to call finalizers ([§15.13](classes.md#1513-finalizers)) for all of its objects that have not yet been garbage collected, unless such cleanup has been suppressed (by a call to the library method `GC.SuppressFinalize`, for example). *end note* ## 7.3 Declarations -Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespaces. These are introduced using namespace declarations ([§13](namespaces.md#13-namespaces)), which can contain type declarations and nested namespace declarations. Type declarations ([§13.7](namespaces.md#137-type-declarations)) are used to define classes ([§14](classes.md#14-classes)), structs ([§15](structs.md#15-structs)), interfaces ([§17](interfaces.md#17-interfaces)), enums ([§18](enums.md#18-enums)), and delegates ([§19](delegates.md#19-delegates)). The kinds of members permitted in a type declaration depend on the form of the type declaration. For instance, class declarations can contain declarations for constants ([§14.4](classes.md#144-constants)), fields ([§14.5](classes.md#145-fields)), methods ([§14.6](classes.md#146-methods)), properties ([§14.7](classes.md#147-properties)), events ([§14.8](classes.md#148-events)), indexers ([§14.9](classes.md#149-indexers)), operators ([§14.10](classes.md#1410-operators)), instance constructors ([§14.11](classes.md#1411-instance-constructors)), static constructors ([§14.12](classes.md#1412-static-constructors)), finalizers ([§14.13](classes.md#1413-finalizers)), and nested types ([§14.3.9](classes.md#1439-nested-types)). +Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespaces. These are introduced using namespace declarations ([§14](namespaces.md#14-namespaces)), which can contain type declarations and nested namespace declarations. Type declarations ([§14.7](namespaces.md#147-type-declarations)) are used to define classes ([§15](classes.md#15-classes)), structs ([§16](structs.md#16-structs)), interfaces ([§18](interfaces.md#18-interfaces)), enums ([§19](enums.md#19-enums)), and delegates ([§20](delegates.md#20-delegates)). The kinds of members permitted in a type declaration depend on the form of the type declaration. For instance, class declarations can contain declarations for constants ([§15.4](classes.md#154-constants)), fields ([§15.5](classes.md#155-fields)), methods ([§15.6](classes.md#156-methods)), properties ([§15.7](classes.md#157-properties)), events ([§15.8](classes.md#158-events)), indexers ([§15.9](classes.md#159-indexers)), operators ([§15.10](classes.md#1510-operators)), instance constructors ([§15.11](classes.md#1511-instance-constructors)), static constructors ([§15.12](classes.md#1512-static-constructors)), finalizers ([§15.13](classes.md#1513-finalizers)), and nested types ([§15.3.9](classes.md#1539-nested-types)). A declaration defines a name in the ***declaration space*** to which the declaration belongs. It is a compile-time error to have two or more declarations that introduce members with the same name in a declaration space, except in the following cases: @@ -70,27 +70,33 @@ A declaration defines a name in the ***declaration space*** to which the declara > *Note*: However, these declarations could introduce ambiguities if included in the same application. *end note* - Two or more methods with the same name but distinct signatures are allowed in the same declaration space ([§7.6](basic-concepts.md#76-signatures-and-overloading)). - Two or more type declarations with the same name but distinct numbers of type parameters are allowed in the same declaration space ([§7.8.2](basic-concepts.md#782-unqualified-names)). -- Two or more type declarations with the partial modifier in the same declaration space may share the same name, same number of type parameters and same classification (class, struct or interface). In this case, the type declarations contribute to a single type and are themselves aggregated to form a single declaration space ([§14.2.7](classes.md#1427-partial-declarations)). +- Two or more type declarations with the partial modifier in the same declaration space may share the same name, same number of type parameters and same classification (class, struct or interface). In this case, the type declarations contribute to a single type and are themselves aggregated to form a single declaration space ([§15.2.7](classes.md#1527-partial-declarations)). - A namespace declaration and a type declaration in the same declaration space can share the same name as long as the type declaration has at least one type parameter ([§7.8.2](basic-concepts.md#782-unqualified-names)). There are several different types of declaration spaces, as described in the following. - Within all compilation units of a program, *namespace_member_declaration*s with no enclosing *namespace_declaration* are members of a single combined declaration space called the ***global declaration space***. - Within all compilation units of a program, *namespace_member_declaration*s within *namespace_declaration*s that have the same fully qualified namespace name are members of a single combined declaration space. -- Each *compilation_unit* and *namespace_body* has an ***alias declaration space***. Each *extern_alias_directive* and *using_alias_directive* of the *compilation_unit* or *namespace_body* contributes a member to the alias declaration space ([§13.5.2](namespaces.md#1352-using-alias-directives)). -- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ([§15.2.3](structs.md#1523-partial-modifier)).Names are introduced into this declaration space through *class_member_declaration*s, *struct_member_declaration*s, *interface_member_declaration*s, or *type_parameter*s. Except for overloaded instance constructor declarations and static constructor declarations, a class or struct cannot contain a member declaration with the same name as the class or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to ***hide*** the inherited member. +- Each *compilation_unit* and *namespace_body* has an ***alias declaration space***. Each *extern_alias_directive* and *using_alias_directive* of the *compilation_unit* or *namespace_body* contributes a member to the alias declaration space ([§14.5.2](namespaces.md#1452-using-alias-directives)). +- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ([§16.2.4](structs.md#1624-partial-modifier)). Names are introduced into this declaration space through *class_member_declaration*s, *struct_member_declaration*s, *interface_member_declaration*s, or *type_parameter*s. Except for overloaded instance constructor declarations and static constructor declarations, a class or struct cannot contain a member declaration with the same name as the class or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to ***hide*** the inherited member. - Each delegate declaration creates a new declaration space. Names are introduced into this declaration space through formal parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. - Each enumeration declaration creates a new declaration space. Names are introduced into this declaration space through *enum_member_declarations*. -- Each method declaration, property declaration, property accessor declaration, indexer declaration, indexer accessor declaration, operator declaration, instance constructor declaration, anonymous function, and local function creates a new declaration space called a ***local variable declaration space***. Names are introduced into this declaration space through formal parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. The set accessor for a property or an indexer introduces the name `value` as a formal parameter. The body of the function member, anonymous function, or local function, if any, is considered to be nested within the local variable declaration space. It is an error for a local variable declaration space and a nested local variable declaration space to contain elements with the same name. Thus, within a nested declaration space it is not possible to declare a local variable or constant with the same name as a local variable or constant in an enclosing declaration space. It is possible for two declaration spaces to contain elements with the same name as long as neither declaration space contains the other. -- Each *block* or *switch_block*, as well as a `for`, `foreach`, and `using` statement, creates a local variable declaration space for local variables and local constants. Names are introduced into this declaration space through *local_variable_declaration*s and *local_constant_declaration*s. Note that blocks that occur as or within the body of a function member, anonymous function, or local function are nested within the local variable declaration space declared by those functions for their parameters. Thus, it is an error to have, for example, a method with a local variable and a parameter of the same name. - +- Each method declaration, property declaration, property accessor declaration, indexer declaration, indexer accessor declaration, operator declaration, instance constructor declaration, anonymous function, and local function creates a new declaration space called a ***local variable declaration space***. Names are introduced into this declaration space through formal parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. The set accessor for a property or an indexer introduces the name `value` as a formal parameter. +- Additional local variable declaration spaces may occur within member declarations, anonymous functions and local functions. Names are introduced into these declaration spaces through *pattern*s, *declaration_expression*s, *declaration_statement*s and *exception_specifier*s. Local variable declaration spaces may be nested, but it is an error for a local variable declaration space and a nested local variable declaration space to contain elements with the same name. Thus, within a nested declaration space it is not possible to declare a local variable, local function or constant with the same name as a parameter, type parameter, local variable, local function or constant in an enclosing declaration space. It is possible for two declaration spaces to contain elements with the same name as long as neither declaration space contains the other. Local declaration spaces are created by the following constructs: + - Each *variable_initializer* in a field and property declaration introduces its own local variable declaration space, that is not nested within any other local variable declaration space. + - The body of a function member, anonymous function, or local function, if any, creates a local variable declaration space that is considered to be nested within the function’s local variable declaration space. + - Each *constructor_initializer* creates a local variable declaration space nested within the instance constructor declaration. The local variable declaration space for the constructor body is in turn nested within this local variable declaration space. + - Each *block*, *switch_block*, *specific_catch_clause*, *iteration_statement* and *using_statement* creates a nested local variable declaration space. + - Each *embedded_statement* that is not directly part of a *statement_list* creates a nested local variable declaration space. + - Each *switch_section* creates a nested local variable declaration space. However, variables declared directly within the *statement_list* of the *switch_section* (but not within a nested local variable declaration space inside the *statement_list*) are added directly to the local variable declaration space of the enclosing *switch_block*, instead of that of the *switch_section*. + - The syntactic translation of a *query_expression* ([§12.20.3](expressions.md#12203-query-expression-translation)) may introduce one or more lambda expressions. As anonymous functions, each of these creates a local variable declaration space as described above. - Each *block* or *switch_block* creates a separate declaration space for labels. Names are introduced into this declaration space through *labeled_statement*s, and the names are referenced through *goto_statement*s. The ***label declaration space*** of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a label with the same name as a label in an enclosing block. The textual order in which names are declared is generally of no significance. In particular, textual order is not significant for the declaration and use of namespaces, constants, methods, properties, events, indexers, operators, instance constructors, finalizers, static constructors, and types. Declaration order is significant in the following ways: -- Declaration order for field declarations determines the order in which their initializers (if any) are executed ([§14.5.6.2](classes.md#14562-static-field-initialization), [§14.5.6.3](classes.md#14563-instance-field-initialization)). +- Declaration order for field declarations determines the order in which their initializers (if any) are executed ([§15.5.6.2](classes.md#15562-static-field-initialization), [§15.5.6.3](classes.md#15563-instance-field-initialization)). - Local variables shall be defined before they are used ([§7.7](basic-concepts.md#77-scopes)). -- Declaration order for enum member declarations ([§18.4](enums.md#184-enum-members)) is significant when *constant_expression* values are omitted. +- Declaration order for enum member declarations ([§19.4](enums.md#194-enum-members)) is significant when *constant_expression* values are omitted. > *Example*: The declaration space of a namespace is “open ended”, and two namespace declarations with the same fully qualified name contribute to the same declaration space. For example > @@ -213,7 +219,7 @@ The members of `object` ([§8.2.3](types.md#823-the-object-type)) and `string` ( The members of an interface are the members declared in the interface and in all base interfaces of the interface. -> *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§17.4](interfaces.md#174-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§11.5](expressions.md#115-member-lookup)). *end note* +> *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§18.4](interfaces.md#184-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* ### 7.4.7 Array members @@ -221,7 +227,7 @@ The members of an array are the members inherited from class `System.Array`. ### 7.4.8 Delegate members -A delegate inherits members from class `System.Delegate`. Additionally, it contains a method named `Invoke` with the same return type and formal parameter list specified in its declaration ([§19.2](delegates.md#192-delegate-declarations)). An invocation of this method shall behave identically to a delegate invocation ([§19.6](delegates.md#196-delegate-invocation)) on the same delegate instance. +A delegate inherits members from class `System.Delegate`. Additionally, it contains a method named `Invoke` with the same return type and formal parameter list specified in its declaration ([§20.2](delegates.md#202-delegate-declarations)). An invocation of this method shall behave identically to a delegate invocation ([§20.6](delegates.md#206-delegate-invocation)) on the same delegate instance. An implementation may provide additional members, either through inheritance or directly in the delegate itself. @@ -381,7 +387,7 @@ Let `B` be a base class that declares a protected instance member `M`, and let - A *primary_expression* of the form `base.M`. - A *primary_expression* of the form `base[`*argument_list*`]`. -In addition to these forms of access, a derived class can access a protected instance constructor of a base class in a *constructor_initializer* ([§14.11.2](classes.md#14112-constructor-initializers)). +In addition to these forms of access, a derived class can access a protected instance constructor of a base class in a *constructor_initializer* ([§15.11.2](classes.md#15112-constructor-initializers)). > *Example*: In the following code > @@ -443,7 +449,7 @@ In addition to these forms of access, a derived class can access a protected ins -> *Note:* The accessibility domain ([§7.5.3](basic-concepts.md#753-accessibility-domains)) of a protected member declared in a generic class includes the program text of all class declarations derived from any type constructed from that generic class. In the example: +> *Note*: The accessibility domain ([§7.5.3](basic-concepts.md#753-accessibility-domains)) of a protected member declared in a generic class includes the program text of all class declarations derived from any type constructed from that generic class. In the example: > > > ```csharp @@ -518,9 +524,8 @@ The following accessibility constraints exist: Methods, instance constructors, indexers, and operators are characterized by their ***signatures***: -- The signature of a method consists of the name of the method, the number of type parameters, and the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type parameter list of the method. The signature of a method specifically does not include the return type, parameter names, type parameter names, type parameter constraints, the `params` or `this` parameter modifiers, nor whether parameters are required or optional. - -- The signature of an instance constructor consists of the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. The signature of an instance constructor specifically does not include the `params` modifier that may be specified for the right-most parameter. +- The signature of a method consists of the name of the method, the number of type parameters, and the type and parameter-passing mode of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type parameter list of the method. The signature of a method specifically does not include the return type, parameter names, type parameter names, type parameter constraints, the `params` or `this` parameter modifiers, nor whether parameters are required or optional. +- The signature of an instance constructor consists of the type and parameter-passing mode of each of its formal parameters, considered in the order left to right. The signature of an instance constructor specifically does not include the `params` modifier that may be specified for the right-most parameter. - The signature of an indexer consists of the type of each of its formal parameters, considered in the order left to right. The signature of an indexer specifically does not include the element type, nor does it include the `params` modifier that may be specified for the right-most parameter. - The signature of an operator consists of the name of the operator and the type of each of its formal parameters, considered in the order left to right. The signature of an operator specifically does not include the result type. - The signature of a conversion operator consists of the source type and the target type. The implicit or explicit classification of a conversion operator is not part of the signature. @@ -533,9 +538,9 @@ Signatures are the enabling mechanism for ***overloading*** of members in classe - Overloading of indexers permits a class, struct, or interface to declare multiple indexers, provided their signatures are unique within that class, struct, or interface. - Overloading of operators permits a class or struct to declare multiple operators with the same name, provided their signatures are unique within that class or struct. -Although `out` and `ref` parameter modifiers are considered part of a signature, members declared in a single type cannot differ in signature solely by `ref` and `out`. A compile-time error occurs if two members are declared in the same type with signatures that would be the same if all parameters in both methods with `out` modifiers were changed to `ref` modifiers. For other purposes of signature matching (e.g., hiding or overriding), `ref` and `out` are considered part of the signature and do not match each other. +Although `in`, `out`, and `ref` parameter modifiers are considered part of a signature, members declared in a single type cannot differ in signature solely by `in`, `out`, and `ref`. A compile-time error occurs if two members are declared in the same type with signatures that would be the same if all parameters in both methods with `out` or `in` modifiers were changed to `ref` modifiers. For other purposes of signature matching (e.g., hiding or overriding), `in`, `out`, and `ref` are considered part of the signature and do not match each other. -> *Note*: This restriction is to allow C# programs to be easily translated to run on the Common Language Infrastructure (CLI), which does not provide a way to define methods that differ solely in `ref` and `out`. *end note* +> *Note*: This restriction is to allow C# programs to be easily translated to run on the Common Language Infrastructure (CLI), which does not provide a way to define methods that differ solely in `in`, `out`, and `ref`. *end note* The types `object` and `dynamic` are not distinguished when comparing signatures. Therefore members declared in a single type whose signatures differ only by replacing `object` with `dynamic` are not allowed. @@ -563,9 +568,7 @@ The types `object` and `dynamic` are not distinguished when comparing signatures > } > ``` > -> Note that any `ref` and `out` parameter modifiers ([§14.6.2](classes.md#1462-method-parameters)) are part of a signature. Thus, `F(int)`, `F(ref int)`, and `F(out int)` are all unique signatures. However, `F(ref int)` and `F(out int)` cannot be declared within the same interface because their signatures differ solely by `ref` and `out`. Also, note that the return type and the `params` modifier are not part of a signature, so it is not possible to overload solely based on return type or on the inclusion or exclusion of the `params` modifier. As such, the declarations of the methods `F(int)` and `F(params string[])` identified above, result in a compile-time error. -> -> *end example* +> Note that any `in`, `out`, and `ref` parameter modifiers ([§15.6.2](classes.md#1562-method-parameters)) are part of a signature. Thus, `F(int)`, `F(in int)`, `F(out int)` , and `F(ref int)` are all unique signatures. However, `F(in int)`, `F(out int)` , and `F(ref int)` cannot be declared within the same interface because their signatures differ solely by `in`, `out`, and `ref`. Also, note that the return type and the `params` modifier are not part of a signature, so it is not possible to overload solely based on return type or on the inclusion or exclusion of the `params` modifier. As such, the declarations of the methods `F(int)` and `F(params string[])` identified above, result in a compile-time error. *end example* ## 7.7 Scopes @@ -573,31 +576,31 @@ The types `object` and `dynamic` are not distinguished when comparing signatures The ***scope*** of a name is the region of program text within which it is possible to refer to the entity declared by the name without qualification of the name. Scopes can be *nested*, and an inner scope may redeclare the meaning of a name from an outer scope. (This does not, however, remove the restriction imposed by [§7.3](basic-concepts.md#73-declarations) that within a nested block it is not possible to declare a local variable or local constant with the same name as a local variable or local constant in an enclosing block.) The name from the outer scope is then said to be ***hidden*** in the region of program text covered by the inner scope, and access to the outer name is only possible by qualifying the name. -- The scope of a namespace member declared by a *namespace_member_declaration* ([§13.6](namespaces.md#136-namespace-member-declarations)) with no enclosing *namespace_declaration* is the entire program text. +- The scope of a namespace member declared by a *namespace_member_declaration* ([§14.6](namespaces.md#146-namespace-member-declarations)) with no enclosing *namespace_declaration* is the entire program text. - The scope of a namespace member declared by a *namespace_member_declaration* within a *namespace_declaration* whose fully qualified name is `N`, is the *namespace_body* of every *namespace_declaration* whose fully qualified name is `N` or starts with `N`, followed by a period. -- The scope of a name defined by an *extern_alias_directive* ([§13.4](namespaces.md#134-extern-alias-directives)) extends over the *using_directive*s, *global_attributes* and *namespace_member_declaration*s of its immediately containing *compilation_unit* or *namespace_body*. An *extern_alias_directive* does not contribute any new members to the underlying declaration space. In other words, an *extern_alias_directive* is not transitive, but, rather, affects only the *compilation_unit* or *namespace_body* in which it occurs. -- The scope of a name defined or imported by a *using_directive* ([§13.5](namespaces.md#135-using-directives)) extends over the *global_attributes* and *namespace_member_declaration*s of the *compilation_unit* or *namespace_body* in which the *using_directive* occurs. A *using_directive* may make zero or more namespace or type names available within a particular *compilation_unit* or *namespace_body*, but does not contribute any new members to the underlying declaration space. In other words, a *using_directive* is not transitive but rather affects only the *compilation_unit* or *namespace_body* in which it occurs. -- The scope of a type parameter declared by a *type_parameter_list* on a *class_declaration* ([§14.2](classes.md#142-class-declarations)) is the *class_base*, *type_parameter_constraints_clauses*, and *class_body* of that *class_declaration*. +- The scope of a name defined by an *extern_alias_directive* ([§14.4](namespaces.md#144-extern-alias-directives)) extends over the *using_directive*s, *global_attributes* and *namespace_member_declaration*s of its immediately containing *compilation_unit* or *namespace_body*. An *extern_alias_directive* does not contribute any new members to the underlying declaration space. In other words, an *extern_alias_directive* is not transitive, but, rather, affects only the *compilation_unit* or *namespace_body* in which it occurs. +- The scope of a name defined or imported by a *using_directive* ([§14.5](namespaces.md#145-using-directives)) extends over the *global_attributes* and *namespace_member_declaration*s of the *compilation_unit* or *namespace_body* in which the *using_directive* occurs. A *using_directive* may make zero or more namespace or type names available within a particular *compilation_unit* or *namespace_body*, but does not contribute any new members to the underlying declaration space. In other words, a *using_directive* is not transitive but rather affects only the *compilation_unit* or *namespace_body* in which it occurs. +- The scope of a type parameter declared by a *type_parameter_list* on a *class_declaration* ([§15.2](classes.md#152-class-declarations)) is the *class_base*, *type_parameter_constraints_clauses*, and *class_body* of that *class_declaration*. > *Note*: Unlike members of a class, this scope does not extend to derived classes. *end note* -- The scope of a type parameter declared by a *type_parameter_list* on a *struct_declaration* ([§15.2](structs.md#152-struct-declarations)) is the *struct_interfaces*, *type_parameter_constraints_clause*s, and *struct_body* of that *struct_declaration*. -- The scope of a type parameter declared by a *type_parameter_list* on an *interface_declaration* ([§17.2](interfaces.md#172-interface-declarations)) is the *interface_base*, *type_parameter_constraints_clause*s, and *interface_body* of that *interface_declaration*. -- The scope of a type parameter declared by a *type_parameter_list* on a *delegate_declaration* ([§19.2](delegates.md#192-delegate-declarations)) is the *return_type*, *formal_parameter_list*, and *type_parameter_constraints_clause*s of that *delegate_declaration*. -- The scope of a type parameter declared by a *type_parameter_list* on a *method_declaration* ([§14.6.1](classes.md#1461-general)) is the *method_declaration*. -- The scope of a member declared by a *class_member_declaration* ([§14.3.1](classes.md#1431-general)) is the *class_body* in which the declaration occurs. In addition, the scope of a class member extends to the *class_body* of those derived classes that are included in the accessibility domain ([§7.5.3](basic-concepts.md#753-accessibility-domains)) of the member. -- The scope of a member declared by a *struct_member_declaration* ([§15.3](structs.md#153-struct-members)) is the *struct_body* in which the declaration occurs. - -- The scope of a member declared by an *enum_member_declaration* ([§18.4](enums.md#184-enum-members)) is the *enum_body* in which the declaration occurs. -- The scope of a parameter declared in a *method_declaration* ([§14.6](classes.md#146-methods)) is the *method_body* of that *method_declaration*. -- The scope of a parameter declared in an *indexer_declaration* ([§14.9](classes.md#149-indexers)) is the *indexer_body* of that *indexer_declaration*. -- The scope of a parameter declared in an *operator_declaration* ([§14.10](classes.md#1410-operators)) is the *operator_body* of that *operator_declaration*. -- The scope of a parameter declared in a *constructor_declaration* ([§14.11](classes.md#1411-instance-constructors)) is the *constructor_initializer* and *block* of that *constructor_declaration*. -- The scope of a parameter declared in a *lambda_expression* ([§11.17](expressions.md#1117-anonymous-function-expressions)) is the *lambda_expression_body* of that *lambda_expression*. -- The scope of a parameter declared in an *anonymous_method_expression* ([§11.17](expressions.md#1117-anonymous-function-expressions)) is the *block* of that *anonymous_method_expression*. -- The scope of a label declared in a *labeled_statement* ([§12.5](statements.md#125-labeled-statements)) is the *block* in which the declaration occurs. -- The scope of a local variable declared in a *local_variable_declaration* ([§12.6.2](statements.md#1262-local-variable-declarations)) is the *block* in which the declaration occurs. -- The scope of a local variable declared in a *switch_block* of a `switch` statement ([§12.8.3](statements.md#1283-the-switch-statement)) is the *switch_block*. -- The scope of a local variable declared in a *for_initializer* of a `for` statement ([§12.9.4](statements.md#1294-the-for-statement)) is the *for_initializer*, the *for_condition*, the *for_iterator*, and the contained *statement* of the `for` statement. -- The scope of a local constant declared in a *local_constant_declaration* ([§12.6.3](statements.md#1263-local-constant-declarations)) is the *block* in which the declaration occurs. It is a compile-time error to refer to a local constant in a textual position that precedes its *constant_declarator*. +- The scope of a type parameter declared by a *type_parameter_list* on a *struct_declaration* ([§16.2](structs.md#162-struct-declarations)) is the *struct_interfaces*, *type_parameter_constraints_clause*s, and *struct_body* of that *struct_declaration*. +- The scope of a type parameter declared by a *type_parameter_list* on an *interface_declaration* ([§18.2](interfaces.md#182-interface-declarations)) is the *interface_base*, *type_parameter_constraints_clause*s, and *interface_body* of that *interface_declaration*. +- The scope of a type parameter declared by a *type_parameter_list* on a *delegate_declaration* ([§20.2](delegates.md#202-delegate-declarations)) is the *return_type*, *formal_parameter_list*, and *type_parameter_constraints_clause*s of that *delegate_declaration*. +- The scope of a type parameter declared by a *type_parameter_list* on a *method_declaration* ([§15.6.1](classes.md#1561-general)) is the *method_declaration*. +- The scope of a member declared by a *class_member_declaration* ([§15.3.1](classes.md#1531-general)) is the *class_body* in which the declaration occurs. In addition, the scope of a class member extends to the *class_body* of those derived classes that are included in the accessibility domain ([§7.5.3](basic-concepts.md#753-accessibility-domains)) of the member. +- The scope of a member declared by a *struct_member_declaration* ([§16.3](structs.md#163-struct-members)) is the *struct_body* in which the declaration occurs. + +- The scope of a member declared by an *enum_member_declaration* ([§19.4](enums.md#194-enum-members)) is the *enum_body* in which the declaration occurs. +- The scope of a parameter declared in a *method_declaration* ([§15.6](classes.md#156-methods)) is the *method_body* or *ref_method_body* of that *method_declaration*. +- The scope of a parameter declared in an *indexer_declaration* ([§15.9](classes.md#159-indexers)) is the *indexer_body* of that *indexer_declaration*. +- The scope of a parameter declared in an *operator_declaration* ([§15.10](classes.md#1510-operators)) is the *operator_body* of that *operator_declaration*. +- The scope of a parameter declared in a *constructor_declaration* ([§15.11](classes.md#1511-instance-constructors)) is the *constructor_initializer* and *block* of that *constructor_declaration*. +- The scope of a parameter declared in a *lambda_expression* ([§12.19](expressions.md#1219-anonymous-function-expressions)) is the *lambda_expression_body* of that *lambda_expression*. +- The scope of a parameter declared in an *anonymous_method_expression* ([§12.19](expressions.md#1219-anonymous-function-expressions)) is the *block* of that *anonymous_method_expression*. +- The scope of a label declared in a *labeled_statement* ([§13.5](statements.md#135-labeled-statements)) is the *block* in which the declaration occurs. +- The scope of a local variable declared in a *local_variable_declaration* ([§13.6.2](statements.md#1362-local-variable-declarations)) is the *block* in which the declaration occurs. +- The scope of a local variable declared in a *switch_block* of a `switch` statement ([§13.8.3](statements.md#1383-the-switch-statement)) is the *switch_block*. +- The scope of a local variable declared in a *for_initializer* of a `for` statement ([§13.9.4](statements.md#1394-the-for-statement)) is the *for_initializer*, *for_condition*, *for_iterator*, and *embedded_statement* of the `for` statement. +- The scope of a local constant declared in a *local_constant_declaration* ([§13.6.3](statements.md#1363-local-constant-declarations)) is the *block* in which the declaration occurs. It is a compile-time error to refer to a local constant in a textual position that precedes its *constant_declarator*. - The scope of a variable declared as part of a *foreach_statement*, *using_statement*, *lock_statement* or *query_expression* is determined by the expansion of the given construct. Within the scope of a namespace, class, struct, or enumeration member it is possible to refer to the member in a textual position that precedes the declaration of the member. @@ -621,7 +624,7 @@ Within the scope of a namespace, class, struct, or enumeration member it is poss > > *end example* -Within the scope of a local variable, it is a compile-time error to refer to the local variable in a textual position that precedes the *local_variable_declarator* of the local variable. +Within the scope of a local variable, it is a compile-time error to refer to the local variable in a textual position that precedes its declarator. > *Example*: > @@ -650,7 +653,7 @@ Within the scope of a local variable, it is a compile-time error to refer to the > } > ``` > -> In the `F` method above, the first assignment to `i` specifically does not refer to the field declared in the outer scope. Rather, it refers to the local variable and it results in a compile-time error because it textually precedes the declaration of the variable. In the `G` method, the use of `j` in the initializer for the declaration of `j` is valid because the use does not precede the *local_variable_declarator*. In the `H` method, a subsequent *local_variable_declarator* correctly refers to a local variable declared in an earlier *local_variable_declarator* within the same *local_variable_declaration*. +> In the `F` method above, the first assignment to `i` specifically does not refer to the field declared in the outer scope. Rather, it refers to the local variable and it results in a compile-time error because it textually precedes the declaration of the variable. In the `G` method, the use of `j` in the initializer for the declaration of `j` is valid because the use does not precede the declarator. In the `H` method, a subsequent declarator correctly refers to a local variable declared in an earlier declarator within the same *local_variable_declaration*. > > *end example* @@ -752,7 +755,7 @@ Name hiding through inheritance occurs when classes or structs redeclare names t - A method introduced in a class or struct hides all non-method base class members with the same name, and all base class methods with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). - An indexer introduced in a class or struct hides all base class indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . -The rules governing operator declarations ([§14.10](classes.md#1410-operators)) make it impossible for a derived class to declare an operator with the same signature as an operator in a base class. Thus, operators never hide one another. +The rules governing operator declarations ([§15.10](classes.md#1510-operators)) make it impossible for a derived class to declare an operator with the same signature as an operator in a base class. Thus, operators never hide one another. Contrary to hiding a name from an outer scope, hiding a visible name from an inherited scope causes a warning to be reported. @@ -853,7 +856,7 @@ Following resolution as described below, the *namespace_or_type_name* of a *name A *type_name* is a *namespace_or_type_name* that refers to a type. Following resolution as described below, the *namespace_or_type_name* of a *type_name* shall refer to a type, or otherwise a compile-time error occurs. -If the *namespace_or_type_name* is a *qualified_alias_member* its meaning is as described in [§13.8.1](namespaces.md#1381-general). Otherwise, a *namespace_or_type_name* has one of four forms: +If the *namespace_or_type_name* is a *qualified_alias_member* its meaning is as described in [§14.8.1](namespaces.md#1481-general). Otherwise, a *namespace_or_type_name* has one of four forms: - `I` - `I` @@ -864,10 +867,10 @@ where `I` is a single identifier, `N` is a *namespace_or_type_name* and ``: - - If `x` is zero and the *namespace_or_type_name* appears within a generic method declaration ([§14.6](classes.md#146-methods)) but outside the *attributes* of its *method-header,* and if that declaration includes a type parameter ([§14.2.3](classes.md#1423-type-parameters)) with name `I`, then the *namespace_or_type_name* refers to that type parameter. - - Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§14.3.2](classes.md#1432-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any): + - If `x` is zero and the *namespace_or_type_name* appears within a generic method declaration ([§15.6](classes.md#156-methods)) but outside the *attributes* of its *method-header,* and if that declaration includes a type parameter ([§15.2.3](classes.md#1523-type-parameters)) with name `I`, then the *namespace_or_type_name* refers to that type parameter. + - Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any): - If `x` is zero and the declaration of `T` includes a type parameter with name `I`, then the *namespace_or_type_name* refers to that type parameter. - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then the *namespace_or_type_name* refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. > *Note*: Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the *namespace_or_type_name*. *end note* @@ -887,13 +890,13 @@ The meaning of a *namespace_or_type_name* is determined as follows: - If `x` is zero and `N` refers to a namespace and `N` contains a nested namespace with name `I`, then the *namespace_or_type_name* refers to that nested namespace. - Otherwise, if `N` refers to a namespace and `N` contains an accessible type having name `I` and `x` type parameters, then the *namespace_or_type_name* refers to that type constructed with the given type arguments. - Otherwise, if `N` refers to a (possibly constructed) class or struct type and `N` or any of its base classes contain a nested accessible type having name `I` and `x` type parameters, then the *namespace_or_type_name* refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. - > *Note*: If the meaning of `N.I` is being determined as part of resolving the base class specification of `N` then the direct base class of `N` is considered to be `object` ([§14.2.4.2](classes.md#14242-base-classes)). *end note* + > *Note*: If the meaning of `N.I` is being determined as part of resolving the base class specification of `N` then the direct base class of `N` is considered to be `object` ([§15.2.4.2](classes.md#15242-base-classes)). *end note* - Otherwise, `N.I` is an invalid *namespace_or_type_name*, and a compile-time error occurs. -A *namespace_or_type_name* is permitted to reference a static class ([§14.2.2.4](classes.md#14224-static-classes)) only if +A *namespace_or_type_name* is permitted to reference a static class ([§15.2.2.4](classes.md#15224-static-classes)) only if - The *namespace_or_type_name* is the `T` in a *namespace_or_type_name* of the form `T.I`, or -- The *namespace_or_type_name* is the `T` in a *typeof_expression* ([§11.7.16](expressions.md#11716-the-typeof-operator)) of the form `typeof(T)` +- The *namespace_or_type_name* is the `T` in a *typeof_expression* ([§12.8.17](expressions.md#12817-the-typeof-operator)) of the form `typeof(T)` ### 7.8.2 Unqualified names @@ -901,7 +904,7 @@ Every namespace declaration and type declaration has an ***unqualified name*** d - For a namespace declaration, the unqualified name is the *qualified_identifier* specified in the declaration. - For a type declaration with no *type_parameter_list*, the unqualified name is the *identifier* specified in the declaration. -- For a type declaration with K type parameters, the unqualified name is the *identifier* specified in the declaration, followed by the *generic_dimension_specifier* ([§11.7.16](expressions.md#11716-the-typeof-operator)) for K type parameters. +- For a type declaration with K type parameters, the unqualified name is the *identifier* specified in the declaration, followed by the *generic_dimension_specifier* ([§12.8.17](expressions.md#12817-the-typeof-operator)) for K type parameters. ### 7.8.3 Fully qualified names @@ -914,7 +917,7 @@ In other words, the fully qualified name of `N` is the complete hierarchical pa - It is an error for both a namespace declaration and a type declaration to have the same fully qualified name. - It is an error for two different kinds of type declarations to have the same fully qualified name (for example, if both a struct and class declaration have the same fully qualified name). -- It is an error for a type declaration without the partial modifier to have the same fully qualified name as another type declaration ([§14.2.7](classes.md#1427-partial-declarations)). +- It is an error for a type declaration without the partial modifier to have the same fully qualified name as another type declaration ([§15.2.7](classes.md#1527-partial-declarations)). > *Example*: The example below shows several namespace and type declarations along with their associated fully qualified names. > @@ -955,7 +958,7 @@ C# employs automatic memory management, which frees developers from manually all 1. When the object is created, memory is allocated for it, the constructor is run, and the object is considered ***live***. 1. If neither the object nor any of its instance fields can be accessed by any possible continuation of execution, other than the running of finalizers, the object is considered ***no longer in use*** and it becomes eligible for finalization. > *Note*: The C# compiler and the garbage collector might choose to analyze code to determine which references to an object might be used in the future. For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, the garbage collector might (but is not required to) treat the object as no longer in use. *end note* -1. Once the object is eligible for finalization, at some unspecified later time the finalizer ([§14.13](classes.md#1413-finalizers)) (if any) for the object is run. Under normal circumstances the finalizer for the object is run once only, though implementation-specific APIs may allow this behavior to be overridden. +1. Once the object is eligible for finalization, at some unspecified later time the finalizer ([§15.13](classes.md#1513-finalizers)) (if any) for the object is run. Under normal circumstances the finalizer for the object is run once only, though implementation-specific APIs may allow this behavior to be overridden. 1. Once the finalizer for an object is run, if neither the object nor any of its instance fields can be accessed by any possible continuation of execution, including the running of finalizers, the object is considered inaccessible and the object becomes eligible for collection. > *Note*: An object which could previously not be accessed may become accessible again due to its finalizer. An example of this is provided below. *end note* 1. Finally, at some time after the object becomes eligible for collection, the garbage collector frees the memory associated with that object. @@ -1090,8 +1093,8 @@ The behavior of the garbage collector can be controlled, to some degree, via sta ## 7.10 Execution order -Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A ***side effect*** is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects shall be preserved are references to volatile fields ([§14.5.4](classes.md#1454-volatile-fields)), `lock` statements ([§12.13](statements.md#1213-the-lock-statement)), and thread creation and termination. The execution environment is free to change the order of execution of a C# program, subject to the following constraints: +Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A ***side effect*** is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects shall be preserved are references to volatile fields ([§15.5.4](classes.md#1554-volatile-fields)), `lock` statements ([§13.13](statements.md#1313-the-lock-statement)), and thread creation and termination. The execution environment is free to change the order of execution of a C# program, subject to the following constraints: - Data dependence is preserved within a thread of execution. That is, the value of each variable is computed as if all statements in the thread were executed in original program order. -- Initialization ordering rules are preserved ([§14.5.5](classes.md#1455-field-initialization), [§14.5.6](classes.md#1456-variable-initializers)). -- The ordering of side effects is preserved with respect to volatile reads and writes ([§14.5.4](classes.md#1454-volatile-fields)). Additionally, the execution environment need not evaluate part of an expression if it can deduce that that expression’s value is not used and that no needed side effects are produced (including any caused by calling a method or accessing a volatile field). When program execution is interrupted by an asynchronous event (such as an exception thrown by another thread), it is not guaranteed that the observable side effects are visible in the original program order. +- Initialization ordering rules are preserved ([§15.5.5](classes.md#1555-field-initialization), [§15.5.6](classes.md#1556-variable-initializers)). +- The ordering of side effects is preserved with respect to volatile reads and writes ([§15.5.4](classes.md#1554-volatile-fields)). Additionally, the execution environment need not evaluate part of an expression if it can deduce that that expression’s value is not used and that no needed side effects are produced (including any caused by calling a method or accessing a volatile field). When program execution is interrupted by an asynchronous event (such as an exception thrown by another thread), it is not guaranteed that the observable side effects are visible in the original program order. diff --git a/standard/classes.md b/standard/classes.md index da9c2bbc5..6d81a34ae 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1,14 +1,14 @@ -# 14 Classes +# 15 Classes -## 14.1 General +## 15.1 General A class is a data structure that may contain data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, finalizers, and static constructors), and nested types. Class types support inheritance, a mechanism whereby a ***derived class*** can extend and specialize a ***base class***. -## 14.2 Class declarations +## 15.2 Class declarations -### 14.2.1 General +### 15.2.1 General -A *class_declaration* is a *type_declaration* ([§13.7](namespaces.md#137-type-declarations)) that declares a new class. +A *class_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-type-declarations)) that declares a new class. ```ANTLR class_declaration @@ -18,15 +18,15 @@ class_declaration ; ``` -A *class_declaration* consists of an optional set of *attributes* ([§21](attributes.md#21-attributes)), followed by an optional set of *class_modifier*s ([§14.2.2](classes.md#1422-class-modifiers)), followed by an optional `partial` modifier ([§14.2.7](classes.md#1427-partial-declarations)), followed by the keyword `class` and an *identifier* that names the class, followed by an optional *type_parameter_list* ([§14.2.3](classes.md#1423-type-parameters)), followed by an optional *class_base* specification ([§14.2.4](classes.md#1424-class-base-specification)), followed by an optional set of *type_parameter_constraints_clause*s ([§14.2.5](classes.md#1425-type-parameter-constraints)), followed by a *class_body* ([§14.2.6](classes.md#1426-class-body)), optionally followed by a semicolon. +A *class_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *class_modifier*s ([§15.2.2](classes.md#1522-class-modifiers)), followed by an optional `partial` modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword `class` and an *identifier* that names the class, followed by an optional *type_parameter_list* ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *class_base* specification ([§15.2.4](classes.md#1524-class-base-specification)), followed by an optional set of *type_parameter_constraints_clause*s ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *class_body* ([§15.2.6](classes.md#1526-class-body)), optionally followed by a semicolon. A class declaration shall not supply a *type_parameter_constraints_clause*s unless it also supplies a *type_parameter_list*. A class declaration that supplies a *type_parameter_list* is a generic class declaration. Additionally, any class nested inside a generic class declaration or a generic struct declaration is itself a generic class declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)). -### 14.2.2 Class modifiers +### 15.2.2 Class modifiers -#### 14.2.2.1 General +#### 15.2.2.1 General A *class_declaration* may optionally include a sequence of class modifiers: @@ -44,19 +44,19 @@ class_modifier ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). It is a compile-time error for the same modifier to appear multiple times in a class declaration. -The `new` modifier is permitted on nested classes. It specifies that the class hides an inherited member by the same name, as described in [§14.3.5](classes.md#1435-the-new-modifier). It is a compile-time error for the `new` modifier to appear on a class declaration that is not a nested class declaration. +The `new` modifier is permitted on nested classes. It specifies that the class hides an inherited member by the same name, as described in [§15.3.5](classes.md#1535-the-new-modifier). It is a compile-time error for the `new` modifier to appear on a class declaration that is not a nested class declaration. The `public`, `protected`, `internal`, and `private` modifiers control the accessibility of the class. Depending on the context in which the class declaration occurs, some of these modifiers might not be permitted ([§7.5.2](basic-concepts.md#752-declared-accessibility)). -When a partial type declaration ([§14.2.7](classes.md#1427-partial-declarations)) includes an accessibility specification (via the `public`, `protected`, `internal`, and `private` modifiers), that specification shall agree with all other parts that include an accessibility specification. If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility ([§7.5.2](basic-concepts.md#752-declared-accessibility)). +When a partial type declaration ([§15.2.7](classes.md#1527-partial-declarations)) includes an accessibility specification (via the `public`, `protected`, `internal`, and `private` modifiers), that specification shall agree with all other parts that include an accessibility specification. If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility ([§7.5.2](basic-concepts.md#752-declared-accessibility)). The `abstract`, `sealed`, and `static` modifiers are discussed in the following subclauses. -#### 14.2.2.2 Abstract classes +#### 15.2.2.2 Abstract classes The `abstract` modifier is used to indicate that a class is incomplete and that it is intended to be used only as a base class. An ***abstract class*** differs from a ***non-abstract class*** in the following ways: @@ -93,9 +93,9 @@ When a non-abstract class is derived from an abstract class, the non-abstract cl > > *end example* -If one or more parts of a partial type declaration ([§14.2.7](classes.md#1427-partial-declarations)) of a class include the `abstract` modifier, the class is abstract. Otherwise, the class is non-abstract. +If one or more parts of a partial type declaration ([§15.2.7](classes.md#1527-partial-declarations)) of a class include the `abstract` modifier, the class is abstract. Otherwise, the class is non-abstract. -#### 14.2.2.3 Sealed classes +#### 15.2.2.3 Sealed classes The `sealed` modifier is used to prevent derivation from a class. A compile-time error occurs if a sealed class is specified as the base class of another class. @@ -103,46 +103,46 @@ A sealed class cannot also be an abstract class. > *Note*: The `sealed` modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. In particular, because a sealed class is known to never have any derived classes, it is possible to transform virtual function member invocations on sealed class instances into non-virtual invocations. *end note* -If one or more parts of a partial type declaration ([§14.2.7](classes.md#1427-partial-declarations)) of a class include the `sealed` modifier, the class is sealed. Otherwise, the class is unsealed. +If one or more parts of a partial type declaration ([§15.2.7](classes.md#1527-partial-declarations)) of a class include the `sealed` modifier, the class is sealed. Otherwise, the class is unsealed. -#### 14.2.2.4 Static classes +#### 15.2.2.4 Static classes -##### 14.2.2.4.1 General +##### 15.2.2.4.1 General -The `static` modifier is used to mark the class being declared as a ***static class***. A static class shall not be instantiated, shall not be used as a type and shall contain only static members. Only a static class can contain declarations of extension methods ([§14.6.10](classes.md#14610-extension-methods)). +The `static` modifier is used to mark the class being declared as a ***static class***. A static class shall not be instantiated, shall not be used as a type and shall contain only static members. Only a static class can contain declarations of extension methods ([§15.6.10](classes.md#15610-extension-methods)). A static class declaration is subject to the following restrictions: - A static class shall not include a `sealed` or `abstract` modifier. (However, since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.) -- A static class shall not include a *class_base* specification ([§14.2.4](classes.md#1424-class-base-specification)) and cannot explicitly specify a base class or a list of implemented interfaces. A static class implicitly inherits from type `object`. -- A static class shall only contain static members ([§14.3.8](classes.md#1438-static-and-instance-members)). +- A static class shall not include a *class_base* specification ([§15.2.4](classes.md#1524-class-base-specification)) and cannot explicitly specify a base class or a list of implemented interfaces. A static class implicitly inherits from type `object`. +- A static class shall only contain static members ([§15.3.8](classes.md#1538-static-and-instance-members)). > *Note*: All constants and nested types are classified as static members. *end note* - A static class shall not have members with `protected`, `private protected`, or `protected internal` declared accessibility. It is a compile-time error to violate any of these restrictions. -A static class has no instance constructors. It is not possible to declare an instance constructor in a static class, and no default instance constructor ([§14.11.5](classes.md#14115-default-constructors)) is provided for a static class. +A static class has no instance constructors. It is not possible to declare an instance constructor in a static class, and no default instance constructor ([§15.11.5](classes.md#15115-default-constructors)) is provided for a static class. The members of a static class are not automatically static, and the member declarations shall explicitly include a `static` modifier (except for constants and nested types). When a class is nested within a static outer class, the nested class is not a static class unless it explicitly includes a `static` modifier. -If one or more parts of a partial type declaration ([§14.2.7](classes.md#1427-partial-declarations)) of a class include the `static` modifier, the class is static. Otherwise, the class is not static. +If one or more parts of a partial type declaration ([§15.2.7](classes.md#1527-partial-declarations)) of a class include the `static` modifier, the class is static. Otherwise, the class is not static. -##### 14.2.2.4.2 Referencing static class types +##### 15.2.2.4.2 Referencing static class types A *namespace_or_type_name* ([§7.8](basic-concepts.md#78-namespace-and-type-names)) is permitted to reference a static class if - The *namespace_or_type_name* is the `T` in a *namespace_or_type_name* of the form `T.I`, or -- The *namespace_or_type-name* is the `T` in a *typeof_expression* ([§11.7.16](expressions.md#11716-the-typeof-operator)) of the form `typeof(T)`. +- The *namespace_or_type-name* is the `T` in a *typeof_expression* ([§12.8.17](expressions.md#12817-the-typeof-operator)) of the form `typeof(T)`. -A *primary_expression* ([§11.7](expressions.md#117-primary-expressions)) is permitted to reference a static class if +A *primary_expression* ([§12.8](expressions.md#128-primary-expressions)) is permitted to reference a static class if -- The *primary_expression* is the `E` in a *member_access* ([§11.7.6](expressions.md#1176-member-access)) of the form `E.I`. +- The *primary_expression* is the `E` in a *member_access* ([§12.8.7](expressions.md#1287-member-access)) of the form `E.I`. In any other context, it is a compile-time error to reference a static class. -> *Note*: For example, it is an error for a static class to be used as a base class, a constituent type ([§14.3.7](classes.md#1437-constituent-types)) of a member, a generic type argument, or a type parameter constraint. Likewise, a static class cannot be used in an array type, a pointer type, a new expression, a cast expression, an is expression, an as expression, a `sizeof` expression, or a default value expression. *end note* +> *Note*: For example, it is an error for a static class to be used as a base class, a constituent type ([§15.3.7](classes.md#1537-constituent-types)) of a member, a generic type argument, or a type parameter constraint. Likewise, a static class cannot be used in an array type, a new expression, a cast expression, an is expression, an as expression, a `sizeof` expression, or a default value expression. *end note* -### 14.2.3 Type parameters +### 15.2.3 Type parameters A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. By constrast, a type argument ([§8.4.2](types.md#842-type-arguments)) is the type that is substituted for the type parameter when a constructed type is created. @@ -161,13 +161,13 @@ type_parameters Each type parameter in a class declaration defines a name in the declaration space ([§7.3](basic-concepts.md#73-declarations)) of that class. Thus, it cannot have the same name as another type parameter of that class or a member declared in that class. A type parameter cannot have the same name as the type itself. -Two partial generic type declarations (in the same program) contribute to the same unbound generic type if they have the same fully qualified name (which includes a *generic_dimension_specifier* ([§11.7.16](expressions.md#11716-the-typeof-operator)) for the number of type parameters) ([§7.8.3](basic-concepts.md#783-fully-qualified-names)). Two such partial type declarations shall specify the same name for each type parameter, in order. +Two partial generic type declarations (in the same program) contribute to the same unbound generic type if they have the same fully qualified name (which includes a *generic_dimension_specifier* ([§12.8.17](expressions.md#12817-the-typeof-operator)) for the number of type parameters) ([§7.8.3](basic-concepts.md#783-fully-qualified-names)). Two such partial type declarations shall specify the same name for each type parameter, in order. -### 14.2.4 Class base specification +### 15.2.4 Class base specification -#### 14.2.4.1 General +#### 15.2.4.1 General -A class declaration may include a *class_base* specification, which defines the direct base class of the class and the interfaces ([§17](interfaces.md#17-interfaces)) directly implemented by the class. +A class declaration may include a *class_base* specification, which defines the direct base class of the class and the interfaces ([§18](interfaces.md#18-interfaces)) directly implemented by the class. ```ANTLR class_base @@ -181,9 +181,9 @@ interface_type_list ; ``` -#### 14.2.4.2 Base classes +#### 15.2.4.2 Base classes -When a *class_type* is included in the *class_base*, it specifies the direct base class of the class being declared. If a non-partial class declaration has no *class_base*, or if the *class_base* lists only interface types, the direct base class is assumed to be `object`. When a partial class declaration includes a base class specification, that base class specification shall reference the same type as all other parts of that partial type that include a base class specification. If no part of a partial class includes a base class specification, the base class is `object`. A class inherits members from its direct base class, as described in [§14.3.4](classes.md#1434-inheritance). +When a *class_type* is included in the *class_base*, it specifies the direct base class of the class being declared. If a non-partial class declaration has no *class_base*, or if the *class_base* lists only interface types, the direct base class is assumed to be `object`. When a partial class declaration includes a base class specification, that base class specification shall reference the same type as all other parts of that partial type that include a base class specification. If no part of a partial class includes a base class specification, the base class is `object`. A class inherits members from its direct base class, as described in [§15.3.4](classes.md#1534-inheritance). > *Example*: In the following code > @@ -197,7 +197,7 @@ When a *class_type* is included in the *class_base*, it specifies the direct bas > > *end example* -For a constructed class type, including a nested type declared within a generic type declaration ([§14.3.9.7](classes.md#14397-nested-types-in-generic-classes)), if a base class is specified in the generic class declaration, the base class of the constructed type is obtained by substituting, for each *type_parameter* in the base class declaration, the corresponding *type_argument* of the constructed type. +For a constructed class type, including a nested type declared within a generic type declaration ([§15.3.9.7](classes.md#15397-nested-types-in-generic-classes)), if a base class is specified in the generic class declaration, the base class of the constructed type is obtained by substituting, for each *type_parameter* in the base class declaration, the corresponding *type_argument* of the constructed type. > *Example*: Given the generic class declarations > @@ -233,7 +233,7 @@ The base class specified in a class declaration can be a constructed class type The direct base class of a class type shall be at least as accessible as the class type itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). For example, it is a compile-time error for a public class to derive from a private or internal class. -The direct base class of a class type shall not be any of the following types: `System.Array`, `System.Delegate`, `System.Enum`, or `System.ValueType`. Furthermore, a generic class declaration shall not use `System.Attribute` as a direct or indirect base class ([§21.2.1](attributes.md#2121-general)). +The direct base class of a class type shall not be any of the following types: `System.Array`, `System.Delegate`, `System.Enum`, or `System.ValueType`. Furthermore, a generic class declaration shall not use `System.Attribute` as a direct or indirect base class ([§22.2.1](attributes.md#2221-general)). In determining the meaning of the direct base class specification `A` of a class `B`, the direct base class of `B` is temporarily assumed to be `object`, which ensures that the meaning of a base class specification cannot recursively depend on itself. @@ -333,11 +333,11 @@ It is not possible to derive from a sealed class. > > *end example* -#### 14.2.4.3 Interface implementations +#### 15.2.4.3 Interface implementations -A *class_base* specification may include a list of interface types, in which case the class is said to implement the given interface types. For a constructed class type, including a nested type declared within a generic type declaration ([§14.3.9.7](classes.md#14397-nested-types-in-generic-classes)), each implemented interface type is obtained by substituting, for each *type_parameter* in the given interface, the corresponding *type_argument* of the constructed type. +A *class_base* specification may include a list of interface types, in which case the class is said to implement the given interface types. For a constructed class type, including a nested type declared within a generic type declaration ([§15.3.9.7](classes.md#15397-nested-types-in-generic-classes)), each implemented interface type is obtained by substituting, for each *type_parameter* in the given interface, the corresponding *type_argument* of the constructed type. -The set of interfaces for a type declared in multiple parts ([§14.2.7](classes.md#1427-partial-declarations)) is the union of the interfaces specified on each part. A particular interface can only be named once on each part, but multiple parts can name the same base interface(s). There shall only be one implementation of each member of any given interface. +The set of interfaces for a type declared in multiple parts ([§15.2.7](classes.md#1527-partial-declarations)) is the union of the interfaces specified on each part. A particular interface can only be named once on each part, but multiple parts can name the same base interface(s). There shall only be one implementation of each member of any given interface. > *Example*: In the following: > @@ -371,7 +371,7 @@ Typically, each part provides an implementation of the interface(s) declared on > > *end example* -The base interfaces specified in a class declaration can be constructed interface types ([§8.4](types.md#84-constructed-types), [§17.2](interfaces.md#172-interface-declarations)). A base interface cannot be a type parameter on its own, though it can involve the type parameters that are in scope. +The base interfaces specified in a class declaration can be constructed interface types ([§8.4](types.md#84-constructed-types), [§18.2](interfaces.md#182-interface-declarations)). A base interface cannot be a type parameter on its own, though it can involve the type parameters that are in scope. > *Example*: The following code illustrates how a class can implement and extend constructed types: > @@ -385,9 +385,9 @@ The base interfaces specified in a class declaration can be constructed interfac > > *end example* -Interface implementations are discussed further in [§17.6](interfaces.md#176-interface-implementations). +Interface implementations are discussed further in [§18.6](interfaces.md#186-interface-implementations). -### 14.2.5 Type parameter constraints +### 15.2.5 Type parameter constraints Generic type and method declarations can optionally specify type parameter constraints by including *type_parameter_constraints_clause*s. @@ -415,6 +415,7 @@ primary_constraint : class_type | 'class' | 'struct' + | 'unmanaged' ; secondary_constraints @@ -433,15 +434,21 @@ Each *type_parameter_constraints_clause* consists of the token `where`, followed The list of constraints given in a `where` clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, `new()`. -A primary constraint can be a class type or the ***reference type constraint*** `class` or the ***value type constraint*** `struct`. A secondary constraint can be a *type_parameter* or *interface_type*. +A primary constraint can be a class type, the ***reference type constraint*** `class`, the ***value type constraint*** `struct`, or the ***unmanaged type constraint*** `unmanaged`. + +A secondary constraint can be a *type_parameter* or *interface_type*. The reference type constraint specifies that a type argument used for the type parameter shall be a reference type. All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint. -The value type constraint specifies that a type argument used for the type parameter shall be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable value type ([§8.3.11](types.md#8311-nullable-value-types)) does not satisfy the value type constraint. A type parameter having the value type constraint shall not also have the *constructor_constraint*, although it may be used as a type argument for another type parameter with a *constructor_constraint*. +The value type constraint specifies that a type argument used for the type parameter shall be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)) does not satisfy the value type constraint. A type parameter having the value type constraint shall not also have the *constructor_constraint*, although it may be used as a type argument for another type parameter with a *constructor_constraint*. > *Note*: The `System.Nullable` type specifies the non-nullable value type constraint for `T`. Thus, recursively constructed types of the forms `T??` and `Nullable>` are prohibited. *end note* -Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints. +Because `unmanaged` is not a keyword, in *primary_constraint* the unmanaged constraint is always syntactically ambiguous with *class_type*. For compatibility reasons, if a name lookup ([§12.8.4](expressions.md#1284-simple-names)) of the name `unmanaged` succeeds it is treated as a `class_type`. Otherwise it is treated as the unmanaged constraint. + +The unmanaged type constraint specifies that a type argument used for the type parameter shall be a non-nullable unmanaged type ([§8.8](types.md#88-unmanaged-types)). + +Pointer types are never allowed to be type arguments, and don’t satisfy any type constraints, even unmanaged, despite being unmanaged types. If a constraint is a class type, an interface type, or a type parameter, that type specifies a minimal “base type” that every type argument used for that type parameter shall support. Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. The type argument supplied shall satisfy the conditions described in [§8.4.5](types.md#845-satisfying-constraints). @@ -449,7 +456,7 @@ A *class_type* constraint shall satisfy the following rules: - The type shall be a class type. - The type shall not be `sealed`. -- The type shall not be one of the following types: `System.Array`, `System.Delegate`, `System.Enum`, or `System.ValueType`. +- The type shall not be one of the following types: `System.Array` or `System.ValueType`. - The type shall not be `object`. - At most one constraint for a given type parameter may be a class type. @@ -483,9 +490,9 @@ Any constraints shall be consistent among dependent type parameters. If type par It is valid for `S` to have the value type constraint and `T` to have the reference type constraint. Effectively this limits `T` to the types `System.Object`, `System.ValueType`, `System.Enum`, and any interface type. -If the `where` clause for a type parameter includes a constructor constraint (which has the form `new()`), it is possible to use the `new` operator to create instances of the type ([§11.7.15.2](expressions.md#117152-object-creation-expressions)). Any type argument used for a type parameter with a constructor constraint shall be a value type, a non-abstract class having a public parameterless constructor, or a type parameter having the value type constraint or constructor constraint. +If the `where` clause for a type parameter includes a constructor constraint (which has the form `new()`), it is possible to use the `new` operator to create instances of the type ([§12.8.16.2](expressions.md#128162-object-creation-expressions)). Any type argument used for a type parameter with a constructor constraint shall be a value type, a non-abstract class having a public parameterless constructor, or a type parameter having the value type constraint or constructor constraint. -It is a compile-time error for *type_parameter_constraints* having a *primary_constraint* of `struct` to also have a *constructor_constraint*. +It is a compile-time error for *type_parameter_constraints* having a *primary_constraint* of `struct` or `unmanaged` to also have a *constructor_constraint*. > *Example*: The following are examples of constraints: > @@ -566,7 +573,6 @@ The ***dynamic erasure*** of a type `C` is type `Cₓ` constructed as follows: - If `C` is a nested type `Outer.Inner` then `Cₓ` is a nested type `Outerₓ.Innerₓ`. - If `C` `Cₓ`is a constructed type `G` with type arguments `A¹, ..., Aⁿ` then `Cₓ` is the constructed type `G`. - If `C` is an array type `E[]` then `Cₓ` is the array type `Eₓ[]`. -- If `C` is a pointer type `E*` then `Cₓ` is the pointer type `Eₓ*`. - If `C` is dynamic then `Cₓ` is `object`. - Otherwise, `Cₓ` is `C`. @@ -651,7 +657,7 @@ When a partial generic type declaration includes constraints, the constraints sh > > *end example* -### 14.2.6 Class body +### 15.2.6 Class body The *class_body* of a class defines the members of that class. @@ -661,7 +667,7 @@ class_body ; ``` -### 14.2.7 Partial declarations +### 15.2.7 Partial declarations The modifier `partial` is used when defining a class, struct, or interface type in multiple parts. The `partial` modifier is a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)) and only has special meaning immediately before one of the keywords `class`, `struct`, or `interface`. @@ -673,7 +679,7 @@ Nested types can be declared in multiple parts by using the `partial` modifier. > *Example*: The following partial class is implemented in two parts, which reside in different compilation units. The first part is machine generated by a database-mapping tool while the second part is manually authored: > -> +> > ```csharp > public partial class Customer > { @@ -688,6 +694,7 @@ Nested types can be declared in multiple parts by using the `partial` modifier. > } > } > +> // File: Customer2.cs > public partial class Customer > { > public void SubmitOrder(Order orderSubmitted) => orders.Add(orderSubmitted); @@ -720,11 +727,11 @@ Nested types can be declared in multiple parts by using the `partial` modifier. > > *end example* -The handling of attributes specified on the type or type parameters of different parts of a partial declaration is discussed in [§21.3](attributes.md#213-attribute-specification). +The handling of attributes specified on the type or type parameters of different parts of a partial declaration is discussed in [§22.3](attributes.md#223-attribute-specification). -## 14.3 Class members +## 15.3 Class members -### 14.3.1 General +### 15.3.1 General The members of a class consist of the members introduced by its *class_member_declaration*s and the members inherited from the direct base class. @@ -746,17 +753,17 @@ class_member_declaration The members of a class are divided into the following categories: -- Constants, which represent constant values associated with the class ([§14.4](classes.md#144-constants)). -- Fields, which are the variables of the class ([§14.5](classes.md#145-fields)). -- Methods, which implement the computations and actions that can be performed by the class ([§14.6](classes.md#146-methods)). -- Properties, which define named characteristics and the actions associated with reading and writing those characteristics ([§14.7](classes.md#147-properties)). -- Events, which define notifications that can be generated by the class ([§14.8](classes.md#148-events)). -- Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays ([§14.9](classes.md#149-indexers)). -- Operators, which define the expression operators that can be applied to instances of the class ([§14.10](classes.md#1410-operators)). -- Instance constructors, which implement the actions required to initialize instances of the class ([§14.11](classes.md#1411-instance-constructors)) -- Finalizers, which implement the actions to be performed before instances of the class are permanently discarded ([§14.13](classes.md#1413-finalizers)). -- Static constructors, which implement the actions required to initialize the class itself ([§14.12](classes.md#1412-static-constructors)). -- Types, which represent the types that are local to the class ([§13.7](namespaces.md#137-type-declarations)). +- Constants, which represent constant values associated with the class ([§15.4](classes.md#154-constants)). +- Fields, which are the variables of the class ([§15.5](classes.md#155-fields)). +- Methods, which implement the computations and actions that can be performed by the class ([§15.6](classes.md#156-methods)). +- Properties, which define named characteristics and the actions associated with reading and writing those characteristics ([§15.7](classes.md#157-properties)). +- Events, which define notifications that can be generated by the class ([§15.8](classes.md#158-events)). +- Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays ([§15.9](classes.md#159-indexers)). +- Operators, which define the expression operators that can be applied to instances of the class ([§15.10](classes.md#1510-operators)). +- Instance constructors, which implement the actions required to initialize instances of the class ([§15.11](classes.md#1511-instance-constructors)) +- Finalizers, which implement the actions to be performed before instances of the class are permanently discarded ([§15.13](classes.md#1513-finalizers)). +- Static constructors, which implement the actions required to initialize the class itself ([§15.12](classes.md#1512-static-constructors)). +- Types, which represent the types that are local to the class ([§14.7](namespaces.md#147-type-declarations)). A *class_declaration* creates a new declaration space ([§7.3](basic-concepts.md#73-declarations)), and the *type_parameter*s and the *class_member_declaration*s immediately contained by the *class_declaration* introduce new members into this declaration space. The following rules apply to *class_member_declaration*s: @@ -764,13 +771,12 @@ A *class_declaration* creates a new declaration space ([§7.3](basic-concepts.md - The name of a type parameter in the *type_parameter_list* of a class declaration shall differ from the names of all other type parameters in the same *type_parameter_list* and shall differ from the name of the class and the names of all members of the class. -- The name of a type shall differ from the names of all non-type members declared in the same class. If two or more type declarations share the same fully qualified name, the declarations shall have the `partial` modifier ([§14.2.7](classes.md#1427-partial-declarations)) and these declarations combine to define a single type. +- The name of a type shall differ from the names of all non-type members declared in the same class. If two or more type declarations share the same fully qualified name, the declarations shall have the `partial` modifier ([§15.2.7](classes.md#1527-partial-declarations)) and these declarations combine to define a single type. -> *Note*: Since the fully qualified name of a type declaration encodes the number of type parameters, two distinct types may share the same name as long as they have different number of type parameters. *end note* +> *Note*: Since the fully qualified name of a type declaration encodes the number of type parameters, two distinct types may share the same name as long as they have different number of type parameters. *end note* - The name of a constant, field, property, or event shall differ from the names of all other members declared in the same class. - -- The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class shall not have signatures that differ solely by `ref` and `out`. +- The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class shall not have signatures that differ solely by `in`, `out`, and `ref`. - The signature of an instance constructor shall differ from the signatures of all other instance constructors declared in the same class, and two constructors declared in the same class shall not have signatures that differ solely by `ref` and `out`. @@ -778,11 +784,11 @@ A *class_declaration* creates a new declaration space ([§7.3](basic-concepts.md - The signature of an operator shall differ from the signatures of all other operators declared in the same class. -The inherited members of a class ([§14.3.4](classes.md#1434-inheritance)) are not part of the declaration space of a class. +The inherited members of a class ([§15.3.4](classes.md#1534-inheritance)) are not part of the declaration space of a class. > *Note*: Thus, a derived class is allowed to declare a member with the same name or signature as an inherited member (which in effect hides the inherited member). *end note* -The set of members of a type declared in multiple parts ([§14.2.7](classes.md#1427-partial-declarations)) is the union of the members declared in each part. The bodies of all parts of the type declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. The accessibility domain of any member always includes all the parts of the enclosing type; a private member declared in one part is freely accessible from another part. It is a compile-time error to declare the same member in more than one part of the type, unless that member is a type having the `partial` modifier. +The set of members of a type declared in multiple parts ([§15.2.7](classes.md#1527-partial-declarations)) is the union of the members declared in each part. The bodies of all parts of the type declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. The accessibility domain of any member always includes all the parts of the enclosing type; a private member declared in one part is freely accessible from another part. It is a compile-time error to declare the same member in more than one part of the type, unless that member is a type having the `partial` modifier. > *Example*: > @@ -811,9 +817,9 @@ The set of members of a type declared in multiple parts ([§14.2.7](classes.md#1 > > *end example* -Field initialization order can be significant within C# code, and some guarantees are provided, as defined in [§14.5.6.1](classes.md#14561-general). Otherwise, the ordering of members within a type is rarely significant, but may be significant when interfacing with other languages and environments. In these cases, the ordering of members within a type declared in multiple parts is undefined. +Field initialization order can be significant within C# code, and some guarantees are provided, as defined in [§15.5.6.1](classes.md#15561-general). Otherwise, the ordering of members within a type is rarely significant, but may be significant when interfacing with other languages and environments. In these cases, the ordering of members within a type declared in multiple parts is undefined. -### 14.3.2 The instance type +### 15.3.2 The instance type Each class declaration has an associated ***instance type***. For a generic class declaration, the instance type is formed by creating a constructed type ([§8.4](types.md#84-constructed-types)) from the type declaration, with each of the supplied type arguments being the corresponding type parameter. Since the instance type uses the type parameters, it can only be used where the type parameters are in scope; that is, inside the class declaration. The instance type is the type of `this` for code written inside the class declaration. For non-generic classes, the instance type is simply the declared class. @@ -831,7 +837,7 @@ Each class declaration has an associated ***instance type***. For a generic clas > > *end example* -### 14.3.3 Members of constructed types +### 15.3.3 Members of constructed types The non-inherited members of a constructed type are obtained by substituting, for each *type_parameter* in the member declaration, the corresponding *type_argument* of the constructed type. The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution. @@ -861,7 +867,7 @@ The non-inherited members of a constructed type are obtained by substituting, fo > > *end example* -Within instance function members, the type of `this` is the instance type ([§14.3.2](classes.md#1432-the-instance-type)) of the containing declaration. +Within instance function members, the type of `this` is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the containing declaration. All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. When a particular closed constructed type ([§8.4.3](types.md#843-open-and-closed-types)) is used at run-time, each use of a type parameter is replaced with the type argument supplied to the constructed type. @@ -896,7 +902,7 @@ All members of a generic class can use type parameters from any enclosing class, > > *end example* -### 14.3.4 Inheritance +### 15.3.4 Inheritance A class ***inherits*** the members of its direct base class. Inheritance means that a class implicitly contains all members of its direct base class, except for the instance constructors, finalizers, and static constructors of the base class. Some important aspects of inheritance are: @@ -912,7 +918,7 @@ A class ***inherits*** the members of its direct base class. Inheritance means t - A class can declare virtual methods, properties, indexers, and events, and derived classes can override the implementation of these function members. This enables classes to exhibit polymorphic behavior wherein the actions performed by a function member invocation vary depending on the run-time type of the instance through which that function member is invoked. -The inherited members of a constructed class type are the members of the immediate base class type ([§14.2.4.2](classes.md#14242-base-classes)), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the *base_class_specification*. These members, in turn, are transformed by substituting, for each *type_parameter* in the member declaration, the corresponding *type_argument* of the *base_class_specification*. +The inherited members of a constructed class type are the members of the immediate base class type ([§15.2.4.2](classes.md#15242-base-classes)), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the *base_class_specification*. These members, in turn, are transformed by substituting, for each *type_parameter* in the member declaration, the corresponding *type_argument* of the *base_class_specification*. > *Example*: > @@ -933,23 +939,23 @@ The inherited members of a constructed class type are the members of the immedia > > *end example* -### 14.3.5 The new modifier +### 15.3.5 The new modifier A *class_member_declaration* is permitted to declare a member with the same name or signature as an inherited member. When this occurs, the derived class member is said to *hide* the base class member. See [§7.7.2.3](basic-concepts.md#7723-hiding-through-inheritance) for a precise specification of when a member hides an inherited member. -An inherited member `M` is considered to be ***available*** if `M` is accessible and there is no other inherited accessible member N that already hides `M`. Implicitly hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning unless the declaration of the derived class member includes a `new` modifier to explicitly indicate that the derived member is intended to hide the base member. If one or more parts of a partial declaration ([§14.2.7](classes.md#1427-partial-declarations)) of a nested type include the `new` modifier, no warning is issued if the nested type hides an available inherited member. +An inherited member `M` is considered to be ***available*** if `M` is accessible and there is no other inherited accessible member N that already hides `M`. Implicitly hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning unless the declaration of the derived class member includes a `new` modifier to explicitly indicate that the derived member is intended to hide the base member. If one or more parts of a partial declaration ([§15.2.7](classes.md#1527-partial-declarations)) of a nested type include the `new` modifier, no warning is issued if the nested type hides an available inherited member. If a `new` modifier is included in a declaration that doesn’t hide an available inherited member, a warning to that effect is issued. -### 14.3.6 Access modifiers +### 15.3.6 Access modifiers A *class_member_declaration* can have any one of the permitted kinds of declared accessibility ([§7.5.2](basic-concepts.md#752-declared-accessibility)): `public`, `protected internal`, `protected`, `private protected`, `internal`, or `private`. Except for the `protected internal` and `private protected` combinations, it is a compile-time error to specify more than one access modifier. When a *class_member_declaration* does not include any access modifiers, `private` is assumed. -### 14.3.7 Constituent types +### 15.3.7 Constituent types Types that are used in the declaration of a member are called the ***constituent types*** of that member. Possible constituent types are the type of a constant, field, property, event, or indexer, the return type of a method or operator, and the parameter types of a method, indexer, operator, or instance constructor. The constituent types of a member shall be at least as accessible as that member itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -### 14.3.8 Static and instance members +### 15.3.8 Static and instance members Members of a class are either ***static members*** or ***instance members***. @@ -957,15 +963,15 @@ Members of a class are either ***static members*** or ***instance members***. When a field, method, property, event, operator, or constructor declaration includes a `static` modifier, it declares a static member. In addition, a constant or type declaration implicitly declares a static member. Static members have the following characteristics: -- When a static member `M` is referenced in a *member_access* ([§11.7.6](expressions.md#1176-member-access)) of the form `E.M`, `E` shall denote a type that has a member `M`. It is a compile-time error for `E` to denote an instance. +- When a static member `M` is referenced in a *member_access* ([§12.8.7](expressions.md#1287-member-access)) of the form `E.M`, `E` shall denote a type that has a member `M`. It is a compile-time error for `E` to denote an instance. - A static field in a non-generic class identifies exactly one storage location. No matter how many instances of a non-generic class are created, there is only ever one copy of a static field. Each distinct closed constructed type ([§8.4.3](types.md#843-open-and-closed-types)) has its own set of static fields, regardless of the number of instances of the closed constructed type. - A static function member (method, property, event, operator, or constructor) does not operate on a specific instance, and it is a compile-time error to refer to this in such a function member. When a field, method, property, event, indexer, constructor, or finalizer declaration does not include a static modifier, it declares an instance member. (An instance member is sometimes called a non-static member.) Instance members have the following characteristics: -- When an instance member `M` is referenced in a *member_access* ([§11.7.6](expressions.md#1176-member-access)) of the form `E.M`, `E` shall denote an instance of a type that has a member `M`. It is a binding-time error for E to denote a type. +- When an instance member `M` is referenced in a *member_access* ([§12.8.7](expressions.md#1287-member-access)) of the form `E.M`, `E` shall denote an instance of a type that has a member `M`. It is a binding-time error for E to denote a type. - Every instance of a class contains a separate set of all instance fields of the class. -- An instance function member (method, property, indexer, instance constructor, or finalizer) operates on a given instance of the class, and this instance can be accessed as `this` ([§11.7.12](expressions.md#11712-this-access)). +- An instance function member (method, property, indexer, instance constructor, or finalizer) operates on a given instance of the class, and this instance can be accessed as `this` ([§12.8.13](expressions.md#12813-this-access)). > *Example*: The following example illustrates the rules for accessing static and instance members: > @@ -998,13 +1004,13 @@ When a field, method, property, event, indexer, constructor, or finalizer declar > } > ``` > -> The `F` method shows that in an instance function member, a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) can be used to access both instance members and static members. The `G` method shows that in a static function member, it is a compile-time error to access an instance member through a *simple_name*. The `Main` method shows that in a *member_access* ([§11.7.6](expressions.md#1176-member-access)), instance members shall be accessed through instances, and static members shall be accessed through types. +> The `F` method shows that in an instance function member, a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) can be used to access both instance members and static members. The `G` method shows that in a static function member, it is a compile-time error to access an instance member through a *simple_name*. The `Main` method shows that in a *member_access* ([§12.8.7](expressions.md#1287-member-access)), instance members shall be accessed through instances, and static members shall be accessed through types. > > *end example* -### 14.3.9 Nested types +### 15.3.9 Nested types -#### 14.3.9.1 General +#### 15.3.9.1 General A type declared within a class or struct is called a ***nested type***. A type that is declared within a compilation unit or namespace is called a ***non-nested type***. @@ -1028,11 +1034,11 @@ A type declared within a class or struct is called a ***nested type***. A type t > > *end example* -#### 14.3.9.2 Fully qualified name +#### 15.3.9.2 Fully qualified name -The fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names)) for a nested type declarationis `S.N` where `S` is the fully qualified name of the type declarationin which type `N` is declared and `N` is the unqualified name ([§7.8.2](basic-concepts.md#782-unqualified-names)) of the nested type declaration (including any *generic_dimension_specifier* ([§11.7.16](expressions.md#11716-the-typeof-operator))). +The fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names)) for a nested type declarationis `S.N` where `S` is the fully qualified name of the type declarationin which type `N` is declared and `N` is the unqualified name ([§7.8.2](basic-concepts.md#782-unqualified-names)) of the nested type declaration (including any *generic_dimension_specifier* ([§12.8.17](expressions.md#12817-the-typeof-operator))). -#### 14.3.9.3 Declared accessibility +#### 15.3.9.3 Declared accessibility Non-nested types can have `public` or `internal` declared accessibility and have `internal` declared accessibility by default. Nested types can have these forms of declared accessibility too, plus one or more additional forms of declared accessibility, depending on whether the containing type is a class or struct: @@ -1074,9 +1080,9 @@ Non-nested types can have `public` or `internal` declared accessibility and have > > *end example* -#### 14.3.9.4 Hiding +#### 15.3.9.4 Hiding -A nested type may hide ([§7.7.2.2](basic-concepts.md#7722-hiding-through-nesting)) a base member. The `new` modifier ([§14.3.5](classes.md#1435-the-new-modifier)) is permitted on nested type declarations so that hiding can be expressed explicitly. +A nested type may hide ([§7.7.2.2](basic-concepts.md#7722-hiding-through-nesting)) a base member. The `new` modifier ([§15.3.5](classes.md#1535-the-new-modifier)) is permitted on nested type declarations so that hiding can be expressed explicitly. > *Example*: The example > @@ -1114,9 +1120,9 @@ A nested type may hide ([§7.7.2.2](basic-concepts.md#7722-hiding-through-nestin > > *end example* -#### 14.3.9.5 this access +#### 15.3.9.5 this access -A nested type and its containing type do not have a special relationship with regard to *this_access* ([§11.7.12](expressions.md#11712-this-access)). Specifically, `this` within a nested type cannot be used to refer to instance members of the containing type. In cases where a nested type needs access to the instance members of its containing type, access can be provided by providing the `this` for the instance of the containing type as a constructor argument for the nested type. +A nested type and its containing type do not have a special relationship with regard to *this_access* ([§12.8.13](expressions.md#12813-this-access)). Specifically, `this` within a nested type cannot be used to refer to instance members of the containing type. In cases where a nested type needs access to the instance members of its containing type, access can be provided by providing the `this` for the instance of the containing type as a constructor argument for the nested type. > *Example*: The following example > @@ -1161,7 +1167,7 @@ A nested type and its containing type do not have a special relationship with re > > *end example* -#### 14.3.9.6 Access to private and protected members of the containing type +#### 15.3.9.6 Access to private and protected members of the containing type A nested type has access to all of the members that are accessible to its containing type, including members of the containing type that have `private` and `protected` declared accessibility. @@ -1226,7 +1232,7 @@ A nested type also may access protected members defined in a base type of its co > > *end example* -#### 14.3.9.7 Nested types in generic classes +#### 15.3.9.7 Nested types in generic classes A generic class declaration may contain nested type declarations. The type parameters of the enclosing class may be used within the nested types. A nested type declaration may contain additional type parameters that apply only to the nested type. @@ -1272,13 +1278,13 @@ Although it is bad programming style, a type parameter in a nested type can hide > > *end example* -### 14.3.10 Reserved member names +### 15.3.10 Reserved member names -#### 14.3.10.1 General +#### 15.3.10.1 General -To facilitate the underlying C# run-time implementation, for each source member declaration that is a property, event, or indexer, the implementation shall reserve two method signatures based on the kind of the member declaration, its name, and its type ([§14.3.10.2](classes.md#143102-member-names-reserved-for-properties), [§14.3.10.3](classes.md#143103-member-names-reserved-for-events), [§14.3.10.4](classes.md#143104-member-names-reserved-for-indexers)). It is a compile-time error for a program to declare a member whose signature matches a signature reserved by a member declared in the same scope, even if the underlying run-time implementation does not make use of these reservations. +To facilitate the underlying C# run-time implementation, for each source member declaration that is a property, event, or indexer, the implementation shall reserve two method signatures based on the kind of the member declaration, its name, and its type ([§15.3.10.2](classes.md#153102-member-names-reserved-for-properties), [§15.3.10.3](classes.md#153103-member-names-reserved-for-events), [§15.3.10.4](classes.md#153104-member-names-reserved-for-indexers)). It is a compile-time error for a program to declare a member whose signature matches a signature reserved by a member declared in the same scope, even if the underlying run-time implementation does not make use of these reservations. -The reserved names do not introduce declarations, thus they do not participate in member lookup. However, a declaration’s associated reserved method signatures do participate in inheritance ([§14.3.4](classes.md#1434-inheritance)), and can be hidden with the `new` modifier ([§14.3.5](classes.md#1435-the-new-modifier)). +The reserved names do not introduce declarations, thus they do not participate in member lookup. However, a declaration’s associated reserved method signatures do participate in inheritance ([§15.3.4](classes.md#1534-inheritance)), and can be hidden with the `new` modifier ([§15.3.5](classes.md#1535-the-new-modifier)). > *Note*: The reservation of these names serves three purposes: > @@ -1288,11 +1294,11 @@ The reserved names do not introduce declarations, thus they do not participate i > > *end note* -The declaration of a finalizer ([§14.13](classes.md#1413-finalizers)) also causes a signature to be reserved ([§14.3.10.5](classes.md#143105-member-names-reserved-for-finalizers)). +The declaration of a finalizer ([§15.13](classes.md#1513-finalizers)) also causes a signature to be reserved ([§15.3.10.5](classes.md#153105-member-names-reserved-for-finalizers)). -#### 14.3.10.2 Member names reserved for properties +#### 15.3.10.2 Member names reserved for properties -For a property `P` ([§14.7](classes.md#147-properties)) of type `T`, the following signatures are reserved: +For a property `P` ([§15.7](classes.md#157-properties)) of type `T`, the following signatures are reserved: ```csharp T get_P(); @@ -1346,18 +1352,18 @@ Both signatures are reserved, even if the property is read-only or write-only. > > *end example* -#### 14.3.10.3 Member names reserved for events +#### 15.3.10.3 Member names reserved for events -For an event `E` ([§14.8](classes.md#148-events)) of delegate type `T`, the following signatures are reserved: +For an event `E` ([§15.8](classes.md#158-events)) of delegate type `T`, the following signatures are reserved: ```csharp void add_E(T handler); void remove_E(T handler); ``` -#### 14.3.10.4 Member names reserved for indexers +#### 15.3.10.4 Member names reserved for indexers -For an indexer ([§14.9](classes.md#149-indexers)) of type `T` with parameter-list `L`, the following signatures are reserved: +For an indexer ([§15.9](classes.md#159-indexers)) of type `T` with parameter-list `L`, the following signatures are reserved: ```csharp T get_Item(L); @@ -1368,15 +1374,15 @@ Both signatures are reserved, even if the indexer is read-only or write-only. Furthermore the member name `Item` is reserved. -#### 14.3.10.5 Member names reserved for finalizers +#### 15.3.10.5 Member names reserved for finalizers -For a class containing a finalizer ([§14.13](classes.md#1413-finalizers)), the following signature is reserved: +For a class containing a finalizer ([§15.13](classes.md#1513-finalizers)), the following signature is reserved: ```csharp void Finalize(); ``` -## 14.4 Constants +## 15.4 Constants A ***constant*** is a class member that represents a constant value: a value that can be computed at compile-time. A *constant_declaration* introduces one or more constants of a given type. @@ -1394,15 +1400,15 @@ constant_modifier ; ``` -A *constant_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)), a `new` modifier ([§14.3.5](classes.md#1435-the-new-modifier)), and any one of the permitted kinds of declared accessibility ([§14.3.6](classes.md#1436-access-modifiers)). The attributes and modifiers apply to all of the members declared by the *constant_declaration*. Even though constants are considered static members, a *constant_declaration* neither requires nor allows a `static` modifier. It is an error for the same modifier to appear multiple times in a constant declaration. +A *constant_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)), a `new` modifier ([§15.3.5](classes.md#1535-the-new-modifier)), and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)). The attributes and modifiers apply to all of the members declared by the *constant_declaration*. Even though constants are considered static members, a *constant_declaration* neither requires nor allows a `static` modifier. It is an error for the same modifier to appear multiple times in a constant declaration. -The *type* of a *constant_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *constant_declarator*s ([§12.6.3](statements.md#1263-local-constant-declarations)), each of which introduces a new member. A *constant_declarator* consists of an *identifier* that names the member, followed by an “`=`” token, followed by a *constant_expression* ([§11.21](expressions.md#1121-constant-expressions)) that gives the value of the member. +The *type* of a *constant_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *constant_declarator*s ([§13.6.3](statements.md#1363-local-constant-declarations)), each of which introduces a new member. A *constant_declarator* consists of an *identifier* that names the member, followed by an “`=`” token, followed by a *constant_expression* ([§12.23](expressions.md#1223-constant-expressions)) that gives the value of the member. The *type* specified in a constant declaration shall be `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`, `string`, an *enum_type*, or a *reference_type*. Each *constant_expression* shall yield a value of the target type or of a type that can be converted to the target type by an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). The *type* of a constant shall be at least as accessible as the constant itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -The value of a constant is obtained in an expression using a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) or a *member_access* ([§11.7.6](expressions.md#1176-member-access)). +The value of a constant is obtained in an expression using a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) or a *member_access* ([§12.8.7](expressions.md#1287-member-access)). A constant can itself participate in a *constant_expression*. Thus, a constant may be used in any construct that requires a *constant_expression*. @@ -1410,11 +1416,11 @@ A constant can itself participate in a *constant_expression*. Thus, a constant m -> *Note*: As described in [§11.21](expressions.md#1121-constant-expressions), a *constant_expression* is an expression that can be fully evaluated at compile-time. Since the only way to create a non-null value of a *reference_type* other than `string` is to apply the `new` operator, and since the `new` operator is not permitted in a *constant_expression*, the only possible value for constants of *reference_type*s other than `string` is `null`. *end note* +> *Note*: As described in [§12.23](expressions.md#1223-constant-expressions), a *constant_expression* is an expression that can be fully evaluated at compile-time. Since the only way to create a non-null value of a *reference_type* other than `string` is to apply the `new` operator, and since the `new` operator is not permitted in a *constant_expression*, the only possible value for constants of *reference_type*s other than `string` is `null`. *end note* -When a symbolic name for a constant value is desired, but when the type of that value is not permitted in a constant declaration, or when the value cannot be computed at compile-time by a *constant_expression*, a readonly field ([§14.5.3](classes.md#1453-readonly-fields)) may be used instead. +When a symbolic name for a constant value is desired, but when the type of that value is not permitted in a constant declaration, or when the value cannot be computed at compile-time by a *constant_expression*, a readonly field ([§15.5.3](classes.md#1553-readonly-fields)) may be used instead. -> *Note*: The versioning semantics of `const` and `readonly` differ ([§14.5.3.3](classes.md#14533-versioning-of-constants-and-static-readonly-fields)). *end note* +> *Note*: The versioning semantics of `const` and `readonly` differ ([§15.5.3.3](classes.md#15533-versioning-of-constants-and-static-readonly-fields)). *end note* A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. @@ -1468,9 +1474,9 @@ Constant declarations may depend on constants from other programs, but such depe > *Example*: Referring to the example above, if `A` and `B` were declared in separate programs, it would be possible for `A.X` to depend on `B.Z`, but `B.Z` could then not simultaneously depend on `A.Y`. *end example* -## 14.5 Fields +## 15.5 Fields -### 14.5.1 General +### 15.5.1 General A ***field*** is a member that represents a variable associated with an object or class. A *field_declaration* introduces one or more fields of a given type. @@ -1500,15 +1506,15 @@ variable_declarator ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -A *field_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)), a `new` modifier ([§14.3.5](classes.md#1435-the-new-modifier)), a valid combination of the four access modifiers ([§14.3.6](classes.md#1436-access-modifiers)), and a `static` modifier ([§14.5.2](classes.md#1452-static-and-instance-fields)). In addition, a *field_declaration* may include a `readonly` modifier ([§14.5.3](classes.md#1453-readonly-fields)) or a `volatile` modifier ([§14.5.4](classes.md#1454-volatile-fields)), but not both. The attributes and modifiers apply to all of the members declared by the *field_declaration*. It is an error for the same modifier to appear multiple times in a *field_declaration*. +A *field_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)), a `new` modifier ([§15.3.5](classes.md#1535-the-new-modifier)), a valid combination of the four access modifiers ([§15.3.6](classes.md#1536-access-modifiers)), and a `static` modifier ([§15.5.2](classes.md#1552-static-and-instance-fields)). In addition, a *field_declaration* may include a `readonly` modifier ([§15.5.3](classes.md#1553-readonly-fields)) or a `volatile` modifier ([§15.5.4](classes.md#1554-volatile-fields)), but not both. The attributes and modifiers apply to all of the members declared by the *field_declaration*. It is an error for the same modifier to appear multiple times in a *field_declaration*. -The *type* of a *field_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *variable_declarator*s, each of which introduces a new member. A *variable_declarator* consists of an *identifier* that names that member, optionally followed by an “`=`” token and a *variable_initializer* ([§14.5.6](classes.md#1456-variable-initializers)) that gives the initial value of that member. +The *type* of a *field_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *variable_declarator*s, each of which introduces a new member. A *variable_declarator* consists of an *identifier* that names that member, optionally followed by an “`=`” token and a *variable_initializer* ([§15.5.6](classes.md#1556-variable-initializers)) that gives the initial value of that member. The *type* of a field shall be at least as accessible as the field itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -The value of a field is obtained in an expression using a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)), a *member_access* ([§11.7.6](expressions.md#1176-member-access)) or a base_access ([§11.7.13](expressions.md#11713-base-access)). The value of a non-readonly field is modified using an *assignment* ([§11.19](expressions.md#1119-assignment-operators)). The value of a non-readonly field can be both obtained and modified using postfix increment and decrement operators ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators)) and prefix increment and decrement operators ([§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)). +The value of a field is obtained in an expression using a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)), a *member_access* ([§12.8.7](expressions.md#1287-member-access)) or a base_access ([§12.8.14](expressions.md#12814-base-access)). The value of a non-readonly field is modified using an *assignment* ([§12.21](expressions.md#1221-assignment-operators)). The value of a non-readonly field can be both obtained and modified using postfix increment and decrement operators ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators)) and prefix increment and decrement operators ([§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)). A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. @@ -1536,15 +1542,15 @@ A field declaration that declares multiple fields is equivalent to multiple decl > > *end example* -### 14.5.2 Static and instance fields +### 15.5.2 Static and instance fields When a field declaration includes a `static` modifier, the fields introduced by the declaration are ***static fields***. When no `static` modifier is present, the fields introduced by the declaration are ***instance fields***. Static fields and instance fields are two of the several kinds of variables ([§9](variables.md#9-variables)) supported by C#, and at times they are referred to as ***static variables*** and ***instance variables***, respectively. -As explained in [§14.3.8](classes.md#1438-static-and-instance-members), each instance of a class contains a complete set of the instance fields of the class, while there is only one set of static fields for each non-generic class or closed constructed type, regardless of the number of instances of the class or closed constructed type. +As explained in [§15.3.8](classes.md#1538-static-and-instance-members), each instance of a class contains a complete set of the instance fields of the class, while there is only one set of static fields for each non-generic class or closed constructed type, regardless of the number of instances of the class or closed constructed type. -### 14.5.3 Readonly fields +### 15.5.3 Readonly fields -#### 14.5.3.1 General +#### 15.5.3.1 General When a *field_declaration* includes a `readonly` modifier, the fields introduced by the declaration are ***readonly fields***. Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class. (A readonly field can be assigned to multiple times in these contexts.) Specifically, direct assignments to a readonly field are permitted only in the following contexts: @@ -1553,7 +1559,7 @@ When a *field_declaration* includes a `readonly` modifier, the fields introduced Attempting to assign to a readonly field or pass it as an `out` or `ref` parameter in any other context is a compile-time error. -#### 14.5.3.2 Using static readonly fields for constants +#### 15.5.3.2 Using static readonly fields for constants A static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration, or when the value cannot be computed at compile-time. @@ -1584,13 +1590,13 @@ A static readonly field is useful when a symbolic name for a constant value is d > > *end example* -#### 14.5.3.3 Versioning of constants and static readonly fields +#### 15.5.3.3 Versioning of constants and static readonly fields Constants and readonly fields have different binary versioning semantics. When an expression references a constant, the value of the constant is obtained at compile-time, but when an expression references a readonly field, the value of the field is not obtained until run-time. > *Example*: Consider an application that consists of two separate programs: > -> +> > ```csharp > namespace Program1 > { @@ -1603,7 +1609,7 @@ Constants and readonly fields have different binary versioning semantics. When a > > and > -> +> > ```csharp > namespace Program2 > { @@ -1621,9 +1627,9 @@ Constants and readonly fields have different binary versioning semantics. When a > > *end example* -### 14.5.4 Volatile fields +### 15.5.4 Volatile fields -When a *field_declaration* includes a `volatile` modifier, the fields introduced by that declaration are ***volatile fields***. For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the *lock_statement* ([§12.13](statements.md#1213-the-lock-statement)). These optimizations can be performed by the compiler, by the run-time system, or by hardware. For volatile fields, such reordering optimizations are restricted: +When a *field_declaration* includes a `volatile` modifier, the fields introduced by that declaration are ***volatile fields***. For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the *lock_statement* ([§13.13](statements.md#1313-the-lock-statement)). These optimizations can be performed by the compiler, by the run-time system, or by hardware. For volatile fields, such reordering optimizations are restricted: - A read of a volatile field is called a ***volatile read***. A volatile read has “acquire semantics”; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence. - A write of a volatile field is called a ***volatile write***. A volatile write has “release semantics”; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence. @@ -1631,7 +1637,7 @@ When a *field_declaration* includes a `volatile` modifier, the fields introduced These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. The type of a volatile field shall be one of the following: - A *reference_type*. -- A *type_parameter* that is known to be a reference type ([§14.2.5](classes.md#1425-type-parameter-constraints)). +- A *type_parameter* that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)). - The type `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `char`, `float`, `bool`, `System.IntPtr`, or `System.UIntPtr`. - An *enum_type* having an *enum_base* type of `byte`, `sbyte`, `short`, `ushort`, `int`, or `uint`. @@ -1682,7 +1688,7 @@ These restrictions ensure that all threads will observe volatile writes performe > > *end example* -### 14.5.5 Field initialization +### 15.5.5 Field initialization The initial value of a field, whether it be a static field or an instance field, is the default value ([§9.3](variables.md#93-default-values)) of the field’s type. It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never “uninitialized”. @@ -1713,9 +1719,9 @@ The initial value of a field, whether it be a static field or an instance field, > > *end example* -### 14.5.6 Variable initializers +### 15.5.6 Variable initializers -#### 14.5.6.1 General +#### 15.5.6.1 General Field declarations may include *variable_initializer*s. For static fields, variable initializers correspond to assignment statements that are executed during class initialization. For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created. @@ -1747,7 +1753,7 @@ Field declarations may include *variable_initializer*s. For static fields, varia > > *end example* -The default value initialization described in [§14.5.5](classes.md#1455-field-initialization) occurs for all fields, including fields that have variable initializers. Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order. When there are field declarations in multiple partial type declarations for the same type, the order of the parts is unspecified. However, within each part the field initializers are executed in order. +The default value initialization described in [§15.5.5](classes.md#1555-field-initialization) occurs for all fields, including fields that have variable initializers. Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order. When there are field declarations in multiple partial type declarations for the same type, the order of the parts is unspecified. However, within each part the field initializers are executed in order. It is possible for static fields with variable initializers to be observed in their default value state. @@ -1777,9 +1783,9 @@ It is possible for static fields with variable initializers to be observed in th > > *end example* -#### 14.5.6.2 Static field initialization +#### 15.5.6.2 Static field initialization -The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration ([§14.5.6.1](classes.md#14561-general)). Within a partial class, the meaning of “textual order” is specified by [§14.5.6.1](classes.md#14561-general). If a static constructor ([§14.12](classes.md#1412-static-constructors)) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. +The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration ([§15.5.6.1](classes.md#15561-general)). Within a partial class, the meaning of “textual order” is specified by [§15.5.6.1](classes.md#15561-general). If a static constructor ([§15.12](classes.md#1512-static-constructors)) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. > *Example*: The example > @@ -1866,13 +1872,13 @@ The static field variable initializers of a class correspond to a sequence of as > 1 1 > ``` > -> because the rules for when static constructors execute (as defined in [§14.12](classes.md#1412-static-constructors)) provide that `B`’s static constructor (and hence `B`’s static field initializers) shall run before `A`’s static constructor and field initializers. +> because the rules for when static constructors execute (as defined in [§15.12](classes.md#1512-static-constructors)) provide that `B`’s static constructor (and hence `B`’s static field initializers) shall run before `A`’s static constructor and field initializers. > > *end example* -#### 14.5.6.3 Instance field initialization +#### 15.5.6.3 Instance field initialization -The instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors ([§14.11.3](classes.md#14113-instance-variable-initializers)) of that class. Within a partial class, the meaning of “textual order” is specified by [§14.5.6.1](classes.md#14561-general). The variable initializers are executed in the textual order in which they appear in the class declaration ([§14.5.6.1](classes.md#14561-general)). The class instance creation and initialization process is described further in [§14.11](classes.md#1411-instance-constructors). +The instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors ([§15.11.3](classes.md#15113-instance-variable-initializers)) of that class. Within a partial class, the meaning of “textual order” is specified by [§15.5.6.1](classes.md#15561-general). The variable initializers are executed in the textual order in which they appear in the class declaration ([§15.5.6.1](classes.md#15561-general)). The class instance creation and initialization process is described further in [§15.11](classes.md#1511-instance-constructors). A variable initializer for an instance field cannot reference the instance being created. Thus, it is a compile-time error to reference `this` in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a *simple_name*. @@ -1891,24 +1897,44 @@ A variable initializer for an instance field cannot reference the instance being > > *end example* -## 14.6 Methods +## 15.6 Methods -### 14.6.1 General +### 15.6.1 General A ***method*** is a member that implements a computation or action that can be performed by an object or class. Methods are declared using *method_declaration*s: ```ANTLR method_declaration - : method_header method_body + : attributes? method_modifiers return_type method_header method_body + | attributes? ref_method_modifiers ref_kind ref_return_type method_header + ref_method_body + ; + +method_modifiers + : method_modifier* 'partial'? + ; + +ref_kind + : 'ref' + | 'ref' 'readonly' + ; + +ref_method_modifiers + : ref_method_modifier* ; method_header - : attributes? method_modifier* 'partial'? return_type member_name - type_parameter_list? '(' formal_parameter_list? ')' + : member_name '(' formal_parameter_list? ')' + | member_name type_parameter_list '(' formal_parameter_list? ')' type_parameter_constraints_clause* ; method_modifier + : ref_method_modifier + | 'async' + ; + +ref_method_modifier : 'new' | 'public' | 'protected' @@ -1920,15 +1946,18 @@ method_modifier | 'override' | 'abstract' | 'extern' - | 'async' | unsafe_modifier // unsafe code support ; return_type - : type + : ref_return_type | 'void' ; +ref_return_type + : type + ; + member_name : identifier | interface_type '.' identifier @@ -1940,53 +1969,79 @@ method_body | '=>' expression ';' | ';' ; + +ref_method_body + : block + | '=>' 'ref' variable_reference ';' + | ';' + ; ``` Grammar notes: -- *unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +- *unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). - when recognising a *method_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen. -> *Note*: The overlapping of, and priority between, alternatives here is solely for descriptive convenience; the grammar rules could be elaborated to remove the overlap. ANTLR, and other grammar systems, adopt the same convenience and so *method_body* has the specified semantics automatically. +> *Note*: The overlapping of, and priority between, alternatives here is solely for descriptive convenience; the grammar rules could be elaborated to remove the overlap. ANTLR, and other grammar systems, adopt the same convenience and so *method_body* has the specified semantics automatically. *end note* -A *method_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)) and one of the permitted kinds of declared accessibility ([§14.3.6](classes.md#1436-access-modifiers)), the `new` ([§14.3.5](classes.md#1435-the-new-modifier)), `static` ([§14.6.3](classes.md#1463-static-and-instance-methods)), `virtual` ([§14.6.4](classes.md#1464-virtual-methods)), `override` ([§14.6.5](classes.md#1465-override-methods)), `sealed` ([§14.6.6](classes.md#1466-sealed-methods)), `abstract` ([§14.6.7](classes.md#1467-abstract-methods)), `extern` ([§14.6.8](classes.md#1468-external-methods)) and `async` ([§14.15](classes.md#1415-async-functions)) modifiers. +A *method_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.6.3](classes.md#1563-static-and-instance-methods)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods)), `override` ([§15.6.5](classes.md#1565-override-methods)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods)), `extern` ([§15.6.8](classes.md#1568-external-methods)) and `async` ([§15.15](classes.md#1515-async-functions)) modifiers. A declaration has a valid combination of modifiers if all of the following are true: -- The declaration includes a valid combination of access modifiers ([§14.3.6](classes.md#1436-access-modifiers)). +- The declaration includes a valid combination of access modifiers ([§15.3.6](classes.md#1536-access-modifiers)). - The declaration does not include the same modifier multiple times. - The declaration includes at most one of the following modifiers: `static`, `virtual`, and `override`. - The declaration includes at most one of the following modifiers: `new` and `override`. - If the declaration includes the `abstract` modifier, then the declaration does not include any of the following modifiers: `static`, `virtual`, `sealed`, or `extern`. - If the declaration includes the `private` modifier, then the declaration does not include any of the following modifiers: `virtual`, `override`, or `abstract`. - If the declaration includes the `sealed` modifier, then the declaration also includes the `override` modifier. -- If the declaration includes the `partial` modifier, then it does not include any of the following modifiers: new, `public`, `protected`, `internal`, `private`, `virtual`, `sealed`, `override`, `abstract`, or `extern`. +- If the declaration includes the `partial` modifier, then it does not include any of the following modifiers: `new`, `public`, `protected`, `internal`, `private`, `virtual`, `sealed`, `override`, `abstract`, or `extern`. + +Methods are classified according to what, if anything, they return: + +- If `ref` is present, the method is ***returns-by-ref*** and returns a *variable reference*, that is optionally read-only; +- Otherwise, if *return_type* is `void`, the method is ***returns-no-value*** and does not return a value; +- Otherwise, the method is ***returns-by-value*** and returns a value. -The *return_type* of a method declaration specifies the type of the value computed and returned by the method. The *return_type* is `void` if the method does not return a value. If the declaration includes the `partial` modifier, then the return type shall be `void` ([§14.6.9](classes.md#1469-partial-methods)). If the declaration includes the `async` modifier then the return type shall be `void` or a *task type* ([§14.15.1](classes.md#14151-general)). +The *return_type* of a returns-by-value or returns-no-value method declaration specifies the type of the result, if any, returned by the method. Only a returns-no-value method may include the `partial` modifier ([§15.6.9](classes.md#1569-partial-methods)). If the declaration includes the `async` modifier then *return_type* shall be `void` or the method returns-by-value and the return type is a *task type* ([§15.15.1](classes.md#15151-general)). -A generic method is a method whose declaration includes a *type_parameter_list*. This specifies the type parameters for the method. The optional *type_parameter_constraints_clause*s specify the constraints for the type parameters. A *method_declaration* shall not have *type_parameter_constraints_clauses* unless it also has a *type_parameter_list*. A *method_declaration* for an explicit interface member implementation shall not have any *type_parameter_constraints_clause*s. A generic *method_declaration* for an explicit interface member implementation inherits any constraints from the constraints on the interface method. Similarly, a method declaration with the `override` modifier shall not have any *type_parameter_constraints_clause*s and the constraints of the method’s type parameters are inherited from the virtual method being overridden.The *member_name* specifies the name of the method. Unless the method is an explicit interface member implementation ([§17.6.2](interfaces.md#1762-explicit-interface-member-implementations)), the *member_name* is simply an *identifier*. For an explicit interface member implementation, the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. In this case, the declaration shall include no modifiers other than (possibly) `extern` or `async`. +The *ref_return_type* of a returns-by-ref method declaration specifies the type of the variable referenced by the *variable_reference* returned by the method. -The optional *formal_parameter_list* specifies the parameters of the method ([§14.6.2](classes.md#1462-method-parameters)). +A generic method is a method whose declaration includes a *type_parameter_list*. This specifies the type parameters for the method. The optional *type_parameter_constraints_clause*s specify the constraints for the type parameters. -The *return_type* and each of the types referenced in the *formal_parameter_list* of a method shall be at least as accessible as the method itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). +A generic *method_declaration* for an explicit interface member implementation shall not have any *type_parameter_constraints_clause*s; the declaration inherits any constraints from the constraints on the interface method. -The *method_body* is either a semicolon, a ***block body*** or an ***expression body***. A block body consists of a *block*, which specifies the statements to execute when the method is invoked. An expression body consists of `=>`, followed by a *null_conditional_invocation_expression* or *expression*, followed by a semicolon, and denotes a single expression to perform when the method is invoked. +Similarly, a method declaration with the `override` modifier shall not have any *type_parameter_constraints_clause*s and the constraints of the method’s type parameters are inherited from the virtual method being overridden. + +The *member_name* specifies the name of the method. Unless the method is an explicit interface member implementation ([§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)), the *member_name* is simply an *identifier*. + +For an explicit interface member implementation, the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. In this case, the declaration shall include no modifiers other than (possibly) `extern` or `async`. + +The optional *formal_parameter_list* specifies the parameters of the method ([§15.6.2](classes.md#1562-method-parameters)). + +The *return_type* or *ref_return_type*, and each of the types referenced in the *formal_parameter_list* of a method, shall be at least as accessible as the method itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). + +The *method_body* of a returns-by-value or returns-no-value method is either a semicolon, a ***block body*** or an ***expression body***. A block body consists of a *block*, which specifies the statements to execute when the method is invoked. An expression body consists of `=>`, followed by a *null_conditional_invocation_expression* or *expression*, and a semicolon, and denotes a single expression to perform when the method is invoked. For abstract and extern methods, the *method_body* consists simply of a semicolon. For partial methods the *method_body* may consist of either a semicolon, a block body or an expression body. For all other methods, the *method_body* is either a block body or an expression body. If the *method_body* consists of a semicolon, the declaration shall not include the `async` modifier. -The name, the number of type parameters, and the formal parameter list of a method define the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of the method. Specifically, the signature of a method consists of its name, the number of its type parameters, and the number, *parameter_mode_modifier*s ([§14.6.2.1](classes.md#14621-general)), and types of its formal parameters. The return type is not part of a method’s signature, nor are the names of the formal parameters, the names of the type parameters, or the constraints. When a formal parameter type references a type parameter of the method, the ordinal position of the type parameter (not the name of the type parameter) is used for type equivalence. +The *ref_method_body* of a returns-by-ref method is either a semicolon, a ***block body*** or an ***expression body***. A block body consists of a *block*, which specifies the statements to execute when the method is invoked. An expression body consists of `=>`, followed by `ref`, a *variable_reference*, and a semicolon, and denotes a single *variable_reference* to evaluate when the method is invoked. + +For abstract and extern methods, the *ref_method_body* consists simply of a semicolon; for all other methods, the *ref_method_body* is either a block body or an expression body. -The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by `ref` and `out`. +The name, the number of type parameters, and the formal parameter list of a method define the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of the method. Specifically, the signature of a method consists of its name, the number of its type parameters, and the number, *parameter_mode_modifier*s ([§15.6.2.1](classes.md#15621-general)), and types of its formal parameters. The return type is not part of a method’s signature, nor are the names of the formal parameters, the names of the type parameters, or the constraints. When a formal parameter type references a type parameter of the method, the ordinal position of the type parameter (not the name of the type parameter) is used for type equivalence. -The method’s *type_parameter*s are in scope throughout the *method_declaration*, and can be used to form types throughout that scope in *return_type*, *method_body*, and *type_parameter_constraints_clause*s but not in *attributes*. +The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by `in`, `out`, and `ref`. + +The method’s *type_parameter*s are in scope throughout the *method_declaration*, and can be used to form types throughout that scope in *return_type* or *ref_return_type*, *method_body* or *ref_method_body*, and *type_parameter_constraints_clause*s but not in *attributes*. All formal parameters and type parameters shall have different names. -### 14.6.2 Method parameters +### 15.6.2 Method parameters -#### 14.6.2.1 General +#### 15.6.2.1 General The parameters of a method, if any, are declared by the method’s *formal_parameter_list*. @@ -2017,6 +2072,7 @@ parameter_modifier parameter_mode_modifier : 'ref' | 'out' + | 'in' ; parameter_array @@ -2026,9 +2082,9 @@ parameter_array The formal parameter list consists of one or more comma-separated parameters of which only the last may be a *parameter_array*. -A *fixed_parameter* consists of an optional set of *attributes* ([§21](attributes.md#21-attributes)); an optional `ref`, `out`, or `this` modifier; a *type*; an *identifier*; and an optional *default_argument*. Each *fixed_parameter* declares a parameter of the given type with the given name. The `this` modifier designates the method as an extension method and is only allowed on the first parameter of a static method in a non-generic, non-nested static class. Extension methods are further described in [§14.6.10](classes.md#14610-extension-methods). A *fixed_parameter* with a *default_argument* is known as an ***optional parameter***, whereas a *fixed_parameter* without a *default_argument* is a ***required parameter***. A required parameter may not appear after an optional parameter in a *formal_parameter_list*. +A *fixed_parameter* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)); an optional `in`, `out`, `ref`, or `this` modifier; a *type*; an *identifier*; and an optional *default_argument*. Each *fixed_parameter* declares a parameter of the given type with the given name. The `this` modifier designates the method as an extension method and is only allowed on the first parameter of a static method in a non-generic, non-nested static class. If the parameter is a `struct` type or a type parameter constrained to a `struct`, the `this` modifier may be combined with either the `ref` or `in` modifier, but not the `out` modifier. Extension methods are further described in [§15.6.10](classes.md#15610-extension-methods). A *fixed_parameter* with a *default_argument* is known as an ***optional parameter***, whereas a *fixed_parameter* without a *default_argument* is a ***required parameter***. A required parameter may not appear after an optional parameter in a *formal_parameter_list*. -A parameter with a `ref`, `out` or `this` modifier cannot have a *default_argument*. The *expression* in a *default_argument* shall be one of the following: +A parameter with a `ref`, `out` or `this` modifier cannot have a *default_argument*. A parameter with an `in` modifier may have a *default_argument*. The *expression* in a *default_argument* shall be one of the following: - a *constant_expression* - an expression of the form `new S()` where `S` is a value type @@ -2036,9 +2092,9 @@ A parameter with a `ref`, `out` or `this` modifier cannot have a *default_argume The *expression* shall be implicitly convertible by an identity or nullable conversion to the type of the parameter. -If optional parameters occur in an implementing partial method declaration ([§14.6.9](classes.md#1469-partial-methods)), an explicit interface member implementation ([§17.6.2](interfaces.md#1762-explicit-interface-member-implementations)), a single-parameter indexer declaration ([§14.9](classes.md#149-indexers)), or in an operator declaration ([§14.10.1](classes.md#14101-general)) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted. +If optional parameters occur in an implementing partial method declaration ([§15.6.9](classes.md#1569-partial-methods)), an explicit interface member implementation ([§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)), a single-parameter indexer declaration ([§15.9](classes.md#159-indexers)), or in an operator declaration ([§15.10.1](classes.md#15101-general)) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted. -A *parameter_array* consists of an optional set of *attributes* ([§21](attributes.md#21-attributes)), a `params` modifier, an *array_type*, and an *identifier*. A parameter array declares a single parameter of the given array type with the given name. The *array_type* of a parameter array shall be a single-dimensional array type ([§16.2](arrays.md#162-array-types)). In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. Parameter arrays are described further in [§14.6.2.5](classes.md#14625-parameter-arrays). +A *parameter_array* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), a `params` modifier, an *array_type*, and an *identifier*. A parameter array declares a single parameter of the given array type with the given name. The *array_type* of a parameter array shall be a single-dimensional array type ([§17.2](arrays.md#172-array-types)). In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. Parameter arrays are described further in [§15.6.2.6](classes.md#15626-parameter-arrays). A *parameter_array* may occur after an optional parameter, but cannot have a default value – the omission of arguments for a *parameter_array* would instead result in the creation of an empty array. @@ -2064,34 +2120,53 @@ A *parameter_array* may occur after an optional parameter, but cannot have a def A method declaration creates a separate declaration space ([§7.3](basic-concepts.md#73-declarations)) for parameters and type parameters. Names are introduced into this declaration space by the type parameter list and the formal parameter list of the method. The body of the method, if any, is considered to be nested within this declaration space. It is an error for two members of a method declaration space to have the same name. It is an error for the method declaration space and the local variable declaration space of a nested declaration space to contain elements with the same name. -A method invocation ([§11.7.8.2](expressions.md#11782-method-invocations)) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. Within the *block* of a method, formal parameters can be referenced by their identifiers in *simple_name* expressions ([§11.7.4](expressions.md#1174-simple-names)). +A method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. Within the *block* of a method, formal parameters can be referenced by their identifiers in *simple_name* expressions ([§12.8.4](expressions.md#1284-simple-names)). -There are four kinds of formal parameters: +The following kinds of formal parameters exist: - Value parameters, which are declared without any modifiers. -- Reference parameters, which are declared with the `ref` modifier. +- Input parameters, which are declared with the `in` modifier. - Output parameters, which are declared with the `out` modifier. +- Reference parameters, which are declared with the `ref` modifier. - Parameter arrays, which are declared with the `params` modifier. -> *Note*: As described in [§7.6](basic-concepts.md#76-signatures-and-overloading), the `ref` and `out` modifiers are part of a method’s signature, but the `params` modifier is not. +> *Note*: As described in [§7.6](basic-concepts.md#76-signatures-and-overloading), the `in`, `out`, and `ref` modifiers are part of a method’s signature, but the `params` modifier is not. *end note* -#### 14.6.2.2 Value parameters +#### 15.6.2.2 Value parameters -A parameter declared with no modifiers is a value parameter. A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation. +A parameter declared with no modifiers is a value parameter. A value parameter is a local variable that gets its initial value from the corresponding argument supplied in the method invocation. When a formal parameter is a value parameter, the corresponding argument in a method invocation shall be an expression that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the formal parameter type. A method is permitted to assign new values to a value parameter. Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation. -#### 14.6.2.3 Reference parameters +#### 15.6.2.3 Input parameters + +A parameter declared with an `in` modifier is an input parameter. An input parameter is a local reference variable ([§9.7](variables.md#97-reference-variables-and-returns)) that gets its initial referent from the corresponding argument supplied in the method invocation. That argument is either a variable existing at the point of the method invocation, or one created by the implementation ([§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists)) in the method invocation. + +> *Note*: As with reference variables the referent of an input parameter can be changed using the ref assignment (`= ref`) operator, however the value stored in the referent itself cannot be changed. *end note* + +When a formal parameter is an input parameter, the corresponding argument in a method invocation shall consist of either the keyword `in` followed by a *variable_reference* ([§9.2.8](variables.md#928-input-parameters)) of the same type as the formal parameter, or an *expression* for which an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from that argument expression to the type of the corresponding parameter. A variable shall be definitely assigned before it can be passed as an input parameter. + +It is a compile-time error to modify the value of an input parameter. + +Within a method, an input parameter is always considered definitely assigned. + +Input parameters are not allowed on functions declared as an iterator ([§15.14](classes.md#1514-iterators)) or async function ([§15.15](classes.md#1515-async-functions)). + +In a method that takes input parameters, it is possible for multiple names to represent the same storage location. + +#### 15.6.2.4 Reference parameters -A parameter declared with a `ref` modifier is a reference parameter. Unlike a value parameter, a reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation. +A parameter declared with a `ref` modifier is a reference parameter. A reference parameter is a local reference variable ([§9.7](variables.md#97-reference-variables-and-returns)) that gets its initial referent from the corresponding argument supplied in the method invocation. + +> *Note*: As with reference variables the referent of a reference parameter can be changed using the ref assignment (`= ref`) operator. *end note* When a formal parameter is a reference parameter, the corresponding argument in a method invocation shall consist of the keyword `ref` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) of the same type as the formal parameter. A variable shall be definitely assigned before it can be passed as a reference parameter. Within a method, a reference parameter is always considered definitely assigned. -A method declared as an iterator ([§14.14](classes.md#1414-iterators)) may not have reference parameters. +A method declared as an iterator ([§15.14](classes.md#1514-iterators)) may not have reference parameters. > *Example*: The example > @@ -2152,9 +2227,9 @@ In a method that takes reference parameters, it is possible for multiple names t > > *end example* -#### 14.6.2.4 Output parameters +#### 15.6.2.5 Output parameters -A parameter declared with an `out` modifier is an output parameter. Similar to a reference parameter, an output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation. +A parameter declared with an `out` modifier is an output parameter. An output parameter is a local reference variable ([§9.7](variables.md#97-reference-variables-and-returns)) that gets its initial referent from the corresponding argument supplied in the method invocation. When a formal parameter is an output parameter, the corresponding argument in a method invocation shall consist of the keyword `out` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) of the same type as the formal parameter. A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned. @@ -2162,7 +2237,7 @@ Within a method, just like a local variable, an output parameter is initially co Every output parameter of a method shall be definitely assigned before the method returns. -A method declared as a partial method ([§14.6.9](classes.md#1469-partial-methods)) or an iterator ([§14.14](classes.md#1414-iterators)) may not have output parameters. +A method declared as a partial method ([§15.6.9](classes.md#1569-partial-methods)) or an iterator ([§15.14](classes.md#1514-iterators)) may not have output parameters. Output parameters are typically used in methods that produce multiple return values. @@ -2209,20 +2284,22 @@ Output parameters are typically used in methods that produce multiple return val > > *end example* -#### 14.6.2.5 Parameter arrays +#### 15.6.2.6 Parameter arrays A parameter declared with a `params` modifier is a parameter array. If a formal parameter list includes a parameter array, it shall be the last parameter in the list and it shall be of a single-dimensional array type. > *Example*: The types `string[]` and `string[][]` can be used as the type of a parameter array, but the type `string[,]` can not. *end example* + -It is not possible to combine the `params` modifier with the modifiers `ref` and `out`. + +> *Note*: It is not possible to combine the `params` modifier with the modifiers `in`, `out`, or `ref`. *end note* A parameter array permits arguments to be specified in one of two ways in a method invocation: - The argument given for a parameter array can be a single expression that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the parameter array type. In this case, the parameter array acts precisely like a value parameter. - Alternatively, the invocation can specify zero or more arguments for the parameter array, where each argument is an expression that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the element type of the parameter array. In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument. -Except for allowing a variable number of arguments in an invocation, a parameter array is precisely equivalent to a value parameter ([§14.6.2.2](classes.md#14622-value-parameters)) of the same type. +Except for allowing a variable number of arguments in an invocation, a parameter array is precisely equivalent to a value parameter ([§15.6.2.2](classes.md#15622-value-parameters)) of the same type. > *Example*: The example > @@ -2267,7 +2344,7 @@ Except for allowing a variable number of arguments in an invocation, a parameter > > *end example* -When performing overload resolution, a method with a parameter array might be applicable, either in its normal form or in its expanded form ([§11.6.4.2](expressions.md#11642-applicable-function-member)). The expanded form of a method is available only if the normal form of the method is not applicable and only if an applicable method with the same signature as the expanded form is not already declared in the same type. +When performing overload resolution, a method with a parameter array might be applicable, either in its normal form or in its expanded form ([§12.6.4.2](expressions.md#12642-applicable-function-member)). The expanded form of a method is available only if the normal form of the method is not applicable and only if an applicable method with the same signature as the expanded form is not already declared in the same type. > *Example*: The example > @@ -2384,25 +2461,25 @@ When the type of a parameter array is `object[]`, a potential ambiguity arises b > > *end example* -### 14.6.3 Static and instance methods +### 15.6.3 Static and instance methods When a method declaration includes a `static` modifier, that method is said to be a static method. When no `static` modifier is present, the method is said to be an instance method. A static method does not operate on a specific instance, and it is a compile-time error to refer to `this` in a static method. -An instance method operates on a given instance of a class, and that instance can be accessed as `this` ([§11.7.12](expressions.md#11712-this-access)). +An instance method operates on a given instance of a class, and that instance can be accessed as `this` ([§12.8.13](expressions.md#12813-this-access)). -The differences between static and instance members are discussed further in [§14.3.8](classes.md#1438-static-and-instance-members). +The differences between static and instance members are discussed further in [§15.3.8](classes.md#1538-static-and-instance-members). -### 14.6.4 Virtual methods +### 15.6.4 Virtual methods When an instance method declaration includes a virtual modifier, that method is said to be a ***virtual method***. When no virtual modifier is present, the method is said to be a ***non-virtual method***. -The implementation of a non-virtual method is invariant: The implementation is the same whether the method is invoked on an instance of the class in which it is declared or an instance of a derived class. In contrast, the implementation of a virtual method can be superseded by derived classes. The process of superseding the implementation of an inherited virtual method is known as ***overriding*** that method ([§14.6.5](classes.md#1465-override-methods)). +The implementation of a non-virtual method is invariant: The implementation is the same whether the method is invoked on an instance of the class in which it is declared or an instance of a derived class. In contrast, the implementation of a virtual method can be superseded by derived classes. The process of superseding the implementation of an inherited virtual method is known as ***overriding*** that method ([§15.6.5](classes.md#1565-override-methods)). In a virtual method invocation, the ***run-time type*** of the instance for which that invocation takes place determines the actual method implementation to invoke. In a non-virtual method invocation, the ***compile-time type*** of the instance is the determining factor. In precise terms, when a method named `N` is invoked with an argument list `A` on an instance with a compile-time type `C` and a run-time type `R` (where `R` is either `C` or a class derived from `C`), the invocation is processed as follows: -- At binding-time, overload resolution is applied to `C`, `N`, and `A`, to select a specific method `M` from the set of methods declared in and inherited by `C`. This is described in [§11.7.8.2](expressions.md#11782-method-invocations). +- At binding-time, overload resolution is applied to `C`, `N`, and `A`, to select a specific method `M` from the set of methods declared in and inherited by `C`. This is described in [§12.8.9.2](expressions.md#12892-method-invocations). - Then at run-time: - If `M` is a non-virtual method, `M` is invoked. - Otherwise, `M` is a virtual method, and the most derived implementation of `M` with respect to `R` is invoked. @@ -2511,7 +2588,7 @@ Because methods are allowed to hide inherited methods, it is possible for a clas > > *end example* -### 14.6.5 Override methods +### 15.6.5 Override methods When an instance method declaration includes an `override` modifier, the method is said to be an ***override method***. An override method overrides an inherited virtual method with the same signature. Whereas a virtual method declaration *introduces* a new method, an override method declaration *specializes* an existing inherited virtual method by providing a new implementation of that method. @@ -2555,7 +2632,7 @@ A compile-time error occurs unless all of the following are true for an override > > *end example* -An override declaration can access the overridden base method using a *base_access* ([§11.7.13](expressions.md#11713-base-access)). +An override declaration can access the overridden base method using a *base_access* ([§12.8.14](expressions.md#12814-base-access)). > *Example*: In the following code > @@ -2631,7 +2708,7 @@ Only by including an `override` modifier can a method override another method. I > > *end example* -### 14.6.6 Sealed methods +### 15.6.6 Sealed methods When an instance method declaration includes a `sealed` modifier, that method is said to be a ***sealed method***. A sealed method overrides an inherited virtual method with the same signature. A sealed method shall also be marked with the `override` modifier. Use of the `sealed` modifier prevents a derived class from further overriding the method. @@ -2661,13 +2738,13 @@ When an instance method declaration includes a `sealed` modifier, that method is > > *end example* -### 14.6.7 Abstract methods +### 15.6.7 Abstract methods When an instance method declaration includes an `abstract` modifier, that method is said to be an ***abstract method***. Although an abstract method is implicitly also a virtual method, it cannot have the modifier `virtual`. -An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Because an abstract method provides no actual implementation, the *method_body* of an abstract method simply consists of a semicolon. +An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Because an abstract method provides no actual implementation, the method body of an abstract method simply consists of a semicolon. -Abstract method declarations are only permitted in abstract classes ([§14.2.2.2](classes.md#14222-abstract-classes)). +Abstract method declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)). > *Example*: In the following code > @@ -2693,7 +2770,7 @@ Abstract method declarations are only permitted in abstract classes ([§14.2.2.2 > > *end example* -It is a compile-time error for a *base_access* ([§11.7.13](expressions.md#11713-base-access)) to reference an abstract method. +It is a compile-time error for a *base_access* ([§12.8.14](expressions.md#12814-base-access)) to reference an abstract method. > *Example*: In the following code > @@ -2741,9 +2818,9 @@ An abstract method declaration is permitted to override a virtual method. This a > > *end example* -### 14.6.8 External methods +### 15.6.8 External methods -When a method declaration includes an `extern` modifier, the method is said to be an ***external method***. External methods are implemented externally, typically using a language other than C#. Because an external method declaration provides no actual implementation, the *method_body* of an external method simply consists of a semicolon. An external method shall not be generic. +When a method declaration includes an `extern` modifier, the method is said to be an ***external method***. External methods are implemented externally, typically using a language other than C#. Because an external method declaration provides no actual implementation, the method body of an external method simply consists of a semicolon. An external method shall not be generic. The mechanism by which linkage to an external method is achieved, is implementation-defined. @@ -2769,9 +2846,9 @@ The mechanism by which linkage to an external method is achieved, is implementat > > *end example* -### 14.6.9 Partial methods +### 15.6.9 Partial methods -When a method declaration includes a `partial` modifier, that method is said to be a ***partial method***. Partial methods may only be declared as members of partial types ([§14.2.7](classes.md#1427-partial-declarations)), and are subject to a number of restrictions. +When a method declaration includes a `partial` modifier, that method is said to be a ***partial method***. Partial methods may only be declared as members of partial types ([§15.2.7](classes.md#1527-partial-declarations)), and are subject to a number of restrictions. Partial methods may be defined in one part of a type declaration and implemented in another. The implementation is optional; if no part implements the partial method, the partial method declaration and all calls to it are removed from the type declaration resulting from the combination of the parts. @@ -2787,20 +2864,17 @@ An implementing partial method declaration can appear in the same part as the co Only a defining partial method participates in overload resolution. Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Because a partial method always returns `void`, such invocation expressions will always be expression statements. Furthermore, because a partial method is implicitly `private`, such statements will always occur within one of the parts of the type declaration within which the partial method is declared. -> *Note*: The definition of matching defining and implementing partial method declarations does not require parameter names to match. This can produce *surprising*, albeit *well defined*, behaviour when named arguments ([§11.6.2.1](expressions.md#11621-general)) are used. For example, given the defining partial method declaration for `M`: +> *Note*: The definition of matching defining and implementing partial method declarations does not require parameter names to match. This can produce *surprising*, albeit *well defined*, behaviour when named arguments ([§12.6.2.1](expressions.md#12621-general)) are used. For example, given the defining partial method declaration for `M` in one file, and the implementing partial method declaration in another file: > -> +> > ```csharp +> // File P1.cs: > partial class P > { > static partial void M(int x); > } -> ``` > -> Then the implementing partial method declaration and invocation in other file: -> -> -> ```csharp +> // File P2.cs: > partial class P > { > static void Caller() => M(y: 0); @@ -2824,7 +2898,7 @@ If an implementing declaration exists for a given partial method, the invocation If a defining declaration but not an implementing declaration is given for a partial method `M`, the following restrictions apply: -- It is a compile-time error to create a delegate from `M` ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)). +- It is a compile-time error to create a delegate from `M` ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)). - It is a compile-time error to refer to `M` inside an anonymous function that is converted to an expression tree type ([§8.6](types.md#86-expression-tree-types)). @@ -2835,6 +2909,7 @@ If a defining declaration but not an implementing declaration is given for a par Partial methods are useful for allowing one part of a type declaration to customize the behavior of another part, e.g., one that is generated by a tool. Consider the following partial class declaration: + ```csharp partial class Customer { @@ -2874,7 +2949,7 @@ class Customer Assume that another part is given, however, which provides implementing declarations of the partial methods: - + ```csharp partial class Customer { @@ -2913,13 +2988,18 @@ class Customer } ``` -### 14.6.10 Extension methods +### 15.6.10 Extension methods -When the first parameter of a method includes the `this` modifier, that method is said to be an ***extension method***. Extension methods shall only be declared in non-generic, non-nested static classes. The first parameter of an extension method may have no modifiers other than `this`, and the parameter type may not be a pointer type. +When the first parameter of a method includes the `this` modifier, that method is said to be an ***extension method***. Extension methods shall only be declared in non-generic, non-nested static classes. The first parameter of an extension method is restricted, as follows: + +- It may have the parameter modifier `in` only if the parameter has a value type +- It may have the parameter modifier `ref` only if the parameter has a value type or is a generic type constrained to struct +- It shall not be a pointer type. > *Example*: The following is an example of a static class that declares two extension methods: > > +> > ```csharp > public static class Extensions > { @@ -2940,11 +3020,11 @@ When the first parameter of a method includes the `this` modifier, that method i > > *end example* -An extension method is a regular static method. In addition, where its enclosing static class is in scope, an extension method may be invoked using instance method invocation syntax ([§11.7.8.3](expressions.md#11783-extension-method-invocations)), using the receiver expression as the first argument. +An extension method is a regular static method. In addition, where its enclosing static class is in scope, an extension method may be invoked using instance method invocation syntax ([§12.8.9.3](expressions.md#12893-extension-method-invocations)), using the receiver expression as the first argument. > *Example*: The following program uses the extension methods declared above: > -> +> > ```csharp > static class Program > { @@ -2961,7 +3041,7 @@ An extension method is a regular static method. In addition, where its enclosing > > The `Slice` method is available on the `string[]`, and the `ToInt32` method is available on `string`, because they have been declared as extension methods. The meaning of the program is the same as the following, using ordinary static method calls: > -> +> > ```csharp > static class Program > { @@ -2978,21 +3058,23 @@ An extension method is a regular static method. In addition, where its enclosing > > *end example* -### 14.6.11 Method body +### 15.6.11 Method body + +The method body of a method declaration consists of either a block body, an expression body or a semicolon. -The *method_body* of a method declaration consists of either a block body, an expression body or a semicolon. +Abstract and external method declarations do not provide a method implementation, so their method bodies simply consist of a semicolon. For any other method, the method body is a block ([§13.3](statements.md#133-blocks)) that contains the statements to execute when that method is invoked. -Abstract and external method declarations do not provide a method implementation, so their method bodies simply consist of a semicolon. For any other method, the method body is a block ([§12.3](statements.md#123-blocks)) that contains the statements to execute when that method is invoked. +The ***effective return type*** of a method is `void` if the return type is `void`, or if the method is async and the return type is `«TaskType»` ([§15.15.1](classes.md#15151-general)). Otherwise, the effective return type of a non-async method is its return type, and the effective return type of an async method with return type `«TaskType»`([§15.15.1](classes.md#15151-general)) is `T`. -The ***effective return type*** of a method is `void` if the return type is `void`, or if the method is async and the return type is `System.Threading.Tasks.Task`. Otherwise, the effective return type of a non-async method is its return type, and the effective return type of an async method with return type `System.Threading.Tasks.Task` is `T`. +When the effective return type of a method is `void` and the method has a block body, `return` statements ([§13.10.5](statements.md#13105-the-return-statement)) in the block shall not specify an expression. If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its caller. -When the effective return type of a method is `void` and the method has a block body, `return` statements ([§12.10.5](statements.md#12105-the-return-statement)) in the block shall not specify an expression. If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its caller. +When the effective return type of a method is `void` and the method has an expression body, the expression `E` shall be a *statement_expression*, and the body is exactly equivalent to a block body of the form `{ E; }`. -When the effective return type of a method is `void` and the method has an expression body, the expression `E` shall be a *statement_expression*, and the body is exactly equivalent to a statment body of the form `{ E; }`. +For a returns-by-value method ([§15.6.1](classes.md#1561-general)), each return statement in that method’s body shall specify an expression that is implicitly convertible to the effective return type. -When the effective return type of a method is not `void` and the method has a block body, each return statement in that method’s body shall specify an expression that is implicitly convertible to the effective return type. The endpoint of the method body of a value-returning method shall not be reachable. In other words, in a value-returning method with a block body, control is not permitted to flow off the end of the method body. +For a returns-by-ref method ([§15.6.1](classes.md#1561-general)), each return statement in that method’s body shall specify an expression whose type is that of the effective return type, and has a *ref-safe-context* of *caller-context* ([§9.7.2](variables.md#972-ref-safe-contexts)). -When the effective return type of a method is not `void` and the method has an expression body, `E`, the expression shall be implicitly convertible to the effective return type, and the body is exactly equivalent to a block body of the form `{ return E; }`. +For returns-by-value and returns-by-ref methods the endpoint of the method body shall not be reachable. In other words, control is not permitted to flow off the end of the method body. > *Example*: In the following code > @@ -3027,9 +3109,9 @@ When the effective return type of a method is not `void` and the method has an e > > *end example* -## 14.7 Properties +## 15.7 Properties -### 14.7.1 General +### 15.7.1 General A ***property*** is a member that provides access to a characteristic of an object or a class. Examples of properties include the length of a string, the size of a font, the caption of a window, the name of a customer, and so on. Properties are a natural extension of fields—both are named members with associated types, and the syntax for accessing fields and properties is the same. However, unlike fields, properties do not denote storage locations. Instead, properties have ***accessors*** that specify the statements to be executed when their values are read or written. Properties thus provide a mechanism for associating actions with the reading and writing of an object’s characteristics; furthermore, they permit such characteristics to be computed. @@ -3038,6 +3120,7 @@ Properties are declared using *property_declaration*s: ```ANTLR property_declaration : attributes? property_modifier* type member_name property_body + | attributes? property_modifier* ref_kind type member_name ref_property_body ; property_modifier @@ -3063,41 +3146,57 @@ property_body property_initializer : '=' variable_initializer ';' ; + +ref_property_body + : '{' ref_get_accessor_declaration '}' + | '=>' 'ref' variable_reference ';' + ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). + +There are two kinds of *property_declaration*: + +- The first declares a non-ref-valued property. Its value has type *type*. This kind of property may be readable and/or writeable. +- The second declares a ref-valued property. Its value is a *variable_reference* ([§9.5](variables.md#95-variable-references)), that may be `readonly`, to a variable of type *type*. This kind of property is only readable. -A *property_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)) and any one of the permitted kinds of declared accessibility ([§14.3.6](classes.md#1436-access-modifiers)), the `new` ([§14.3.5](classes.md#1435-the-new-modifier)), `static` ([§14.7.2](classes.md#1472-static-and-instance-properties)), `virtual` ([§14.6.4](classes.md#1464-virtual-methods), [§14.7.6](classes.md#1476-virtual-sealed-override-and-abstract-accessors)), `override` ([§14.6.5](classes.md#1465-override-methods), [§14.7.6](classes.md#1476-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§14.6.6](classes.md#1466-sealed-methods)), `abstract` ([§14.6.7](classes.md#1467-abstract-methods), [§14.7.6](classes.md#1476-virtual-sealed-override-and-abstract-accessors)), and `extern` ([§14.6.8](classes.md#1468-external-methods)) modifiers. +A *property_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.7.2](classes.md#1572-static-and-instance-properties)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods), [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors)), `override` ([§15.6.5](classes.md#1565-override-methods), [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods), [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors)), and `extern` ([§15.6.8](classes.md#1568-external-methods)) modifiers. -Property declarations are subject to the same rules as method declarations ([§14.6](classes.md#146-methods)) with regard to valid combinations of modifiers. +Property declarations are subject to the same rules as method declarations ([§15.6](classes.md#156-methods)) with regard to valid combinations of modifiers. -The *type* of a property declaration specifies the type of the property introduced by the declaration, and the *member_name* ([§14.6.1](classes.md#1461-general)) specifies the name of the property. Unless the property is an explicit interface member implementation, the *member_name* is simply an *identifier*. For an explicit interface member implementation ([§17.6.2](interfaces.md#1762-explicit-interface-member-implementations)), the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. +The *member_name* ([§15.6.1](classes.md#1561-general)) specifies the name of the property. Unless the property is an explicit interface member implementation, the *member_name* is simply an *identifier*. For an explicit interface member implementation ([§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)), the *member_name* consists of an *interface_type* followed by a “`.`” and an *identifier*. The *type* of a property shall be at least as accessible as the property itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -A *property_body* may either consist of an ***accessor body*** or an expression body. In an accessor body, *accessor_declarations*, which shall be enclosed in “`{`” and “`}`” tokens, declare the accessors ([§14.7.3](classes.md#1473-accessors)) of the property. The accessors specify the executable statements associated with reading and writing the property. +A *property_body* may either consist of a ***statement body*** or an ***expression body***. In a statement body, *accessor_declarations*, which shall be enclosed in “`{`” and “`}`” tokens, declare the accessors ([§15.7.3](classes.md#1573-accessors)) of the property. The accessors specify the executable statements associated with reading and writing the property. -An expression body consisting of `=>` followed by an *expression* `E` and a semicolon is exactly equivalent to the statement body `{ get { return E; } }`, and can therefore only be used to specify read-only properties where the result of the get accessor is given by a single expression. +In a *property_body* an expression body consisting of `=>` followed by an *expression* `E` and a semicolon is exactly equivalent to the statement body `{ get { return E; } }`, and can therefore only be used to specify read-only properties where the result of the get accessor is given by a single expression. -A *property_initializer* may only be given for an automatically implemented property ([§14.7.4](classes.md#1474-automatically-implemented-properties)), and causes the initialization of the underlying field of such properties with the value given by the *expression*. +A *property_initializer* may only be given for an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)), and causes the initialization of the underlying field of such properties with the value given by the *expression*. -Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Thus, it is not possible to pass a property as a `ref` or `out` argument. +A *ref_property_body* may either consist of a statement body or an expression body. In a statement body a *get_accessor_declaration* declares the get accessor ([§15.7.3](classes.md#1573-accessors)) of the property. The accessor specifies the executable statements associated with reading the property. + +In a *ref_property_body* an expression body consisting of `=>` followed by `ref`, a *variable_reference* `V` and a semicolon is exactly equivalent to the statement body `{ get { return ref V; } }`. + +> *Note*: Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Thus, it is not possible to pass a property as an `in`, `out`, or `ref` argument unless the property is ref-valued and therefore returns a variable reference ([§9.7](variables.md#97-reference-variables-and-returns)). *end note* When a property declaration includes an `extern` modifier, the property is said to be an ***external property***. Because an external property declaration provides no actual implementation, each of its *accessor_declarations* consists of a semicolon. -### 14.7.2 Static and instance properties +### 15.7.2 Static and instance properties When a property declaration includes a `static` modifier, the property is said to be a ***static property***. When no `static` modifier is present, the property is said to be an ***instance property***. A static property is not associated with a specific instance, and it is a compile-time error to refer to `this` in the accessors of a static property. -An instance property is associated with a given instance of a class, and that instance can be accessed as `this` ([§11.7.12](expressions.md#11712-this-access)) in the accessors of that property. +An instance property is associated with a given instance of a class, and that instance can be accessed as `this` ([§12.8.13](expressions.md#12813-this-access)) in the accessors of that property. -The differences between static and instance members are discussed further in [§14.3.8](classes.md#1438-static-and-instance-members). +The differences between static and instance members are discussed further in [§15.3.8](classes.md#1538-static-and-instance-members). -### 14.7.3 Accessors +### 15.7.3 Accessors -The *accessor_declarations* of a property specify the executable statements associated with reading and writing that property. +*Note*: This clause applies to both properties ([§15.7](classes.md#157-properties)) and indexers ([§15.9](classes.md#159-indexers)). The clause is written in terms of properties, when reading for indexers substitute indexer/indexers for property/properties and consult the list of differences between properties and indexers given in [§15.9.2](classes.md#1592-indexer-and-property-differences). *end note* + +The *accessor_declarations* of a property specify the executable statements associated with writing and/or reading that property. ```ANTLR accessor_declarations @@ -3128,9 +3227,21 @@ accessor_body | '=>' expression ';' | ';' ; + +ref_get_accessor_declaration + : attributes? accessor_modifier? 'get' ref_accessor_body + ; + +ref_accessor_body + : block + | '=>' 'ref' variable_reference ';' + | ';' + ; ``` -The accessor declarations consist of a *get_accessor_declaration*, a *set_accessor_declaration*, or both. Each accessor declaration consists of optional attributes, an optional *accessor_modifier*, the token `get` or `set`, followed by an *accessor_body*. +The *accessor_declarations* consist of a *get_accessor_declaration*, a *set_accessor_declaration*, or both. Each accessor declaration consists of optional attributes, an optional *accessor_modifier*, the token `get` or `set`, followed by an *accessor_body*. + +For a ref-valued property the *ref_get_accessor_declaration* consists optional attributes, an optional *accessor_modifier*, the token `get`, followed by an *ref_accessor_body*. The use of *accessor_modifier*s is governed by the following restrictions: @@ -3144,14 +3255,46 @@ The use of *accessor_modifier*s is governed by the following restrictions: - If the property or indexer has a declared accessibility of `private protected`, the accessibility declared by *accessor_modifier* shall be `private`. - If the property or indexer has a declared accessibility of `private`, no *accessor_modifier* may be used. -For `abstract` and `extern` properties, the *accessor_body* for each accessor specified is simply a semicolon. A non-abstract, non-extern property may also have the *accessor_body* for all accessors specified be a semicolon, in which case it is an ***automatically implemented property*** ([§14.7.4](classes.md#1474-automatically-implemented-properties)). An automatically implemented property shall have at least a get accessor. For the accessors of any other non-abstract, non-extern property, the *accessor_body* is either +For `abstract` and `extern` non-ref-valued properties, any *accessor_body* for each accessor specified is simply a semicolon. A non-abstract, non-extern property, but not an indexer, may also have the *accessor_body* for all accessors specified be a semicolon, in which case it is an ***automatically implemented property*** ([§15.7.4](classes.md#1574-automatically-implemented-properties)). An automatically implemented property shall have at least a get accessor. For the accessors of any other non-abstract, non-extern property, the *accessor_body* is either: - a *block* that specifies the statements to be executed when the corresponding accessor is invoked; or - an expression body, which consists of `=>` followed by an *expression* and a semicolon, and denotes a single expression to be executed when the corresponding accessor is invoked. -A get accessor corresponds to a parameterless method with a return value of the property type. Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property ([§11.2.2](expressions.md#1122-values-of-expressions)). The body of a get accessor shall conform to the rules for value-returning methods described in [§14.6.11](classes.md#14611-method-body). In particular, all `return` statements in the body of a get accessor shall specify an expression that is implicitly convertible to the property type. Furthermore, the endpoint of a get accessor shall not be reachable. +For `abstract` and `extern` ref-valued properties the *ref_accessor_body* is simply a semicolon. For the accessor of any other non-abstract, non-extern property, the *ref_accessor_body* is either: + +- a *block* that specifies the statements to be executed when the get accessor is invoked; or +- an expression body, which consists of `=>` followed by `ref`, a *variable_reference* and a semicolon. The variable reference is evaluated when the get accessor is invoked. + +A get accessor for a non-ref-valued property corresponds to a parameterless method with a return value of the property type. Except as the target of an assignment, when such a property is referenced in an expression its get accessor is invoked to compute the value of the property ([§12.2.2](expressions.md#1222-values-of-expressions)). -A `set` accessor corresponds to a method with a single value parameter of the property type and a `void` return type. The implicit parameter of a `set` accessor is always named `value`. When a property is referenced as the target of an assignment ([§11.19](expressions.md#1119-assignment-operators)), or as the operand of `++` or `–-` ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators), [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)), the `set` accessor is invoked with an argument that provides the new value ([§11.19.2](expressions.md#11192-simple-assignment)). The body of a `set` accessor shall conform to the rules for `void` methods described in [§14.6.11](classes.md#14611-method-body). In particular, return statements in the `set` accessor body are not permitted to specify an expression. Since a `set` accessor implicitly has a parameter named `value`, it is a compile-time error for a local variable or constant declaration in a `set` accessor to have that name. +The body of a get accessor for a non-ref-valued property shall conform to the rules for value-returning methods described in [§15.6.11](classes.md#15611-method-body). In particular, all `return` statements in the body of a get accessor shall specify an expression that is implicitly convertible to the property type. Furthermore, the endpoint of a get accessor shall not be reachable. + +A get accessor for a ref-valued property corresponds to a parameterless method with a return value of a *variable_reference* to a variable of the property type. When such a property is referenced in an expression its get accessor is invoked to compute the *variable_reference* value of the property. That *variable reference*, like any other, is then used to read or, for non-readonly *variable_reference*s, write the referenced variable as required by the context. + +> *Example*: The following example illustrates a ref-valued property as the target of an assignment: +> +> ```csharp +> class Program +> { +> static int field; +> static ref int Property => ref field; +> +> static void Main() +> { +> field = 10; +> Console.WriteLine(Property); // Prints 10 +> Property = 20; // This invokes the getter, then assigns +> // via the resulting variable reference +> Console.WriteLine(field); // Prints 20 +> } +> } +> ``` +> +> *end example* + +The body of a get accessor for a ref-valued property shall conform to the rules for ref-valued methods described in [§15.6.11](classes.md#15611-method-body). + +A set accessor corresponds to a method with a single value parameter of the property type and a `void` return type. The implicit parameter of a set accessor is always named `value`. When a property is referenced as the target of an assignment ([§12.21](expressions.md#1221-assignment-operators)), or as the operand of `++` or `–-` ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators), [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)), the set accessor is invoked with an argument that provides the new value ([§12.21.2](expressions.md#12212-simple-assignment)). The body of a set accessor shall conform to the rules for `void` methods described in [§15.6.11](classes.md#15611-method-body). In particular, return statements in the set accessor body are not permitted to specify an expression. Since a set accessor implicitly has a parameter named `value`, it is a compile-time error for a local variable or constant declaration in a set accessor to have that name. Based on the presence or absence of the get and set accessors, a property is classified as follows: @@ -3405,13 +3548,13 @@ Properties can be used to delay initialization of a resource until the moment it > > *end example* -### 14.7.4 Automatically implemented properties +### 15.7.4 Automatically implemented properties -An automatically implemented property (or auto-property for short), is a non-abstract, non-extern property with semicolon-only accessor bodies. Auto-properties shall have a get accessor and may optionally have a set accessor. +An automatically implemented property (or auto-property for short), is a non-abstract, non-extern, non-ref-valued property with semicolon-only accessor bodies. Auto-properties shall have a get accessor and may optionally have a set accessor. -When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. The hidden backing field is inaccessible, it can be read and written only through the automatically implemented property accessors, even within the containing type. If the auto-property has no set accessor, the backing field is considered `readonly` ([§14.5.3](classes.md#1453-readonly-fields)). Just like a `readonly` field, a read-only auto-property may also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the read-only backing field of the property. +When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. The hidden backing field is inaccessible, it can be read and written only through the automatically implemented property accessors, even within the containing type. If the auto-property has no set accessor, the backing field is considered `readonly` ([§15.5.3](classes.md#1553-readonly-fields)). Just like a `readonly` field, a read-only auto-property may also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the read-only backing field of the property. -An auto-property may optionally have a *property_initializer*, which is applied directly to the backing field as a *variable_initializer* ([§16.7](arrays.md#167-array-initializers)). +An auto-property may optionally have a *property_initializer*, which is applied directly to the backing field as a *variable_initializer* ([§17.7](arrays.md#177-array-initializers)). > *Example*: > @@ -3482,7 +3625,7 @@ An auto-property may optionally have a *property_initializer*, which is applied > > *end example* -Although the backing field is hidden, that field may have field-targeted attributes applied directly to it via the automatically implemented property’s *property_declaration* ([§14.7.1](classes.md#1471-general)). +Although the backing field is hidden, that field may have field-targeted attributes applied directly to it via the automatically implemented property’s *property_declaration* ([§15.7.1](classes.md#1571-general)). > *Example*: The following code > @@ -3515,17 +3658,17 @@ Although the backing field is hidden, that field may have field-targeted attribu > > *end example* -### 14.7.5 Accessibility +### 15.7.5 Accessibility If an accessor has an *accessor_modifier*, the accessibility domain ([§7.5.3](basic-concepts.md#753-accessibility-domains)) of the accessor is determined using the declared accessibility of the *accessor_modifier*. If an accessor does not have an *accessor_modifier*, the accessibility domain of the accessor is determined from the declared accessibility of the property or indexer. -The presence of an *accessor_modifier* never affects member lookup ([§11.5](expressions.md#115-member-lookup)) or overload resolution ([§11.6.4](expressions.md#1164-overload-resolution)). The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access. +The presence of an *accessor_modifier* never affects member lookup ([§12.5](expressions.md#125-member-lookup)) or overload resolution ([§12.6.4](expressions.md#1264-overload-resolution)). The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access. -Once a particular property or indexer has been selected, the accessibility domains of the specific accessors involved are used to determine if that usage is valid: +Once a particular non-ref-valued property or non-ref-valued indexer has been selected, the accessibility domains of the specific accessors involved are used to determine if that usage is valid: -- If the usage is as a value ([§11.2.2](expressions.md#1122-values-of-expressions)), the get accessor shall exist and be accessible. -- If the usage is as the target of a simple assignment ([§11.19.2](expressions.md#11192-simple-assignment)), the set accessor shall exist and be accessible. -- If the usage is as the target of compound assignment ([§11.19.3](expressions.md#11193-compound-assignment)), or as the target of the `++` or `--` operators ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators), [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)), both the get accessors and the set accessor shall exist and be accessible. +- If the usage is as a value ([§12.2.2](expressions.md#1222-values-of-expressions)), the get accessor shall exist and be accessible. +- If the usage is as the target of a simple assignment ([§12.21.2](expressions.md#12212-simple-assignment)), the set accessor shall exist and be accessible. +- If the usage is as the target of compound assignment ([§12.21.4](expressions.md#12214-compound-assignment)), or as the target of the `++` or `--` operators ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators), [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)), both the get accessors and the set accessor shall exist and be accessible. > *Example*: In the following example, the property `A.Text` is hidden by the property `B.Text`, even in contexts where only the set accessor is called. In contrast, the property `B.Count` is not accessible to class `M`, so the accessible property `A.Count` is used instead. > @@ -3579,6 +3722,8 @@ Once a particular property or indexer has been selected, the accessibility domai > > *end example* +Once a particular ref-valued property or ref-valued indexer has been selected; whether the usage is as a value, the target of a simple assignment, or the target of a compound assignment; the accessibility domain of the get accessor involved is used to determine if that usage is valid. + An accessor that is used to implement an interface shall not have an *accessor_modifier*. If only one accessor is used to implement an interface, the other accessor may be declared with an *accessor_modifier*: > *Example*: @@ -3602,7 +3747,9 @@ An accessor that is used to implement an interface shall not have an *accessor_m > > *end example* -### 14.7.6 Virtual, sealed, override, and abstract accessors +### 15.7.6 Virtual, sealed, override, and abstract accessors + +*Note*: This clause applies to both properties ([§15.7](classes.md#157-properties)) and indexers ([§15.9](classes.md#159-indexers)). The clause is written in terms of properties, when reading for indexers substitute indexer/indexers for property/properties and consult the list of differences between properties and indexers given in [§15.9.2](classes.md#1592-indexer-and-property-differences). *end note* A virtual property declaration specifies that the accessors of the property are virtual. The `virtual` modifier applies to all non-private accessors of a property. When an accessor of a virtual property has the private *accessor_modifier*, the `private` accessor is implicitly not virtual. @@ -3610,16 +3757,16 @@ An abstract property declaration specifies that the accessors of the property ar A property declaration that includes both the `abstract` and `override` modifiers specifies that the property is abstract and overrides a base property. The accessors of such a property are also abstract. -Abstract property declarations are only permitted in abstract classes ([§14.2.2.2](classes.md#14222-abstract-classes)). The accessors of an inherited virtual property can be overridden in a derived class by including a property declaration that specifies an `override` directive. This is known as an ***overriding property declaration***. An overriding property declaration does not declare a new property. Instead, it simply specializes the implementations of the accessors of an existing virtual property. +Abstract property declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)). The accessors of an inherited virtual property can be overridden in a derived class by including a property declaration that specifies an `override` directive. This is known as an ***overriding property declaration***. An overriding property declaration does not declare a new property. Instead, it simply specializes the implementations of the accessors of an existing virtual property. The override declaration and the overridden base property are required to have the same declared accessibility. In other words, an override declaration may not change the accessibility of the base property. However, if the overridden base property is protected internal and it is declared in a different assembly than the assembly containing the override declaration then the override declaration’s declared accessibility shall be protected. If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property shall include only that accessor. If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors. There shall be an identity conversion between the type of the overriding and the inherited property. An overriding property declaration may include the `sealed` modifier. Use of this modifier prevents a derived class from further overriding the property. The accessors of a sealed property are also sealed. -Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in [§14.6.4](classes.md#1464-virtual-methods), [§14.6.5](classes.md#1465-override-methods), [§14.6.6](classes.md#1466-sealed-methods), and [§14.6.7](classes.md#1467-abstract-methods) apply as if accessors were methods of a corresponding form: +Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in [§15.6.4](classes.md#1564-virtual-methods), [§15.6.5](classes.md#1565-override-methods), [§15.6.6](classes.md#1566-sealed-methods), and [§15.6.7](classes.md#1567-abstract-methods) apply as if accessors were methods of a corresponding form: -- A `get` accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property. -- A `set` accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property. +- A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property. +- A set accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property. > *Example*: In the following code > @@ -3704,9 +3851,9 @@ When a property is declared as an override, any overridden accessors shall be ac > > *end example* -## 14.8 Events +## 15.8 Events -### 14.8.1 General +### 15.8.1 General An ***event*** is a member that enables an object or class to provide notifications. Clients can attach executable code for events by supplying ***event handlers***. @@ -3748,15 +3895,15 @@ remove_accessor_declaration ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -An *event_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)) and any one of the permitted kinds of declared accessibility ([§14.3.6](classes.md#1436-access-modifiers)), the `new` ([§14.3.5](classes.md#1435-the-new-modifier)), `static` ([§14.6.3](classes.md#1463-static-and-instance-methods), [§14.8.4](classes.md#1484-static-and-instance-events)), `virtual` ([§14.6.4](classes.md#1464-virtual-methods), [§14.8.5](classes.md#1485-virtual-sealed-override-and-abstract-accessors)), `override` ([§14.6.5](classes.md#1465-override-methods), [§14.8.5](classes.md#1485-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§14.6.6](classes.md#1466-sealed-methods)), `abstract` ([§14.6.7](classes.md#1467-abstract-methods), [§14.8.5](classes.md#1485-virtual-sealed-override-and-abstract-accessors)), and `extern` ([§14.6.8](classes.md#1468-external-methods)) modifiers. +An *event_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `static` ([§15.6.3](classes.md#1563-static-and-instance-methods), [§15.8.4](classes.md#1584-static-and-instance-events)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `override` ([§15.6.5](classes.md#1565-override-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods), [§15.8.5](classes.md#1585-virtual-sealed-override-and-abstract-accessors)), and `extern` ([§15.6.8](classes.md#1568-external-methods)) modifiers. -Event declarations are subject to the same rules as method declarations ([§14.6](classes.md#146-methods)) with regard to valid combinations of modifiers. +Event declarations are subject to the same rules as method declarations ([§15.6](classes.md#156-methods)) with regard to valid combinations of modifiers. The *type* of an event declaration shall be a *delegate_type* ([§8.2.8](types.md#828-delegate-types)), and that *delegate_type* shall be at least as accessible as the event itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -An event declaration can include *event_accessor_declaration*s. However, if it does not, for non-extern, non-abstract events, the compiler shall supply them automatically ([§14.8.2](classes.md#1482-field-like-events)); for `extern` events, the accessors are provided externally. +An event declaration can include *event_accessor_declaration*s. However, if it does not, for non-extern, non-abstract events, the compiler shall supply them automatically ([§15.8.2](classes.md#1582-field-like-events)); for `extern` events, the accessors are provided externally. An event declaration that omits *event_accessor_declaration*s defines one or more events—one for each of the *variable_declarator*s. The attributes and modifiers apply to all of the members declared by such an *event_declaration*. @@ -3766,11 +3913,11 @@ When an event declaration includes an `extern` modifier, the event is said to be It is a compile-time error for a *variable_declarator* of an event declaration with an `abstract` or `external` modifier to include a *variable_initializer*. -An event can be used as the left-hand operand of the `+=` and `-=` operators. These operators are used, respectively, to attach event handlers to, or to remove event handlers from an event, and the access modifiers of the event control the contexts in which such operations are permitted. +An event can be used as the left operand of the `+=` and `-=` operators. These operators are used, respectively, to attach event handlers to, or to remove event handlers from an event, and the access modifiers of the event control the contexts in which such operations are permitted. The only operations that are permitted on an event by code that is outside the type in which that event is declared, are `+=` and `-=`. Therefore, while such code can add and remove handlers for an event, it cannot directly obtain or modify the underlying list of event handlers. -In an operation of the form `x += y` or `x –= y`, when `x` is an event the result of the operation has type `void` ([§11.19.4](expressions.md#11194-event-assignment)) (as opposed to having the type of `x`, with the value of `x` after the assignment, as for other the `+=` and `-=` operators defined on non-event types). This prevents external code from indirectly examining the underlying delegate of an event. +In an operation of the form `x += y` or `x –= y`, when `x` is an event the result of the operation has type `void` ([§12.21.5](expressions.md#12215-event-assignment)) (as opposed to having the type of `x`, with the value of `x` after the assignment, as for other the `+=` and `-=` operators defined on non-event types). This prevents external code from indirectly examining the underlying delegate of an event. > *Example*: The following example shows how event handlers are attached to instances of the `Button` class: > @@ -3812,9 +3959,9 @@ In an operation of the form `x += y` or `x –= y`, when `x` is an event the > > *end example* -### 14.8.2 Field-like events +### 15.8.2 Field-like events -Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event shall not be abstract or extern, and shall not explicitly include *event_accessor_declaration*s. Such an event can be used in any context that permits a field. The field contains a delegate ([§19](delegates.md#19-delegates)), which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains `null`. +Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event shall not be abstract or extern, and shall not explicitly include *event_accessor_declaration*s. Such an event can be used in any context that permits a field. The field contains a delegate ([§20](delegates.md#20-delegates)), which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains `null`. > *Example*: In the following code > @@ -3857,7 +4004,7 @@ Within the program text of the class or struct that contains the declaration of > > *end example* -When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock ([§9.4.4.19](variables.md#94419-lock-statements)) in the containing object for an instance event, or the type `object` ([§11.7.15.7](expressions.md#117157-anonymous-object-creation-expressions)) for a static event. +When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock ([§13.13](statements.md#1313-the-lock-statement)) on the containing object for an instance event, or the `System.Type` object ([§12.8.17](expressions.md#12817-the-typeof-operator)) for a static event. > *Note*: Thus, an instance event declaration of the form: > @@ -3891,11 +4038,11 @@ When compiling a field-like event, the compiler automatically creates storage to > } > ``` > -> Within the class `X`, references to `Ev` on the left-hand side of the `+=` and `–=` operators cause the add and remove accessors to be invoked. All other references to `Ev` are compiled to reference the hidden field `__Ev` instead ([§11.7.6](expressions.md#1176-member-access)). The name “`__Ev`” is arbitrary; the hidden field could have any name or no name at all. +> Within the class `X`, references to `Ev` on the left-hand side of the `+=` and `–=` operators cause the add and remove accessors to be invoked. All other references to `Ev` are compiled to reference the hidden field `__Ev` instead ([§12.8.7](expressions.md#1287-member-access)). The name “`__Ev`” is arbitrary; the hidden field could have any name or no name at all. > > *end note* -### 14.8.3 Event accessors +### 15.8.3 Event accessors > *Note*: Event declarations typically omit *event_accessor_declaration*s, as in the `Button` example above. For example, they might be included if the storage cost of one field per event is not acceptable. In such cases, a class can include *event_accessor_declaration*s and use a private mechanism for storing the list of event handlers. *end note* @@ -3903,7 +4050,7 @@ The *event_accessor_declarations* of an event specify the executable statements The accessor declarations consist of an *add_accessor_declaration* and a *remove_accessor_declaration*. Each accessor declaration consists of the token add or remove followed by a *block*. The *block* associated with an *add_accessor_declaration* specifies the statements to execute when an event handler is added, and the *block* associated with a *remove_accessor_declaration* specifies the statements to execute when an event handler is removed. -Each *add_accessor_declaration* and *remove_accessor_declaration* corresponds to a method with a single value parameter of the event type, and a `void` return type. The implicit parameter of an `event` accessor is named `value`. When an event is used in an event assignment, the appropriate `event` accessor is used. Specifically, if the assignment operator is `+=` then the add accessor is used, and if the assignment operator is `–=` then the remove accessor is used. In either case, the right-hand operand of the assignment operator is used as the argument to the `event` accessor. The block of an *add_accessor_declaration* or a *remove_accessor_declaration* shall conform to the rules for `void` methods described in [§14.6.9](classes.md#1469-partial-methods). In particular, `return` statements in such a block are not permitted to specify an expression. +Each *add_accessor_declaration* and *remove_accessor_declaration* corresponds to a method with a single value parameter of the event type, and a `void` return type. The implicit parameter of an `event` accessor is named `value`. When an event is used in an event assignment, the appropriate `event` accessor is used. Specifically, if the assignment operator is `+=` then the add accessor is used, and if the assignment operator is `–=` then the remove accessor is used. In either case, the right operand of the assignment operator is used as the argument to the `event` accessor. The block of an *add_accessor_declaration* or a *remove_accessor_declaration* shall conform to the rules for `void` methods described in [§15.6.9](classes.md#1569-partial-methods). In particular, `return` statements in such a block are not permitted to specify an expression. Since an `event` accessor implicitly has a parameter named `value`, it is a compile-time error for a local variable or constant declared in an `event` accessor to have that name. @@ -3958,17 +4105,17 @@ Since an `event` accessor implicitly has a parameter named `value`, it is a comp > > *end example* -### 14.8.4 Static and instance events +### 15.8.4 Static and instance events When an event declaration includes a `static` modifier, the event is said to be a ***static event***. When no `static` modifier is present, the event is said to be an ***instance event***. A static event is not associated with a specific instance, and it is a compile-time error to refer to `this` in the accessors of a static event. -An instance event is associated with a given instance of a class, and this instance can be accessed as `this` ([§11.7.12](expressions.md#11712-this-access)) in the accessors of that event. +An instance event is associated with a given instance of a class, and this instance can be accessed as `this` ([§12.8.13](expressions.md#12813-this-access)) in the accessors of that event. -The differences between static and instance members are discussed further in [§14.3.8](classes.md#1438-static-and-instance-members). +The differences between static and instance members are discussed further in [§15.3.8](classes.md#1538-static-and-instance-members). -### 14.8.5 Virtual, sealed, override, and abstract accessors +### 15.8.5 Virtual, sealed, override, and abstract accessors A virtual event declaration specifies that the accessors of that event are virtual. The `virtual` modifier applies to both accessors of an event. @@ -3976,7 +4123,7 @@ An abstract event declaration specifies that the accessors of the event are virt An event declaration that includes both the `abstract` and `override` modifiers specifies that the event is abstract and overrides a base event. The accessors of such an event are also abstract. -Abstract event declarations are only permitted in abstract classes ([§14.2.2.2](classes.md#14222-abstract-classes)). +Abstract event declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)). The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an `override` modifier. This is known as an ***overriding event declaration***. An overriding event declaration does not declare a new event. Instead, it simply specializes the implementations of the accessors of an existing virtual event. @@ -3986,15 +4133,18 @@ An overriding event declaration can include the `sealed` modifier. Use of `this` It is a compile-time error for an overriding event declaration to include a `new` modifier. -Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in [§14.6.4](classes.md#1464-virtual-methods), [§14.6.5](classes.md#1465-override-methods), [§14.6.6](classes.md#1466-sealed-methods), and [§14.6.7](classes.md#1467-abstract-methods) apply as if accessors were methods of a corresponding form. Each accessor corresponds to a method with a single value parameter of the event type, a `void` return type, and the same modifiers as the containing event. +Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in [§15.6.4](classes.md#1564-virtual-methods), [§15.6.5](classes.md#1565-override-methods), [§15.6.6](classes.md#1566-sealed-methods), and [§15.6.7](classes.md#1567-abstract-methods) apply as if accessors were methods of a corresponding form. Each accessor corresponds to a method with a single value parameter of the event type, a `void` return type, and the same modifiers as the containing event. -## 14.9 Indexers +## 15.9 Indexers + +### 15.9.1 General An ***indexer*** is a member that enables an object to be indexed in the same way as an array. Indexers are declared using *indexer_declaration*s: ```ANTLR indexer_declaration : attributes? indexer_modifier* indexer_declarator indexer_body + | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body ; indexer_modifier @@ -4020,55 +4170,50 @@ indexer_body : '{' accessor_declarations '}' | '=>' expression ';' ; + +ref_indexer_body + : '{' ref_get_accessor_declaration '}' + | '=>' 'ref' variable_reference ';' + ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). + +There are two kinds of *indexer_declaration*: -An *indexer_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)) and any one of the permitted kinds of declared accessibility ([§14.3.6](classes.md#1436-access-modifiers)), the `new` ([§14.3.5](classes.md#1435-the-new-modifier)), `virtual` ([§14.6.4](classes.md#1464-virtual-methods)), `override` ([§14.6.5](classes.md#1465-override-methods)), `sealed` ([§14.6.6](classes.md#1466-sealed-methods)), `abstract` ([§14.6.7](classes.md#1467-abstract-methods)), and `extern` ([§14.6.8](classes.md#1468-external-methods)) modifiers. +- The first declares a non-ref-valued indexer. Its value has type *type*. This kind of indexer may be readable and/or writeable. +- The second declares a ref-valued indexer. Its value is a *variable_reference* ([§9.5](variables.md#95-variable-references)), that may be `readonly`, to a variable of type *type*. This kind of indexer is only readable. -Indexer declarations are subject to the same rules as method declarations ([§14.6](classes.md#146-methods)) with regard to valid combinations of modifiers, with the one exception being that the `static` modifier is not permitted on an indexer declaration. +An *indexer_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), the `new` ([§15.3.5](classes.md#1535-the-new-modifier)), `virtual` ([§15.6.4](classes.md#1564-virtual-methods)), `override` ([§15.6.5](classes.md#1565-override-methods)), `sealed` ([§15.6.6](classes.md#1566-sealed-methods)), `abstract` ([§15.6.7](classes.md#1567-abstract-methods)), and `extern` ([§15.6.8](classes.md#1568-external-methods)) modifiers. -The modifiers `virtual`, `override`, and `abstract` are mutually exclusive except in one case. The `abstract` and `override` modifiers may be used together so that an abstract indexer can override a virtual one. +Indexer declarations are subject to the same rules as method declarations ([§15.6](classes.md#156-methods)) with regard to valid combinations of modifiers, with the one exception being that the `static` modifier is not permitted on an indexer declaration. The *type* of an indexer declaration specifies the element type of the indexer introduced by the declaration. -> *Note:* As indexers are designed to be used in array element-like contexts, the term *element type* as defined for an array is also used with an indexer. *end note* +> *Note*: As indexers are designed to be used in array element-like contexts, the term *element type* as defined for an array is also used with an indexer. *end note* + +The *formal_parameter_list* specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method ([§15.6.2](classes.md#1562-method-parameters)), except that at least one parameter shall be specified, and that the `this`, `out`, and `ref` parameter modifiers are not permitted. Unless the indexer is an explicit interface member implementation, the *type* is followed by the keyword `this`. For an explicit interface member implementation, the *type* is followed by an *interface_type*, a “`.`”, and the keyword `this`. Unlike other members, indexers do not have user-defined names. -The *formal_parameter_list* specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method ([§14.6.2](classes.md#1462-method-parameters)), except that at least one parameter shall be specified, and that the `this`, `ref`, and `out` parameter modifiers are not permitted. +The *formal_parameter_list* specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method ([§15.6.2](classes.md#1562-method-parameters)), except that at least one parameter shall be specified, and that the `this`, `ref`, and `out` parameter modifiers are not permitted. The *type* of an indexer and each of the types referenced in the *formal_parameter_list* shall be at least as accessible as the indexer itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -An *indexer_body* may either consist of an accessor body ([§14.7.1](classes.md#1471-general)) or an expression body ([§14.6.1](classes.md#1461-general)). In an accessor body, *accessor_declarations*, which shall be enclosed in “`{`” and “`}`” tokens, declare the accessors ([§14.7.3](classes.md#1473-accessors)) of the indexer. The accessors specify the executable statements associated with reading and writing indexer elements. +An *indexer_body* may either consist of a statement body ([§15.7.1](classes.md#1571-general)) or an expression body ([§15.6.1](classes.md#1561-general)). In a statement body, *accessor_declarations*, which shall be enclosed in “`{`” and “`}`” tokens, declare the accessors ([§15.7.3](classes.md#1573-accessors)) of the indexer. The accessors specify the executable statements associated with reading and writing indexer elements. -Based on the presence or absence of get and set accessors, an indexer is classified as follows: +In a *indexer_body* an expression body consisting of “`=>`” followed by an expression `E` and a semicolon is exactly equivalent to the statement body `{ get { return E; } }`, and can therefore only be used to specify read-only indexers where the result of the get accessor is given by a single expression. -- An indexer that includes both a get accessor and a set accessor is said to be a ***read-write indexer***. -- An indexer that has only a get accessor is said to be a ***read-only indexer***. It is a compile-time error for a read-only indexer to be the target of an assignment. -- An indexer that has only a set accessor is said to be a ***write-only indexer***. Except as the target of an assignment, it is a compile-time error to reference a write-only indexer in an expression. +A *ref_indexer_body* may either consist of a statement body or an expression body. In a statement body a *get_accessor_declaration* declares the get accessor ([§15.7.3](classes.md#1573-accessors)) of the property. The accessor specifies the executable statements associated with reading the property. -An expression body consisting of “`=>`” followed by an expression `E` and a semicolon is exactly equivalent to the block body `{ get { return E; } }`, and can therefore only be used to specify read-only indexers where the result of the get accessor is given by a single expression. +In a *ref_indexer_body* an expression body consisting of `=>` followed by `ref`, a *variable_reference* `V` and a semicolon is exactly equivalent to the statement body `{ get { return ref V; } }`. -Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as a `ref` or `out` argument. +> *Note*: Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as an `in`, `out`, or `ref` argument unless the indexer is ref-valued and therefore returns a reference ([§9.7](variables.md#97-reference-variables-and-returns)). *end note* The *formal_parameter_list* of an indexer defines the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of the indexer. Specifically, the signature of an indexer consists of the number and types of its formal parameters. The element type and names of the formal parameters are not part of an indexer’s signature. The signature of an indexer shall differ from the signatures of all other indexers declared in the same class. -Indexers and properties are very similar in concept, but differ in the following ways: - -- A property is identified by its name, whereas an indexer is identified by its signature. -- A property is accessed through a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) or a *member_access* ([§11.7.6](expressions.md#1176-member-access)), whereas an indexer element is accessed through an *element_access* ([§11.7.10.3](expressions.md#117103-indexer-access)). -- A property can be a static member, whereas an indexer is always an instance member. -- A `get` accessor of a property corresponds to a method with no parameters, whereas a `get` accessor of an indexer corresponds to a method with the same formal parameter list as the indexer. -- A `set` accessor of a property corresponds to a method with a single parameter named `value`, whereas a `set` accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named `value`. -- It is a compile-time error for an indexer accessor to declare a local variable or local constant with the same name as an indexer parameter. -- In an overriding property declaration, the inherited property is accessed using the syntax `base.P`, where `P` is the property name. In an overriding indexer declaration, the inherited indexer is accessed using the syntax `base[E]`, where `E` is a comma-separated list of expressions. -- There is no concept of an “automatically implemented indexer”. It is an error to have a non-abstract, non-external indexer with semicolon accessors. - -Aside from these differences, all rules defined in [§14.7.3](classes.md#1473-accessors) and [§14.7.4](classes.md#1474-automatically-implemented-properties) apply to indexer accessors as well as to property accessors. - When an indexer declaration includes an `extern` modifier, the indexer is said to be an ***external indexer***. Because an external indexer declaration provides no actual implementation, each of its *accessor_declarations* consists of a semicolon. > *Example*: The example below declares a `BitArray` class that implements an indexer for accessing the individual bits in the bit array. @@ -4126,8 +4271,8 @@ When an indexer declaration includes an `extern` modifier, the indexer is said t > > The following `CountPrimes` class uses a `BitArray` and the classical “sieve” algorithm to compute the number of primes between 2 and a given maximum: > -> +> ```csharp > class CountPrimes > { > static int Count(int max) @@ -4147,7 +4292,7 @@ When an indexer declaration includes an `extern` modifier, the indexer is said t > } > return count; > } -> +> > static void Main(string[] args) > { > int max = int.Parse(args[0]); @@ -4155,7 +4300,6 @@ When an indexer declaration includes an `extern` modifier, the indexer is said t > Console.WriteLine($"Found {count} primes between 2 and {max}"); > } > } -> > ``` > > Note that the syntax for accessing elements of the `BitArray` is precisely the same as for a `bool[]`. @@ -4204,9 +4348,26 @@ When an indexer declaration includes an `extern` modifier, the indexer is said t > > *end example* -## 14.10 Operators +### 15.9.2 Indexer and Property Differences -### 14.10.1 General +Indexers and properties are very similar in concept, but differ in the following ways: + +- A property is identified by its name, whereas an indexer is identified by its signature. +- A property is accessed through a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) or a *member_access* ([§12.8.7](expressions.md#1287-member-access)), whereas an indexer element is accessed through an *element_access* ([§12.8.11.3](expressions.md#128113-indexer-access)). +- A property can be a static member, whereas an indexer is always an instance member. +- A get accessor of a property corresponds to a method with no parameters, whereas a get accessor of an indexer corresponds to a method with the same formal parameter list as the indexer. +- A set accessor of a property corresponds to a method with a single parameter named `value`, whereas a set accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named `value`. +- It is a compile-time error for an indexer accessor to declare a local variable or local constant with the same name as an indexer parameter. +- In an overriding property declaration, the inherited property is accessed using the syntax `base.P`, where `P` is the property name. In an overriding indexer declaration, the inherited indexer is accessed using the syntax `base[E]`, where `E` is a comma-separated list of expressions. +- There is no concept of an “automatically implemented indexer”. It is an error to have a non-abstract, non-external indexer with semicolon accessors. + +Aside from these differences, all rules defined in [§15.7.3](classes.md#1573-accessors), [§15.7.5](classes.md#1575-accessibility) and [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors) apply to indexer accessors as well as to property accessors. + +*Note*: This replacing of property/properties with indexer/indexers when reading [§15.7.3](classes.md#1573-accessors), [§15.7.5](classes.md#1575-accessibility) and [§15.7.6](classes.md#1576-virtual-sealed-override-and-abstract-accessors) applies to defined terms as well. For example *read-write property* becomes *read-write-indexer*. *end note* + +## 15.10 Operators + +### 15.10.1 General An ***operator*** is a member that defines the meaning of an expression operator that can be applied to instances of the class. Operators are declared using *operator_declaration*s: @@ -4258,19 +4419,19 @@ operator_body ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -There are three categories of overloadable operators: Unary operators ([§14.10.2](classes.md#14102-unary-operators)), binary operators ([§14.10.3](classes.md#14103-binary-operators)), and conversion operators ([§14.10.4](classes.md#14104-conversion-operators)). +There are three categories of overloadable operators: Unary operators ([§15.10.2](classes.md#15102-unary-operators)), binary operators ([§15.10.3](classes.md#15103-binary-operators)), and conversion operators ([§15.10.4](classes.md#15104-conversion-operators)). -The *operator_body* is either a semicolon, a block body ([§14.6.1](classes.md#1461-general)) or an expression body ([§14.6.1](classes.md#1461-general)). A block body consists of a *block*, which specifies the statements to execute when the operator is invoked. The *block* shall conform to the rules for value-returning methods described in [§14.6.11](classes.md#14611-method-body). An expression body consists of `=>` followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked. +The *operator_body* is either a semicolon, a block body ([§15.6.1](classes.md#1561-general)) or an expression body ([§15.6.1](classes.md#1561-general)). A block body consists of a *block*, which specifies the statements to execute when the operator is invoked. The *block* shall conform to the rules for value-returning methods described in [§15.6.11](classes.md#15611-method-body). An expression body consists of `=>` followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked. For `extern` operators, the *operator_body* consists simply of a semicolon. For all other operators, the *operator_body* is either a block body or an expression body. The following rules apply to all operator declarations: - An operator declaration shall include both a `public` and a `static` modifier. -- The parameter(s) of an operator shall have no modifiers. -- The signature of an operator ([§14.10.2](classes.md#14102-unary-operators), [§14.10.3](classes.md#14103-binary-operators), [§14.10.4](classes.md#14104-conversion-operators)) shall differ from the signatures of all other operators declared in the same class. +- The parameter(s) of an operator shall have no modifiers other than `in`. +- The signature of an operator ([§15.10.2](classes.md#15102-unary-operators), [§15.10.3](classes.md#15103-binary-operators), [§15.10.4](classes.md#15104-conversion-operators)) shall differ from the signatures of all other operators declared in the same class. - All types referenced in an operator declaration shall be at least as accessible as the operator itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). - It is an error for the same modifier to appear multiple times in an operator declaration. @@ -4278,11 +4439,11 @@ Each operator category imposes additional restrictions, as described in the foll Like other members, operators declared in a base class are inherited by derived classes. Because operator declarations always require the class or struct in which the operator is declared to participate in the signature of the operator, it is not possible for an operator declared in a derived class to hide an operator declared in a base class. Thus, the `new` modifier is never required, and therefore never permitted, in an operator declaration. -Additional information on unary and binary operators can be found in [§11.4](expressions.md#114-operators). +Additional information on unary and binary operators can be found in [§12.4](expressions.md#124-operators). Additional information on conversion operators can be found in [§10.5](conversions.md#105-user-defined-conversions). -### 14.10.2 Unary operators +### 15.10.2 Unary operators The following rules apply to unary operator declarations, where `T` denotes the instance type of the class or struct that contains the operator declaration: @@ -4292,7 +4453,7 @@ The following rules apply to unary operator declarations, where `T` denotes the The signature of a unary operator consists of the operator token (`+`, `-`, `!`, `~`, `++`, `--`, `true`, or `false`) and the type of the single formal parameter. The return type is not part of a unary operator’s signature, nor is the name of the formal parameter. -The `true` and `false` unary operators require pair-wise declaration. A compile-time error occurs if a class declares one of these operators without also declaring the other. The `true` and `false` operators are described further in [§11.22](expressions.md#1122-boolean-expressions). +The `true` and `false` unary operators require pair-wise declaration. A compile-time error occurs if a class declares one of these operators without also declaring the other. The `true` and `false` operators are described further in [§12.24](expressions.md#1224-boolean-expressions). > *Example*: The following example shows an implementation and subsequent usage of operator++ for an integer vector class: > @@ -4301,8 +4462,8 @@ The `true` and `false` unary operators require pair-wise declaration. A compile- > public class IntVector > { > public IntVector(int length) {...} -> public int Length { get { ... } } // Read-only property -> public int this[int index] { get { ... } set { ... } } // Read-write indexer +> public int Length { get { ... } } // Read-only property +> public int this[int index] { get { ... } set { ... } } // Read-write indexer > > public static IntVector operator++(IntVector iv) > { @@ -4327,16 +4488,16 @@ The `true` and `false` unary operators require pair-wise declaration. A compile- > } > ``` > -> Note how the operator method returns the value produced by adding 1 to the operand, just like the postfix increment and decrement operators ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators)), and the prefix increment and decrement operators ([§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)). Unlike in C++, this method should not modify the value of its operand directly as this would violate the standard semantics of the postfix increment operator ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators)). +> Note how the operator method returns the value produced by adding 1 to the operand, just like the postfix increment and decrement operators ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators)), and the prefix increment and decrement operators ([§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)). Unlike in C++, this method should not modify the value of its operand directly as this would violate the standard semantics of the postfix increment operator ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators)). > > *end example* -### 14.10.3 Binary operators +### 15.10.3 Binary operators The following rules apply to binary operator declarations, where `T` denotes the instance type of the class or struct that contains the operator declaration: - A binary non-shift operator shall take two parameters, at least one of which shall have type `T` or `T?`, and can return any type. -- A binary `<<` or `>>` operator ([§11.10](expressions.md#1110-shift-operators)) shall take two parameters, the first of which shall have type `T` or T? and the second of which shall have type `int` or `int?`, and can return any type. +- A binary `<<` or `>>` operator ([§12.11](expressions.md#1211-shift-operators)) shall take two parameters, the first of which shall have type `T` or T? and the second of which shall have type `int` or `int?`, and can return any type. The signature of a binary operator consists of the operator token (`+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, `>>`, `==`, `!=`, `>`, `<`, `>=`, or `<=`) and the types of the two formal parameters. The return type and the names of the formal parameters are not part of a binary operator’s signature. Certain binary operators require pair-wise declaration. For every declaration of either operator of a pair, there shall be a matching declaration of the other operator of the pair. Two operator declarations match if identity conversions exist between their return types and their corresponding parameter types. The following operators require pair-wise declaration: @@ -4345,7 +4506,7 @@ Certain binary operators require pair-wise declaration. For every declaration of - operator `>` and operator `<` - operator `>=` and operator `<=` -### 14.10.4 Conversion operators +### 15.10.4 Conversion operators A conversion operator declaration introduces a ***user-defined conversion*** ([§10.5](conversions.md#105-user-defined-conversions)), which augments the pre-defined implicit and explicit conversions. @@ -4477,9 +4638,9 @@ The signature of a conversion operator consists of the source type and the targe > > *end example* -## 14.11 Instance constructors +## 15.11 Instance constructors -### 14.11.1 General +### 15.11.1 General An ***instance constructor*** is a member that implements the actions required to initialize an instance of a class. Instance constructors are declared using *constructor_declaration*s: @@ -4513,35 +4674,35 @@ constructor_body ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -A *constructor_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)), any one of the permitted kinds of declared accessibility ([§14.3.6](classes.md#1436-access-modifiers)), and an `extern` ([§14.6.8](classes.md#1468-external-methods)) modifier. A constructor declaration is not permitted to include the same modifier multiple times. +A *constructor_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)), any one of the permitted kinds of declared accessibility ([§15.3.6](classes.md#1536-access-modifiers)), and an `extern` ([§15.6.8](classes.md#1568-external-methods)) modifier. A constructor declaration is not permitted to include the same modifier multiple times. The *identifier* of a *constructor_declarator* shall name the class in which the instance constructor is declared. If any other name is specified, a compile-time error occurs. -The optional *formal_parameter_list* of an instance constructor is subject to the same rules as the *formal_parameter_list* of a method ([§14.6](classes.md#146-methods)). As the `this` modifier for parameters only applies to extension methods ([§14.6.10](classes.md#14610-extension-methods)), no parameter in a constructor’s *formal_parameter_list* shall contain the `this` modifier. The formal parameter list defines the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of an instance constructor and governs the process whereby overload resolution ([§11.6.4](expressions.md#1164-overload-resolution)) selects a particular instance constructor in an invocation. +The optional *formal_parameter_list* of an instance constructor is subject to the same rules as the *formal_parameter_list* of a method ([§15.6](classes.md#156-methods)). As the `this` modifier for parameters only applies to extension methods ([§15.6.10](classes.md#15610-extension-methods)), no parameter in a constructor’s *formal_parameter_list* shall contain the `this` modifier. The formal parameter list defines the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of an instance constructor and governs the process whereby overload resolution ([§12.6.4](expressions.md#1264-overload-resolution)) selects a particular instance constructor in an invocation. Each of the types referenced in the *formal_parameter_list* of an instance constructor shall be at least as accessible as the constructor itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). -The optional *constructor_initializer* specifies another instance constructor to invoke before executing the statements given in the *constructor_body* of this instance constructor. This is described further in [§14.11.2](classes.md#14112-constructor-initializers). +The optional *constructor_initializer* specifies another instance constructor to invoke before executing the statements given in the *constructor_body* of this instance constructor. This is described further in [§15.11.2](classes.md#15112-constructor-initializers). When a constructor declaration includes an `extern` modifier, the constructor is said to be an ***external constructor***. Because an external constructor declaration provides no actual implementation, its *constructor_body* consists of a semicolon. For all other constructors, the *constructor_body* consists of either - a *block*, which specifies the statements to initialize a new instance of the class; or - an expression body, which consists of `=>` followed by an *expression* and a semicolon, and denotes a single expression to initialize a new instance of the class. -A *constructor_body* that is a *block* or expression body corresponds exactly to the *block* of an instance method with a `void` return type ([§14.6.11](classes.md#14611-method-body)). +A *constructor_body* that is a *block* or expression body corresponds exactly to the *block* of an instance method with a `void` return type ([§15.6.11](classes.md#15611-method-body)). -Instance constructors are not inherited. Thus, a class has no instance constructors other than those actually declared in the class, with the exception that if a class contains no instance constructor declarations, a default instance constructor is automatically provided ([§14.11.5](classes.md#14115-default-constructors)). +Instance constructors are not inherited. Thus, a class has no instance constructors other than those actually declared in the class, with the exception that if a class contains no instance constructor declarations, a default instance constructor is automatically provided ([§15.11.5](classes.md#15115-default-constructors)). -Instance constructors are invoked by *object_creation_expression*s ([§11.7.15.2](expressions.md#117152-object-creation-expressions)) and through *constructor_initializer*s. +Instance constructors are invoked by *object_creation_expression*s ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) and through *constructor_initializer*s. -### 14.11.2 Constructor initializers +### 15.11.2 Constructor initializers All instance constructors (except those for class `object`) implicitly include an invocation of another instance constructor immediately before the *constructor_body*. The constructor to implicitly invoke is determined by the *constructor_initializer*: -- An instance constructor initializer of the form `base(`*argument_list*`)` (where *argument_list* is optional) causes an instance constructor from the direct base class to be invoked. That constructor is selected using *argument_list* and the overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution). The set of candidate instance constructors consists of all the accessible instance constructors of the direct base class. If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. -- An instance constructor initializer of the form `this(`*argument_list*`)` (where *argument_list* is optional) invokes another instance constructor from the same class. The constructor is selected using *argument_list* and the overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution). The set of candidate instance constructors consists of all instance constructors declared in the class itself. If the resulting set of applicable instance constructors is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. If an instance constructor declaration invokes itself through a chain of one or more constructor initializers, a compile-time error occurs. +- An instance constructor initializer of the form `base(`*argument_list*`)` (where *argument_list* is optional) causes an instance constructor from the direct base class to be invoked. That constructor is selected using *argument_list* and the overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). The set of candidate instance constructors consists of all the accessible instance constructors of the direct base class. If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. +- An instance constructor initializer of the form `this(`*argument_list*`)` (where *argument_list* is optional) invokes another instance constructor from the same class. The constructor is selected using *argument_list* and the overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). The set of candidate instance constructors consists of all instance constructors declared in the class itself. If the resulting set of applicable instance constructors is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. If an instance constructor declaration invokes itself through a chain of one or more constructor initializers, a compile-time error occurs. If an instance constructor has no constructor initializer, a constructor initializer of the form `base()` is implicitly provided. @@ -4580,11 +4741,11 @@ The scope of the parameters given by the *formal_parameter_list* of an instance An instance constructor initializer cannot access the instance being created. Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as it is a compile-time error for an argument expression to reference any instance member through a *simple_name*. -### 14.11.3 Instance variable initializers +### 15.11.3 Instance variable initializers -When an instance constructor has no constructor initializer, or it has a constructor initializer of the form `base(...)`, that constructor implicitly performs the initializations specified by the *variable_initializer*s of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order in which they appear in the class declaration ([§14.5.6](classes.md#1456-variable-initializers)). +When an instance constructor has no constructor initializer, or it has a constructor initializer of the form `base(...)`, that constructor implicitly performs the initializations specified by the *variable_initializer*s of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order in which they appear in the class declaration ([§15.5.6](classes.md#1556-variable-initializers)). -### 14.11.4 Constructor execution +### 15.11.4 Constructor execution Variable initializers are transformed into assignment statements, and these assignment statements are executed *before* the invocation of the base class instance constructor. This ordering ensures that all instance fields are initialized by their variable initializers before *any* statements that have access to that instance are executed. @@ -4706,7 +4867,7 @@ Variable initializers are transformed into assignment statements, and these assi > > *end example* -### 14.11.5 Default constructors +### 15.11.5 Default constructors If a class contains no instance constructor declarations, a default instance constructor is automatically provided. That default constructor simply invokes a constructor of the direct base class, as if it had a constructor initializer of the form `base()`. If the class is abstract then the declared accessibility for the default constructor is protected. Otherwise, the declared accessibility for the default constructor is public. @@ -4754,7 +4915,7 @@ If overload resolution is unable to determine a unique best candidate for the ba > > *end example* -## 14.12 Static constructors +## 15.12 Static constructors A ***static constructor*** is a member that implements the actions required to initialize a closed class. Static constructors are declared using *static_constructor_declaration*s: @@ -4781,18 +4942,18 @@ static_constructor_body ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -A *static_constructor_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)) and an `extern` modifier ([§14.6.8](classes.md#1468-external-methods)). +A *static_constructor_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)) and an `extern` modifier ([§15.6.8](classes.md#1568-external-methods)). The *identifier* of a *static_constructor_declaration* shall name the class in which the static constructor is declared. If any other name is specified, a compile-time error occurs. -When a static constructor declaration includes an `extern` modifier, the static constructor is said to be an ***external static constructor***. Because an external static constructor declaration provides no actual implementation, its *static_constructor_body* consists of a semicolon. For all other static constructor declarations, the *static_constructor_body* consists of either +When a static constructor declaration includes an `extern` modifier, the static constructor is said to be an ***external static constructor***. Because an external static constructor declaration provides no actual implementation, its *static_constructor_body* consists of a semicolon. For all other static constructor declarations, the *static_constructor_body* consists of either - a *block*, which specifies the statements to execute in order to initialize the class; or - an expression body, which consists of `=>` followed by an *expression* and a semicolon, and denotes a single expression to execute in order to initialize the class. -A *static_constructor_body* that is a *block* or expression body corresponds exactly to the *method_body* of a static method with a `void` return type ([§14.6.11](classes.md#14611-method-body)). +A *static_constructor_body* that is a *block* or expression body corresponds exactly to the *method_body* of a static method with a `void` return type ([§15.6.11](classes.md#15611-method-body)). Static constructors are not inherited, and cannot be called directly. @@ -4803,7 +4964,7 @@ The static constructor for a closed class executes at most once in a given appli If a class contains the `Main` method ([§7.1](basic-concepts.md#71-application-startup)) in which execution begins, the static constructor for that class executes before the `Main` method is called. -To initialize a new closed class type, first a new set of static fields ([§14.5.2](classes.md#1452-static-and-instance-fields)) for that particular closed type is created. Each of the static fields is initialized to its default value ([§14.5.5](classes.md#1455-field-initialization)). Next, the static field initializers ([§14.5.6.2](classes.md#14562-static-field-initialization)) are executed for those static fields. Finally, the static constructor is executed. +To initialize a new closed class type, first a new set of static fields ([§15.5.2](classes.md#1552-static-and-instance-fields)) for that particular closed type is created. Each of the static fields is initialized to its default value ([§15.5.5](classes.md#1555-field-initialization)). Next, the static field initializers ([§15.5.6.2](classes.md#15562-static-field-initialization)) are executed for those static fields. Finally, the static constructor is executed. > *Example*: The example > @@ -4897,7 +5058,7 @@ It is possible to construct circular dependencies that allow static fields with > > *end example* -Because the static constructor is executed exactly once for each closed constructed class type, it is a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile-time via constraints ([§14.2.5](classes.md#1425-type-parameter-constraints)). +Because the static constructor is executed exactly once for each closed constructed class type, it is a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile-time via constraints ([§15.2.5](classes.md#1525-type-parameter-constraints)). > *Example*: The following type uses a static constructor to enforce that the type argument is an enum: > @@ -4917,9 +5078,9 @@ Because the static constructor is executed exactly once for each closed construc > > *end example* -## 14.13 Finalizers +## 15.13 Finalizers -> *Note*: In an earlier version of this standard, what is now referred to as a “finalizer” was called a “destructor”. Experience has shown that the term “destructor” caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use `Dispose`. *end note* +> *Note*: In an earlier version of this specification, what is now referred to as a “finalizer” was called a “destructor”. Experience has shown that the term “destructor” caused confusion and often resulted to incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use `Dispose`. *end note* A ***finalizer*** is a member that implements the actions required to finalize an instance of a class. A finalizer is declared using a *finalizer_declaration*: @@ -4939,9 +5100,9 @@ finalizer_body ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -A *finalizer_declaration* may include a set of *attributes* ([§21](attributes.md#21-attributes)). +A *finalizer_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)). The *identifier* of a *finalizer_declarator* shall name the class in which the finalizer is declared. If any other name is specified, a compile-time error occurs. @@ -4950,7 +5111,7 @@ When a finalizer declaration includes an `extern` modifier, the finalizer is sai - a *block*, which specifies the statements to execute in order to finalize an instance of the class. - or an expression body, which consists of `=>` followed by an *expression* and a semicolon, and denotes a single expression to execute in order to finalize an instance of the class. -A *finalizer_body* that is a *block* or expression body corresponds exactly to the *method_body* of an instance method with a `void` return type ([§14.6.11](classes.md#14611-method-body)). +A *finalizer_body* that is a *block* or expression body corresponds exactly to the *method_body* of an instance method with a `void` return type ([§15.6.11](classes.md#15611-method-body)). Finalizers are not inherited. Thus, a class has no finalizers other than the one that may be declared in that class. @@ -5038,36 +5199,36 @@ The compiler behaves as if this method, and overrides of it, do not exist at all > > *end example* -For a discussion of the behavior when an exception is thrown from a finalizer, see [§20.4](exceptions.md#204-how-exceptions-are-handled). +For a discussion of the behavior when an exception is thrown from a finalizer, see [§21.4](exceptions.md#214-how-exceptions-are-handled). -## 14.14 Iterators +## 15.14 Iterators -### 14.14.1 General +### 15.14.1 General -A function member ([§11.6](expressions.md#116-function-members)) implemented using an iterator block ([§12.3](statements.md#123-blocks)) is called an ***iterator***. +A function member ([§12.6](expressions.md#126-function-members)) implemented using an iterator block ([§13.3](statements.md#133-blocks)) is called an ***iterator***. -An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces ([§14.14.2](classes.md#14142-enumerator-interfaces)) or one of the enumerable interfaces ([§14.14.3](classes.md#14143-enumerable-interfaces)). It may occur as a *method_body*, *operator_body* or *accessor_body*, whereas events, instance constructors, static constructors and finalizers may not be implemented as iterators. +An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces ([§15.14.2](classes.md#15142-enumerator-interfaces)) or one of the enumerable interfaces ([§15.14.3](classes.md#15143-enumerable-interfaces)). It may occur as a *method_body*, *operator_body* or *accessor_body*, whereas events, instance constructors, static constructors and finalizers may not be implemented as iterators. -When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any `ref` or `out` parameters. +When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any `in`, `out`, or `ref` parameters, or an parameter of a `ref struct` type. -### 14.14.2 Enumerator interfaces +### 15.14.2 Enumerator interfaces The ***enumerator interfaces*** are the non-generic interface `System.Collections.IEnumerator` and all instantiations of the generic interface `System.Collections.Generic.IEnumerator`. For the sake of brevity, in this subclause and its siblings these interfaces are referenced as `IEnumerator` and `IEnumerator`, respectively. -### 14.14.3 Enumerable interfaces +### 15.14.3 Enumerable interfaces The ***enumerable interfaces*** are the non-generic interface `System.Collections.IEnumerable` and all instantiations of the generic interface `System.Collections.Generic.IEnumerable`. For the sake of brevity, in this subclause and its siblings these interfaces are referenced as `IEnumerable` and `IEnumerable`, respectively. -### 14.14.4 Yield type +### 15.14.4 Yield type An iterator produces a sequence of values, all of the same type. This type is called the ***yield type*** of the iterator. - The yield type of an iterator that returns `IEnumerator` or `IEnumerable` is `object`. - The yield type of an iterator that returns `IEnumerator` or `IEnumerable` is `T`. -### 14.14.5 Enumerator objects +### 15.14.5 Enumerator objects -#### 14.14.5.1 General +#### 15.14.5.1 General When a function member returning an enumerator interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an ***enumerator object*** is created and returned. This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s `MoveNext` method is invoked. An enumerator object has the following characteristics: @@ -5084,7 +5245,7 @@ The following subclauses describe the required behavior of the `MoveNext`, `Curr Enumerator objects do not support the `IEnumerator.Reset` method. Invoking this method causes a `System.NotSupportedException` to be thrown. -#### 14.14.5.2 The MoveNext method +#### 15.14.5.2 The MoveNext method The `MoveNext` method of an enumerator object encapsulates the code of an iterator block. Invoking the `MoveNext` method executes code in the iterator block and sets the `Current` property of the enumerator object as appropriate. The precise action performed by `MoveNext` depends on the state of the enumerator object when `MoveNext` is invoked: @@ -5119,7 +5280,7 @@ When `MoveNext` executes the iterator block, execution can be interrupted in fou - The state of the enumerator object is changed to **after**. - The exception propagation continues to the caller of the `MoveNext` method. -#### 14.14.5.3 The Current property +#### 15.14.5.3 The Current property An enumerator object’s `Current` property is affected by `yield return` statements in the iterator block. @@ -5127,7 +5288,7 @@ When an enumerator object is in the **suspended** state, the value of `Current` For an iterator with a yield type other than `object`, the result of accessing `Current` through the enumerator object’s `IEnumerable` implementation corresponds to accessing `Current` through the enumerator object’s `IEnumerator` implementation and casting the result to `object`. -#### 14.14.5.4 The Dispose method +#### 15.14.5.4 The Dispose method The `Dispose` method is used to clean up the iteration by bringing the enumerator object to the **after** state. @@ -5139,9 +5300,9 @@ The `Dispose` method is used to clean up the iteration by bringing the enumerato - Changes the state to **after**. - If the state of the enumerator object is **after**, invoking `Dispose` has no affect. -### 14.14.6 Enumerable objects +### 15.14.6 Enumerable objects -#### 14.14.6.1 General +#### 15.14.6.1 General When a function member returning an enumerable interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an ***enumerable object*** is created and returned. The enumerable object’s `GetEnumerator` method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s `MoveNext` method is invoked. An enumerable object has the following characteristics: @@ -5154,25 +5315,92 @@ An enumerable object may implement more interfaces than those specified above. > *Note*: For example, an enumerable object may also implement `IEnumerator` and `IEnumerator`, enabling it to serve as both an enumerable and an enumerator. Typically, such an implementation would return its own instance (to save allocations) from the first call to `GetEnumerator`. Subsequent invocations of `GetEnumerator`, if any, would return a new class instance, typically of the same class, so that calls to different enumerator instances will not affect each other. It cannot return the same instance even if the previous enumerator has already enumerated past the end of the sequence, since all future calls to an exhausted enumerator must throw exceptions. *end note* -#### 14.14.6.2 The GetEnumerator method +#### 15.14.6.2 The GetEnumerator method -An enumerable object provides an implementation of the `GetEnumerator` methods of the `IEnumerable` and `IEnumerable` interfaces. The two `GetEnumerator` methods share a common implementation that acquires and returns an available enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in [§14.14.5](classes.md#14145-enumerator-objects). +An enumerable object provides an implementation of the `GetEnumerator` methods of the `IEnumerable` and `IEnumerable` interfaces. The two `GetEnumerator` methods share a common implementation that acquires and returns an available enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in [§15.14.5](classes.md#15145-enumerator-objects). -## 14.15 Async Functions +## 15.15 Async Functions -### 14.15.1 General +### 15.15.1 General -A method ([§14.6](classes.md#146-methods)) or anonymous function ([§11.17](expressions.md#1117-anonymous-function-expressions)) with the `async` modifier is called an ***async function***. In general, the term ***async*** is used to describe any kind of function that has the `async` modifier. +A method ([§15.6](classes.md#156-methods)) or anonymous function ([§12.19](expressions.md#1219-anonymous-function-expressions)) with the `async` modifier is called an ***async function***. In general, the term ***async*** is used to describe any kind of function that has the `async` modifier. -It is a compile-time error for the formal parameter list of an async function to specify any `ref` or `out` parameters. +It is a compile-time error for the formal parameter list of an async function to specify any `in`, `out`, or `ref` parameters, or any parameter of a `ref struct` type. -The *return_type* of an async method shall be either `void` or a ***task type***. The task types are `System.Threading.Tasks.Task` and types constructed from `System.Threading.Tasks.Task`. For the sake of brevity, in this clause these types are referenced as `Task` and `Task`, respectively. An async method returning a task type is said to be ***task-returning***. +The *return_type* of an async method shall be either `void` or a ***task type***. For an async method that returns a value, a task type shall be generic. For an async method that does not return a value, a task type shall not be generic. Such types are referred to in this specification as `«TaskType»` and `«TaskType»`, respectively. The Standard library type `System.Threading.Tasks.Task` and types constructed from `System.Threading.Tasks.Task` are task types, as well as a class, struct or interface type that is associated with a ***task builder type*** via the attribute `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute`. Such types are referred to in this specification as `«TaskBuilderType»` and `«TaskBuilderType»`. A task type can have at most one type parameter and cannot be nested in a generic type. -The exact definition of the task types is implementation-defined, but from the language’s point of view, a task type is in one of the states *incomplete*, *succeeded* or *faulted*. A *faulted* task records a pertinent exception. A *succeeded* `Task` records a result of type `T`. Task types are awaitable, and tasks can therefore be the operands of await expressions ([§11.8.8](expressions.md#1188-await-expressions)). +An async method returning a task type is said to be ***task-returning***. -An async function has the ability to suspend evaluation by means of await expressions ([§11.8.8](expressions.md#1188-await-expressions)) in its body. Evaluation may later be resumed at the point of the suspending await expression by means of a ***resumption delegate***. The resumption delegate is of type `System.Action`, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. The ***current caller*** of an async function invocation is the original caller if the function invocation has never been suspended or the most recent caller of the resumption delegate otherwise. +Task types can vary in their exact definition, but from the language’s point of view, a task type is in one of the states *incomplete*, *succeeded* or *faulted*. A *faulted* task records a pertinent exception. A *succeeded* `«TaskType»` records a result of type `T`. Task types are awaitable, and tasks can therefore be the operands of await expressions ([§12.9.8](expressions.md#1298-await-expressions)). -### 14.15.2 Evaluation of a task-returning async function +> *Example*: The task type `MyTask` is associated with the task builder type `MyTaskMethodBuilder` and the awaiter type `Awaiter`: +> +> +> ```csharp +> using System.Runtime.CompilerServices; +> [AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))] +> class MyTask +> { +> public Awaiter GetAwaiter() { ... } +> } +> +> class Awaiter : INotifyCompletion +> { +> public void OnCompleted(Action completion) { ... } +> public bool IsCompleted { get; } +> public T GetResult() { ... } +> } +> ``` +> +> *end example* + +A task builder type is a class or struct type that corresponds to a specific task type ([§15.15.2](classes.md#15152-task-type-builder-pattern)). + +An async function has the ability to suspend evaluation by means of await expressions ([§12.9.8](expressions.md#1298-await-expressions)) in its body. Evaluation may later be resumed at the point of the suspending await expression by means of a ***resumption delegate***. The resumption delegate is of type `System.Action`, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. The ***current caller*** of an async function invocation is the original caller if the function invocation has never been suspended or the most recent caller of the resumption delegate otherwise. + +### 15.15.2 Task-type builder pattern + +A task builder type can have at most one type parameter and cannot be nested in a generic type. A task builder type shall have the following accessible members (for non-generic task builder types, `SetResult` has no parameters): + +```csharp +class «TaskBuilderType» +{ + public static «TaskBuilderType» Create(); + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine; + public void SetStateMachine(IAsyncStateMachine stateMachine); + public void SetException(Exception exception); + public void SetResult(T result); + public void AwaitOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine; + public void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine; + public «TaskType» Task { get; } +} +``` + +The compiler generates code that uses the «TaskBuilderType» to implement the semantics of suspending and resuming the evaluation of the async function. The compiler uses the «TaskBuilderType» as follows: + +- `«TaskBuilderType».Create()` is invoked to create an instance of the «TaskBuilderType», named `builder` in this list. +- `builder.Start(ref stateMachine)` is invoked to associate the builder with a compiler-generated state machine instance, `stateMachine`. + - The builder must call `stateMachine.MoveNext()` either in `Start()` or after `Start()` has returned to advance the state machine. +- After `Start()` returns, the `async` method invokes `builder.Task` for the task to return from the async method. +- Each call to `stateMachine.MoveNext()` will advance the state machine. +- If the state machine completes successfully, `builder.SetResult()` is called, with the method return value, if any. +- Otherwise, if an exception, `e` is thrown in the state machine, `builder.SetException(e)` is called. +- If the state machine reaches an `await expr` expression, `expr.GetAwaiter()` is invoked. +- If the awaiter implements `ICriticalNotifyCompletion` and `IsCompleted` is false, the state machine invokes `builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine)`. + - `AwaitUnsafeOnCompleted()` should call `awaiter.UnsafeOnCompleted(action)` with an `Action` that calls `stateMachine.MoveNext()` when the awaiter completes. +- Otherwise, the state machine invokes `builder.AwaitOnCompleted(ref awaiter, ref stateMachine)`. + - `AwaitOnCompleted()` should call `awaiter.OnCompleted(action)` with an `Action` that calls `stateMachine.MoveNext()` when the awaiter completes. +- `SetStateMachine(IAsyncStateMachine)` may be called by the compiler-generated `IAsyncStateMachine` implementation to identify the instance of the builder associated with a state machine instance, particularly for cases where the state machine is implemented as a value type. + - If the builder calls `stateMachine.SetStateMachine(stateMachine)`, the `stateMachine` will call `builder.SetStateMachine(stateMachine)` on the *builder instance associated with* `stateMachine`. + +### 15.15.3 Evaluation of a task-returning async function Invocation of a task-returning async function causes an instance of the returned task type to be generated. This is called the ***return task*** of the async function. The task is initially in an *incomplete* state. @@ -5181,9 +5409,9 @@ The async function body is then evaluated until it is either suspended (by reach When the body of the async function terminates, the return task is moved out of the incomplete state: - If the function body terminates as the result of reaching a return statement or the end of the body, any result value is recorded in the return task, which is put into a *succeeded* state. -- If the function body terminates as the result of an uncaught exception ([§12.10.6](statements.md#12106-the-throw-statement)) the exception is recorded in the return task which is put into a *faulted* state. +- If the function body terminates as the result of an uncaught exception ([§13.10.6](statements.md#13106-the-throw-statement)) the exception is recorded in the return task which is put into a *faulted* state. -### 14.15.3 Evaluation of a void-returning async function +### 15.15.4 Evaluation of a void-returning async function If the return type of the async function is `void`, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current thread’s ***synchronization context***. The exact definition of synchronization context is implementation-dependent, but is a representation of “where” the current thread is running. The synchronization context is notified when evaluation of a `void`-returning async function commences, completes successfully, or causes an uncaught exception to be thrown. diff --git a/standard/clauses.json b/standard/clauses.json index 77b3aede2..da2b93ba6 100644 --- a/standard/clauses.json +++ b/standard/clauses.json @@ -14,6 +14,7 @@ "types.md", "variables.md", "conversions.md", + "patterns.md", "expressions.md", "statements.md", "namespaces.md", diff --git a/standard/conversions.md b/standard/conversions.md index bdefde193..0ed9932f7 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -60,11 +60,12 @@ The following conversions are classified as implicit conversions: - Method group conversions - Null literal conversions - Implicit nullable conversions +- Implicit tuple conversions - Lifted user-defined implicit conversions - Default literal conversions - Implicit throw conversion -Implicit conversions can occur in a variety of situations, including function member invocations ([§11.6.6](expressions.md#1166-function-member-invocation)), cast expressions ([§11.8.7](expressions.md#1187-cast-expressions)), and assignments ([§11.19](expressions.md#1119-assignment-operators)). +Implicit conversions can occur in a variety of situations, including function member invocations ([§12.6.6](expressions.md#1266-function-member-invocation)), cast expressions ([§12.9.7](expressions.md#1297-cast-expressions)), and assignments ([§12.21](expressions.md#1221-assignment-operators)). The pre-defined implicit conversions always succeed and never cause exceptions to be thrown. @@ -72,15 +73,19 @@ The pre-defined implicit conversions always succeed and never cause exceptions t For the purposes of conversion, the types `object` and `dynamic` are considered equivalent. -However, dynamic conversions ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions) and [§10.3.7](conversions.md#1037-explicit-dynamic-conversions)) apply only to expressions of type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)). +However, dynamic conversions ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions) and [§10.3.8](conversions.md#1038-explicit-dynamic-conversions)) apply only to expressions of type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)). ### 10.2.2 Identity conversion An identity conversion converts from any type to the same type. One reason this conversion exists is so that a type T or an expression of type T can be said to be convertible to T itself. -Because `object` and `dynamic` are considered equivalent there is an identity conversion between `object` and `dynamic`, and between constructed types that are the same when replacing all occurrences of `dynamic` with `object`. +In some cases there is an identity conversion between types that are not exactly the same, but are considered equivalent. Such identity conversions exist: -In most cases, an identity conversion has no effect at runtime. However, since floating point operations may be performed at higher precision than prescribed by their type ([§8.3.7](types.md#837-floating-point-types)), assignment of their results may result in a loss of precision, and explicit casts are guaranteed to reduce precision to what is prescribed by the type ([§11.8.7](expressions.md#1187-cast-expressions)). +- between `object` and `dynamic`. +- between tuple types with the same arity, when an identity conversion exists between each pair of corresponding element types. +- between types constructed from the same generic type where there exists an identity conversion between each corresponding type argument. + +In most cases, an identity conversion has no effect at runtime. However, since floating point operations may be performed at higher precision than prescribed by their type ([§8.3.7](types.md#837-floating-point-types)), assignment of their results may result in a loss of precision, and explicit casts are guaranteed to reduce precision to what is prescribed by the type ([§12.9.7](expressions.md#1297-cast-expressions)). ### 10.2.3 Implicit numeric conversions @@ -103,12 +108,12 @@ There are no predefined implicit conversions to the `char` type, so values of th ### 10.2.4 Implicit enumeration conversions -An implicit enumeration conversion permits a *constant_expression* ([§11.21](expressions.md#1121-constant-expressions)) with any integer type and the value zero to be converted to any *enum_type* and to any *nullable_value_type* whose underlying type is an *enum_type*. In the latter case the conversion is evaluated by converting to the underlying *enum_type* and wrapping the result ([§8.3.11](types.md#8311-nullable-value-types)). +An implicit enumeration conversion permits a *constant_expression* ([§12.23](expressions.md#1223-constant-expressions)) with any integer type and the value zero to be converted to any *enum_type* and to any *nullable_value_type* whose underlying type is an *enum_type*. In the latter case the conversion is evaluated by converting to the underlying *enum_type* and wrapping the result ([§8.3.12](types.md#8312-nullable-value-types)). ### 10.2.5 Implicit interpolated string conversions -An implicit interpolated string conversion permits an *interpolated_string_expression* ([§11.7.3](expressions.md#1173-interpolated-string-expressions)) to be converted to `System.IFormattable` or `System.FormattableString` (which implements `System.IFormattable`). -When this conversion is applied, a string value is not composed from the interpolated string. Instead an instance of `System.FormattableString` is created, as further described in [§11.7.3](expressions.md#1173-interpolated-string-expressions). +An implicit interpolated string conversion permits an *interpolated_string_expression* ([§12.8.3](expressions.md#1283-interpolated-string-expressions)) to be converted to `System.IFormattable` or `System.FormattableString` (which implements `System.IFormattable`). +When this conversion is applied, a string value is not composed from the interpolated string. Instead an instance of `System.FormattableString` is created, as further described in [§12.8.3](expressions.md#1283-interpolated-string-expressions). ### 10.2.6 Implicit nullable conversions @@ -116,7 +121,7 @@ The implicit nullable conversions are those nullable conversions ([§10.6.1](con ### 10.2.7 Null literal conversions -An implicit conversion exists from the `null` literal to any reference type or nullable value type. This conversion produces a null reference if the target type is a reference type, or the null value ([§8.3.11](types.md#8311-nullable-value-types)) of the given nullable value type. +An implicit conversion exists from the `null` literal to any reference type or nullable value type. This conversion produces a null reference if the target type is a reference type, or the null value ([§8.3.12](types.md#8312-nullable-value-types)) of the given nullable value type. ### 10.2.8 Implicit reference conversions @@ -134,7 +139,7 @@ The implicit reference conversions are: - From any *delegate_type* to `System.Delegate` and the interfaces it implements. - From the null literal ([§6.4.5.7](lexical-structure.md#6457-the-null-literal)) to any reference-type. - From any *reference_type* to a *reference_type* `T` if it has an implicit identity or reference conversion to a *reference_type* `T₀` and `T₀` has an identity conversion to `T`. -- From any *reference_type* to an interface or delegate type `T` if it has an implicit identity or reference conversion to an interface or delegate type `T₀` and `T₀` is variance-convertible ([§17.2.3.3](interfaces.md#17233-variance-conversion)) to `T`. +- From any *reference_type* to an interface or delegate type `T` if it has an implicit identity or reference conversion to an interface or delegate type `T₀` and `T₀` is variance-convertible ([§18.2.3.3](interfaces.md#18233-variance-conversion)) to `T`. - Implicit conversions involving type parameters that are known to be reference types. See [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters) for more details on implicit conversions involving type parameters. The implicit reference conversions are those conversions between *reference_type*s that can be proven to always succeed, and therefore require no checks at run-time. @@ -152,7 +157,7 @@ A boxing conversion permits a *value_type* to be implicitly converted to a *refe - From any *enum_type* to the type `System.Enum`. - From any *non_nullable_value_type* to any *interface_type* implemented by the *non_nullable_value_type*. - From any *non_nullable_value_type* to any *interface_type* `I` such that there is a boxing conversion from the *non_nullable_value_type* to another *interface_type* `I₀`, and `I₀` has an identity conversion to `I`. -- From any *non_nullable_value_type* to any *interface_type* `I` such that there is a boxing conversion from the *non_nullable_value_type* to another *interface_type* `I₀`, and `I₀` is variance-convertible ([§17.2.3.3](interfaces.md#17233-variance-conversion)) to `I`. +- From any *non_nullable_value_type* to any *interface_type* `I` such that there is a boxing conversion from the *non_nullable_value_type* to another *interface_type* `I₀`, and `I₀` is variance-convertible ([§18.2.3.3](interfaces.md#18233-variance-conversion)) to `I`. - From any *nullable_value_type* to any *reference_type* where there is a boxing conversion from the underlying type of the *nullable_value_type* to the *reference_type.* - From a type parameter that is not known to be a reference type to any type such that the conversion is permitted by [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters). @@ -259,7 +264,7 @@ Boxing a value of a *nullable_value_type* produces a null reference if it is the ### 10.2.10 Implicit dynamic conversions -An implicit dynamic conversion exists from an expression of type dynamic to any type `T`. The conversion is dynamically bound [§11.3.3](expressions.md#1133-dynamic-binding), which means that an implicit conversion will be sought at run-time from the run-time type of the expression to `T`. If no conversion is found, a run-time exception is thrown. +An implicit dynamic conversion exists from an expression of type dynamic to any type `T`. The conversion is dynamically bound [§12.3.3](expressions.md#1233-dynamic-binding), which means that an implicit conversion will be sought at run-time from the run-time type of the expression to `T`. If no conversion is found, a run-time exception is thrown. This implicit conversion seemingly violates the advice in the beginning of [§10.2](conversions.md#102-implicit-conversions) that an implicit conversion should never cause an exception. However, it is not the conversion itself, but the *finding* of the conversion that causes the exception. The risk of run-time exceptions is inherent in the use of dynamic binding. If dynamic binding of the conversion is not desired, the expression can be first converted to `object`, and then to the desired type. @@ -282,47 +287,68 @@ This implicit conversion seemingly violates the advice in the beginning of [§10 An implicit constant expression conversion permits the following conversions: -- A *constant_expression* ([§11.21](expressions.md#1121-constant-expressions)) of type `int` can be converted to type `sbyte`, `byte`, `short`, `ushort`, `uint`, or `ulong`, provided the value of the *constant_expression* is within the range of the destination type. +- A *constant_expression* ([§12.23](expressions.md#1223-constant-expressions)) of type `int` can be converted to type `sbyte`, `byte`, `short`, `ushort`, `uint`, or `ulong`, provided the value of the *constant_expression* is within the range of the destination type. - A *constant_expression* of type `long` can be converted to type `ulong`, provided the value of the *constant_expression* is not negative. ### 10.2.12 Implicit conversions involving type parameters -For a *type_parameter* `T` that is known to be a reference type ([§14.2.5](classes.md#1425-type-parameter-constraints)), the following implicit reference conversions ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exist: +For a *type_parameter* `T` that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following implicit reference conversions ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exist: - From `T` to its effective base class `C`, from `T` to any base class of `C`, and from `T` to any interface implemented by `C`. - From `T` to an *interface_type* `I` in `T`’s effective interface set and from `T` to any base interface of `I`. -- From `T` to a type parameter `U` provided that `T` depends on `U` ([§14.2.5](classes.md#1425-type-parameter-constraints)). +- From `T` to a type parameter `U` provided that `T` depends on `U` ([§15.2.5](classes.md#1525-type-parameter-constraints)). > *Note*: Since `T` is known to be a reference type, within the scope of `T`, the run-time type of `U` will always be a reference type, even if `U` is not known to be a reference type at compile-time. *end note* - From the null literal ([§6.4.5.7](lexical-structure.md#6457-the-null-literal)) to T. -For a *type_parameter* `T` that is *not* known to be a reference type [§14.2.5](classes.md#1425-type-parameter-constraints), the following conversions involving `T` are considered to be boxing conversions ([§10.2.9](conversions.md#1029-boxing-conversions)) at compile-time. At run-time, if `T` is a value type, the conversion is executed as a boxing conversion. At run-time, if `T` is a reference type, the conversion is executed as an implicit reference conversion or identity conversion. +For a *type_parameter* `T` that is *not* known to be a reference type [§15.2.5](classes.md#1525-type-parameter-constraints), the following conversions involving `T` are considered to be boxing conversions ([§10.2.9](conversions.md#1029-boxing-conversions)) at compile-time. At run-time, if `T` is a value type, the conversion is executed as a boxing conversion. At run-time, if `T` is a reference type, the conversion is executed as an implicit reference conversion or identity conversion. - From `T` to its effective base class `C`, from `T` to any base class of `C`, and from `T` to any interface implemented by `C`. > *Note*: `C` will be one of the types `System.Object`, `System.ValueType`, or `System.Enum` (otherwise `T` would be known to be a reference type). *end note* - From `T` to an *interface_type* `I` in `T`’s effective interface set and from `T` to any base interface of `I`. -For a *type_parameter* `T` that is *not* known to be a reference type, there is an implicit conversion from `T` to a type parameter `U` provided `T` depends on `U`. At run-time, if `T` is a value type and `U` is a reference type, the conversion is executed as a boxing conversion. At run-time, if both `T` and `U` are value types, then `T` and `U` are necessarily the same type and no conversion is performed. At run-time, if `T` is a reference type, then `U` is necessarily also a reference type and the conversion is executed as an implicit reference conversion or identity conversion ([§14.2.5](classes.md#1425-type-parameter-constraints)). +For a *type_parameter* `T` that is *not* known to be a reference type, there is an implicit conversion from `T` to a type parameter `U` provided `T` depends on `U`. At run-time, if `T` is a value type and `U` is a reference type, the conversion is executed as a boxing conversion. At run-time, if both `T` and `U` are value types, then `T` and `U` are necessarily the same type and no conversion is performed. At run-time, if `T` is a reference type, then `U` is necessarily also a reference type and the conversion is executed as an implicit reference conversion or identity conversion ([§15.2.5](classes.md#1525-type-parameter-constraints)). The following further implicit conversions exist for a given type parameter `T`: - From `T` to a reference type `S` if it has an implicit conversion to a reference type `S₀` and `S₀` has an identity conversion to `S`. At run-time, the conversion is executed the same way as the conversion to `S₀`. -- From `T` to an interface type `I` if it has an implicit conversion to an interface type `I₀`, and `I₀` is variance-convertible to `I` ([§17.2.3.3](interfaces.md#17233-variance-conversion)). At run-time, if `T` is a value type, the conversion is executed as a boxing conversion. Otherwise, the conversion is executed as an implicit reference conversion or identity conversion. +- From `T` to an interface type `I` if it has an implicit conversion to an interface type `I₀`, and `I₀` is variance-convertible to `I` ([§18.2.3.3](interfaces.md#18233-variance-conversion)). At run-time, if `T` is a value type, the conversion is executed as a boxing conversion. Otherwise, the conversion is executed as an implicit reference conversion or identity conversion. In all cases, the rules ensure that a conversion is executed as a boxing conversion if and only if at run-time the conversion is from a value type to a reference type. -### 10.2.13 User-defined implicit conversions +### 10.2.13 Implicit tuple conversions + +An implicit conversion exists from a tuple expression `E` to a tuple type `T` if `E` has the same arity as `T` and an implicit conversion exists from each element in `E` to the corresponding element type in `T`. The conversion is performed by creating an instance of `T`’s corresponding `System.ValueTuple<...>` type, and initializing each of its fields in order from left to right by evaluating the corresponding tuple element expression of `E`, converting it to the corresponding element type of `T` using the implicit conversion found, and initializing the field with the result. + +If an element name in the tuple expression does not match a corresponding element name in the tuple type, a warning shall be issued. + +> *Example*: +> +> +> ```csharp +> (int, string) t1 = (1, "One"); +> (byte, string) t2 = (2, null); +> (int, string) t3 = (null, null); // Error: No conversion +> (int i, string s) t4 = (i: 4, "Four"); +> (int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored +> ``` +> +> The declarations of `t1`, `t2`, `t4` and `t5` are all valid, since implicit conversions exist from the element expressions to the corresponding element types. The declaration of `t3` is invalid, because there is no conversion from `null` to `int`. The declaration of `t5` causes a warning because the element names in the tuple expression differs from those in the tuple type. +> +> *end example* + +### 10.2.14 User-defined implicit conversions A user-defined implicit conversion consists of an optional standard implicit conversion, followed by execution of a user-defined implicit conversion operator, followed by another optional standard implicit conversion. The exact rules for evaluating user-defined implicit conversions are described in [§10.5.4](conversions.md#1054-user-defined-implicit-conversions). -### 10.2.14 Anonymous function conversions and method group conversions +### 10.2.15 Anonymous function conversions and method group conversions Anonymous functions and method groups do not have types in and of themselves, but they may be implicitly converted to delegate types. Additionally, some lambda expressions may be implicitly converted to expression tree types. Anonymous function conversions are described in more detail in [§10.7](conversions.md#107-anonymous-function-conversions) and method group conversions in [§10.8](conversions.md#108-method-group-conversions). -### 10.2.15 Default literal conversions +### 10.2.16 Default literal conversions -An implicit conversion exists from a *default_literal* ([§11.7.19](expressions.md#11719-default-value-expressions)) to any type. This conversion produces the default value ([§9.3](variables.md#93-default-values)) of the inferred type. +An implicit conversion exists from a *default_literal* ([§12.8.20](expressions.md#12820-default-value-expressions)) to any type. This conversion produces the default value ([§9.3](variables.md#93-default-values)) of the inferred type. -### 10.2.16 Implicit throw conversions +### 10.2.17 Implicit throw conversions While throw expressions do not have a type, they may be implicitly converted to any type. @@ -336,6 +362,7 @@ The following conversions are classified as explicit conversions: - Explicit numeric conversions - Explicit enumeration conversions - Explicit nullable conversions +- Explicit tuple conversions - Explicit reference conversions - Explicit interface conversions - Unboxing conversions @@ -343,7 +370,7 @@ The following conversions are classified as explicit conversions: - Explicit dynamic conversions - User-defined explicit conversions -Explicit conversions can occur in cast expressions ([§11.8.7](expressions.md#1187-cast-expressions)). +Explicit conversions can occur in cast expressions ([§12.9.7](expressions.md#1297-cast-expressions)). The set of explicit conversions includes all implicit conversions. @@ -368,17 +395,17 @@ The explicit numeric conversions are the conversions from a *numeric_type* to an - From `double` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, or `decimal`. - From `decimal` to `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, or `double`. -Because the explicit conversions include all implicit and explicit numeric conversions, it is always possible to convert from any *numeric_type* to any other *numeric_type* using a cast expression ([§11.8.7](expressions.md#1187-cast-expressions)). +Because the explicit conversions include all implicit and explicit numeric conversions, it is always possible to convert from any *numeric_type* to any other *numeric_type* using a cast expression ([§12.9.7](expressions.md#1297-cast-expressions)). The explicit numeric conversions possibly lose information or possibly cause exceptions to be thrown. An explicit numeric conversion is processed as follows: -- For a conversion from an integral type to another integral type, the processing depends on the overflow checking context ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)) in which the conversion takes place: +- For a conversion from an integral type to another integral type, the processing depends on the overflow checking context ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)) in which the conversion takes place: - In a `checked` context, the conversion succeeds if the value of the source operand is within the range of the destination type, but throws a `System.OverflowException` if the value of the source operand is outside the range of the destination type. - In an `unchecked` context, the conversion always succeeds, and proceeds as follows. - If the source type is larger than the destination type, then the source value is truncated by discarding its “extra” most significant bits. The result is then treated as a value of the destination type. - If the source type is the same size as the destination type, then the source value is treated as a value of the destination type - For a conversion from `decimal` to an integral type, the source value is rounded towards zero to the nearest integral value, and this integral value becomes the result of the conversion. If the resulting integral value is outside the range of the destination type, a `System.OverflowException` is thrown. -- For a conversion from `float` or `double` to an integral type, the processing depends on the overflow-checking context ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)) in which the conversion takes place: +- For a conversion from `float` or `double` to an integral type, the processing depends on the overflow-checking context ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)) in which the conversion takes place: - In a checked context, the conversion proceeds as follows: - If the value of the operand is NaN or infinite, a `System.OverflowException` is thrown. - Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion. @@ -394,7 +421,7 @@ The explicit numeric conversions possibly lose information or possibly cause exc - If the source value is NaN, the result is NaN if the decimal representation supports NaNs; otherwise a System.OverflowException is thrown. - For a conversion from `decimal` to `float` or `double`, the `decimal` value is rounded to the nearest `double` or `float` value. If the source value’s magnitude is too large to represent in the target type, or that value is infinity, the result is infinity preserving the sign of the original value. If the source value is NaN, the result is NaN. While this conversion may lose precision, it never causes an exception to be thrown. -> *Note:* The `decimal` type is not required to support infinities or NaN values but may do so; its range may be smaller than the range of `float` and `double`, but is not guaranteed to be. For `decimal` representations without infinities or NaN values, and with a range smaller than `float`, the result of a conversion from `decimal` to either `float` or `double` will never be infinity or NaN. *end note* +> *Note*: The `decimal` type is not required to support infinities or NaN values but may do so; its range may be smaller than the range of `float` and `double`, but is not guaranteed to be. For `decimal` representations without infinities or NaN values, and with a range smaller than `float`, the result of a conversion from `decimal` to either `float` or `double` will never be infinity or NaN. *end note* ### 10.3.3 Explicit enumeration conversions @@ -429,12 +456,12 @@ The explicit reference conversions are: - From `System.Collections.Generic.IList`, `System.Collections.Generic.IReadOnlyList`, and their base interfaces to a single-dimensional array type `T[]`, provided that there is an identity conversion or explicit reference conversion from `S` to T. - From `System.Delegate` and the interfaces it implements to any *delegate_type*. - From a reference type `S` to a reference type `T` if it has an explicit reference conversion from `S` to a reference type `T₀` and `T₀` and there is an identity conversion from `T₀` to `T`. -- From a reference type `S` to an interface or delegate type `T` if it there is an explicit reference conversion from `S` to an interface or delegate type `T₀` and either `T₀` is variance-convertible to `T` or `T` is variance-convertible to `T₀` [§17.2.3.3](interfaces.md#17233-variance-conversion). +- From a reference type `S` to an interface or delegate type `T` if it there is an explicit reference conversion from `S` to an interface or delegate type `T₀` and either `T₀` is variance-convertible to `T` or `T` is variance-convertible to `T₀` [§18.2.3.3](interfaces.md#18233-variance-conversion). - From `D` to `D` where `D` is a generic delegate type, `D` is not compatible with or identical to `D`, and for each type parameter `Xᵢ` of `D` the following holds: - If `Xᵢ` is invariant, then `Sᵢ` is identical to `Tᵢ`. - If `Xᵢ` is covariant, then there is an identity conversion, implicit reference conversion or explicit reference conversion from `Sᵢ` to `Tᵢ`. - If `Xᵢ` is contravariant, then `Sᵢ` and `Tᵢ` are either identical or both reference types. -- Explicit conversions involving type parameters that are known to be reference types. For more details on explicit conversions involving type parameters, see [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters). +- Explicit conversions involving type parameters that are known to be reference types. For more details on explicit conversions involving type parameters, see [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters). The explicit reference conversions are those conversions between *reference_type*s that require run-time checks to ensure they are correct. @@ -442,7 +469,11 @@ For an explicit reference conversion to succeed at run-time, the value of the so > *Note*: Reference conversions, implicit or explicit, never change the value of the reference itself ([§8.2.1](types.md#821-general)), only its type; neither does it change the type or value of the object being referenced. *end note* -### 10.3.6 Unboxing conversions +### 10.3.6 Explicit tuple conversions + +An explicit conversion exists from a tuple expression `E` to a tuple type `T` if `E` has the same arity as `T` and an implicit or explicit conversion exists from each element in `E` to the corresponding element type in `T`. The conversion is performed by creating an instance of `T`’s corresponding `System.ValueTuple<...>` type, and initializing each of its fields in order from left to right by evaluating the corresponding tuple element expression of `E`, converting it to the corresponding element type of `T` using the explicit conversion found, and initializing the field with the result. + +### 10.3.7 Unboxing conversions An unboxing conversion permits a *reference_type* to be explicitly converted to a *value_type*. The following unboxing conversions exist: @@ -451,9 +482,9 @@ An unboxing conversion permits a *reference_type* to be explicitly converted to - From the type `System.Enum` to any *enum_type*. - From any *interface_type* to any *non-nullable_value_type* that implements the *interface_type*. - From any *interface_type* `I` to any *non_nullable_value_type* where there is an unboxing conversion from an *interface_type* `I₀` to the *non_nullable_value-type* and an identity conversion from `I` to `I₀`. -- From any *interface_type* `I` to any *non_nullable_value_type* where there is an unboxing conversion from an *interface_type* `I₀` to the *non_nullable_value_type* and either either `I₀` is variance_convertible to `I` or `I` is variance-convertible to `I₀` ([§17.2.3.3](interfaces.md#17233-variance-conversion)). +- From any *interface_type* `I` to any *non_nullable_value_type* where there is an unboxing conversion from an *interface_type* `I₀` to the *non_nullable_value_type* and either either `I₀` is variance_convertible to `I` or `I` is variance-convertible to `I₀` ([§18.2.3.3](interfaces.md#18233-variance-conversion)). - From any *reference_type* to any *nullable_value_type* where there is an unboxing conversion from *reference_type* to the underlying *non_nullable_value_type* of the *nullable_value_type*. -- From a type parameter which is not known to be a value type to any type such that the conversion is permitted by [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters). +- From a type parameter which is not known to be a value type to any type such that the conversion is permitted by [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters). An unboxing operation to a *non_nullable_value_type* consists of first checking that the object instance is a boxed value of the given *non_nullable_value_type*, and then copying the value out of the instance. @@ -481,9 +512,9 @@ For an unboxing conversion to a given *non_nullable_value_type* to succeed at ru For an unboxing conversion to a given *nullable_value_type* to succeed at run-time, the value of the source operand shall be either null or a reference to a boxed value of the underlying *non_nullable_value_type* of the *nullable_value_type*. If the source operand is a reference to an incompatible object, a `System.InvalidCastException` is thrown. -### 10.3.7 Explicit dynamic conversions +### 10.3.8 Explicit dynamic conversions -An explicit dynamic conversion exists from an expression of type `dynamic` to any type `T`. The conversion is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)), which means that an explicit conversion will be sought at run-time from the run-time type of the expression to `T`. If no conversion is found, a run-time exception is thrown. +An explicit dynamic conversion exists from an expression of type `dynamic` to any type `T`. The conversion is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)), which means that an explicit conversion will be sought at run-time from the run-time type of the expression to `T`. If no conversion is found, a run-time exception is thrown. If dynamic binding of the conversion is not desired, the expression can be first converted to `object`, and then to the desired type. @@ -522,26 +553,26 @@ If dynamic binding of the conversion is not desired, the expression can be first > > *end example* -### 10.3.8 Explicit conversions involving type parameters +### 10.3.9 Explicit conversions involving type parameters -For a *type_parameter* `T` that is known to be a reference type ([§14.2.5](classes.md#1425-type-parameter-constraints)), the following explicit reference conversions ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exist: +For a *type_parameter* `T` that is known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following explicit reference conversions ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exist: - From the effective base class `C` of `T` to `T` and from any base class of `C` to `T`. - From any *interface_type* to `T`. - From `T` to any *interface_type* `I` provided there isn’t already an implicit reference conversion from `T` to `I`. -- From a *type_parameter* `U` to `T` provided that `T` depends on `U` ([§14.2.5](classes.md#1425-type-parameter-constraints)). +- From a *type_parameter* `U` to `T` provided that `T` depends on `U` ([§15.2.5](classes.md#1525-type-parameter-constraints)). > *Note*: Since `T` is known to be a reference type, within the scope of `T`, the run-time type of U will always be a reference type, even if `U` is not known to be a reference type at compile-time. *end note* -For a *type_parameter* `T` that is *not* known to be a reference type ([§14.2.5](classes.md#1425-type-parameter-constraints)), the following conversions involving `T` are considered to be unboxing conversions ([§10.3.6](conversions.md#1036-unboxing-conversions)) at compile-time. At run-time, if `T` is a value type, the conversion is executed as an unboxing conversion. At run-time, if `T` is a reference type, the conversion is executed as an explicit reference conversion or identity conversion. +For a *type_parameter* `T` that is *not* known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following conversions involving `T` are considered to be unboxing conversions ([§10.3.7](conversions.md#1037-unboxing-conversions)) at compile-time. At run-time, if `T` is a value type, the conversion is executed as an unboxing conversion. At run-time, if `T` is a reference type, the conversion is executed as an explicit reference conversion or identity conversion. - From the effective base class `C` of `T` to `T` and from any base class of `C` to `T`. > *Note*: C will be one of the types `System.Object`, `System.ValueType`, or `System.Enum` (otherwise `T` would be known to be a reference type). *end note* - From any *interface_type* to `T`. -For a *type_parameter* `T` that is *not* known to be a reference type ([§14.2.5](classes.md#1425-type-parameter-constraints)), the following explicit conversions exist: +For a *type_parameter* `T` that is *not* known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following explicit conversions exist: - From `T` to any *interface_type* `I` provided there is not already an implicit conversion from `T` to `I`. This conversion consists of an implicit boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)) from `T` to `object` followed by an explicit reference conversion from `object` to `I`. At run-time, if `T` is a value type, the conversion is executed as a boxing conversion followed by an explicit reference conversion. At run-time, if `T` is a reference type, the conversion is executed as an explicit reference conversion. -- From a type parameter `U` to `T` provided that `T` depends on `U` ([§14.2.5](classes.md#1425-type-parameter-constraints)). At run-time, if `T` is a value type and `U` is a reference type, the conversion is executed as an unboxing conversion. At run-time, if both `T` and `U` are value types, then `T` and `U` are necessarily the same type and no conversion is performed. At run-time, if `T` is a reference type, then `U` is necessarily also a reference type and the conversion is executed as an explicit reference conversion or identity conversion. +- From a type parameter `U` to `T` provided that `T` depends on `U` ([§15.2.5](classes.md#1525-type-parameter-constraints)). At run-time, if `T` is a value type and `U` is a reference type, the conversion is executed as an unboxing conversion. At run-time, if both `T` and `U` are value types, then `T` and `U` are necessarily the same type and no conversion is performed. At run-time, if `T` is a reference type, then `U` is necessarily also a reference type and the conversion is executed as an explicit reference conversion or identity conversion. In all cases, the rules ensure that a conversion is executed as an unboxing conversion if and only if at run-time the conversion is from a reference type to a value type. @@ -577,7 +608,7 @@ The above rules do not permit a direct explicit conversion from an unconstrained > > *end example* -### 10.3.9 User-defined explicit conversions +### 10.3.10 User-defined explicit conversions A user-defined explicit conversion consists of an optional standard explicit conversion, followed by execution of a user-defined implicit or explicit conversion operator, followed by another optional standard explicit conversion. The exact rules for evaluating user-defined explicit conversions are described in [§10.5.5](conversions.md#1055-user-defined-explicit-conversions). @@ -612,7 +643,7 @@ The standard explicit conversions are all standard implicit conversions plus the ### 10.5.1 General -C# allows the pre-defined implicit and explicit conversions to be augmented by user-defined conversions. User-defined conversions are introduced by declaring conversion operators ([§14.10.4](classes.md#14104-conversion-operators)) in class and struct types. +C# allows the pre-defined implicit and explicit conversions to be augmented by user-defined conversions. User-defined conversions are introduced by declaring conversion operators ([§15.10.4](classes.md#15104-conversion-operators)) in class and struct types. ### 10.5.2 Permitted user-defined conversions @@ -625,7 +656,7 @@ For a given source type `S` and target type `T`, if `S` or `T` are nullable - Neither `S₀` nor `T₀` is an *interface_type*. - Excluding user-defined conversions, a conversion does not exist from `S` to `T` or from `T` to `S`. -The restrictions that apply to user-defined conversions are specified in [§14.10.4](classes.md#14104-conversion-operators). +The restrictions that apply to user-defined conversions are specified in [§15.10.4](classes.md#15104-conversion-operators). ### 10.5.3 Evaluation of user-defined conversions @@ -735,9 +766,7 @@ Given a user-defined conversion operator that converts from a non-nullable value ### 10.7.1 General -An *anonymous_method_expression* or *lambda_expression* is classified as an anonymous function ([§11.17](expressions.md#1117-anonymous-function-expressions)). The expression does not have a type, but can be implicitly converted to a compatible delegate type. Some lambda expressions may also be implicitly converted to a compatible expression tree type. - -For the purpose of brevity, this subclause uses the short form for the task types `Task` and `Task` ([§14.15.1](classes.md#14151-general)). +An *anonymous_method_expression* or *lambda_expression* is classified as an anonymous function ([§12.19](expressions.md#1219-anonymous-function-expressions)). The expression does not have a type, but can be implicitly converted to a compatible delegate type. Some lambda expressions may also be implicitly converted to a compatible expression tree type. Specifically, an anonymous function `F` is compatible with a delegate type `D` provided: @@ -745,10 +774,10 @@ Specifically, an anonymous function `F` is compatible with a delegate type `D` - If `F` does not contain an *anonymous_function_signature*, then `D` may have zero or more parameters of any type, as long as no parameter of `D` has the out parameter modifier. - If `F` has an explicitly typed parameter list, each parameter in `D` has the same type and modifiers as the corresponding parameter in `F`. - If `F` has an implicitly typed parameter list, `D` has no ref or out parameters. -- If the body of `F` is an expression, and *either* `D` has a void return type *or* `F` is async and `D` has the return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (w.r.t [§11](expressions.md#11-expressions)) that would be permitted as a *statement_expression* ([§12.7](statements.md#127-expression-statements)). -- If the body of `F` is a block, and *either* `D` has a void return type *or* `F` is async and `D` has the return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid block (w.r.t [§12.3](statements.md#123-blocks)) in which no `return` statement specifies an expression. -- If the body of `F` is an expression, and *either* `F` is non-async and `D` has a non-`void` return type `T`, *or* `F` is async and `D` has a return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (w.r.t [§11](expressions.md#11-expressions)) that is implicitly convertible to `T`. -- If the body of `F` is a block, and *either* `F` is non-async and `D` has a non-void return type `T`, *or* `F` is async and `D` has a return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid block (w.r.t [§12.3](statements.md#123-blocks)) with a non-reachable end point in which each return statement specifies an expression that is implicitly convertible to `T`. +- If the body of `F` is an expression, and *either* `D` has a void return type *or* `F` is async and `D` has a `«TaskType»` return type ([§15.15.1](classes.md#15151-general)), then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (w.r.t [§12](expressions.md#12-expressions)) that would be permitted as a *statement_expression* ([§13.7](statements.md#137-expression-statements)). +- If the body of `F` is a block, and *either* `D` has a void return type *or* `F` is async and `D` has a `«TaskType»` return type , then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid block (w.r.t [§13.3](statements.md#133-blocks)) in which no `return` statement specifies an expression. +- If the body of `F` is an expression, and *either* `F` is non-async and `D` has a non-`void` return type `T`, *or* `F` is async and `D` has a `«TaskType»` return type ([§15.15.1](classes.md#15151-general)), then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (w.r.t [§12](expressions.md#12-expressions)) that is implicitly convertible to `T`. +- If the body of `F` is a block, and *either* `F` is non-async and `D` has a non-void return type `T`, *or* `F` is async and `D` has a `«TaskType»` return type, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid statement block (w.r.t [§13.3](statements.md#133-blocks)) with a non-reachable end point in which each return statement specifies an expression that is implicitly convertible to `T`. > *Example*: The following examples illustrate these rules: > @@ -830,11 +859,11 @@ Specifically, an anonymous function `F` is compatible with a delegate type `D` A lambda expression `F` is compatible with an expression tree type `Expression` if `F` is compatible with the delegate type `D`. This does not apply to anonymous methods, only lambda expressions. -Anonymous functions may influence overload resolution, and participate in type inference. See [§11.6](expressions.md#116-function-members) for further details. +Anonymous functions may influence overload resolution, and participate in type inference. See [§12.6](expressions.md#126-function-members) for further details. ### 10.7.2 Evaluation of anonymous function conversions to delegate types -Conversion of an anonymous function to a delegate type produces a delegate instance that references the anonymous function and the (possibly empty) set of captured outer variables that are active at the time of the evaluation. When the delegate is invoked, the body of the anonymous function is executed. The code in the body is executed using the set of captured outer variables referenced by the delegate. A *delegate_creation_expression* ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)) can be used as an alternate syntax for converting an anonymous method to a delegate type. +Conversion of an anonymous function to a delegate type produces a delegate instance that references the anonymous function and the (possibly empty) set of captured outer variables that are active at the time of the evaluation. When the delegate is invoked, the body of the anonymous function is executed. The code in the body is executed using the set of captured outer variables referenced by the delegate. A *delegate_creation_expression* ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)) can be used as an alternate syntax for converting an anonymous method to a delegate type. The invocation list of a delegate produced from an anonymous function contains a single entry. The exact target object and target method of the delegate are unspecified. In particular, it is unspecified whether the target object of the delegate is `null`, the `this` value of the enclosing function member, or some other object. @@ -885,15 +914,14 @@ Not every lambda expression can be converted to expression tree types. The conve ## 10.8 Method group conversions -An implicit conversion exists from a method group ([§11.2](expressions.md#112-expression-classifications)) to a compatible delegate type ([§19.4](delegates.md#194-delegate-compatibility)). If `D` is a delegate type, and `E` is an expression that is classified as a method group, then `D` is compatible with `E` if and only if `E` contains at least one method that is applicable in its normal form ([§11.6.4.2](expressions.md#11642-applicable-function-member)) to any argument list ([§11.6.2](expressions.md#1162-argument-lists)) having types and modifiers matching the parameter types and modifiers of `D`, as described in the following. +An implicit conversion exists from a method group ([§12.2](expressions.md#122-expression-classifications)) to a compatible delegate type ([§20.4](delegates.md#204-delegate-compatibility)). If `D` is a delegate type, and `E` is an expression that is classified as a method group, then `D` is compatible with `E` if and only if `E` contains at least one method that is applicable in its normal form ([§12.6.4.2](expressions.md#12642-applicable-function-member)) to any argument list ([§12.6.2](expressions.md#1262-argument-lists)) having types and modifiers matching the parameter types and modifiers of `D`, as described in the following. -The compile-time application of the conversion from a method group `E` to a delegate type `D` is described in the following. Note that the existence of an implicit conversion from `E` to `D` does not guarantee that the compile-time application of the conversion will succeed without error. +The compile-time application of the conversion from a method group `E` to a delegate type `D` is described in the following. -- A single method `M` is selected corresponding to a method invocation ([§11.7.8.2](expressions.md#11782-method-invocations)) of the form `E(A)`, with the following modifications: - - The argument list `A` is a list of expressions, each classified as a variable and with the type and modifier (`ref` or `out`) of the corresponding parameter in the *formal_parameter_list* of `D` — excepting parameters of type `dynamic`, where the corresponding expression has the type `object` instead of `dynamic`. - - The candidate methods considered are only those methods that are applicable in their normal form and do not omit any optional parameters ([§11.6.4.2](expressions.md#11642-applicable-function-member)). Thus, candidate methods are ignored if they are applicable only in their expanded form, or if one or more of their optional parameters do not have a corresponding parameter in `D`. -- A conversion is considered to exist if the algorithm of [§11.7.8.2](expressions.md#11782-method-invocations) produces a single best method `M` having the same number of parameters as `D`. -- Even if the conversion exists, a compile-time error occurs if the selected method `M` is not compatible ([§19.4](delegates.md#194-delegate-compatibility)) with the delegate type `D`. +- A single method `M` is selected corresponding to a method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)) of the form `E(A)`, with the following modifications: + - The argument list `A` is a list of expressions, each classified as a variable and with the type and modifier (`in`, `out`, or `ref`) of the corresponding parameter in the *formal_parameter_list* of `D` — excepting parameters of type `dynamic`, where the corresponding expression has the type `object` instead of `dynamic`. + - The candidate methods considered are only those methods that are applicable in their normal form and do not omit any optional parameters ([§12.6.4.2](expressions.md#12642-applicable-function-member)). Thus, candidate methods are ignored if they are applicable only in their expanded form, or if one or more of their optional parameters do not have a corresponding parameter in `D`. +- A conversion is considered to exist if the algorithm of [§12.8.9.2](expressions.md#12892-method-invocations) produces a single best method `M` which is compatible ([§20.4](delegates.md#204-delegate-compatibility)) with `D`. - If the selected method `M` is an instance method, the instance expression associated with `E` determines the target object of the delegate. - If the selected method `M` is an extension method which is denoted by means of a member access on an instance expression, that instance expression determines the target object of the delegate. - The result of the conversion is a value of type `D`, namely a delegate that refers to the selected method and target object. @@ -950,7 +978,7 @@ As with all other implicit and explicit conversions, the cast operator can be us > > *end example* -A method group conversion can refer to a generic method, either by explicitly specifying type arguments within `E`, or via type inference ([§11.6.3](expressions.md#1163-type-inference)). If type inference is used, the parameter types of the delegate are used as argument types in the inference process. The return type of the delegate is not used for inference. Whether the type arguments are specified or inferred, they are part of the method group conversion process; these are the type arguments used to invoke the target method when the resulting delegate is invoked. +A method group conversion can refer to a generic method, either by explicitly specifying type arguments within `E`, or via type inference ([§12.6.3](expressions.md#1263-type-inference)). If type inference is used, the parameter types of the delegate are used as argument types in the inference process. The return type of the delegate is not used for inference. Whether the type arguments are specified or inferred, they are part of the method group conversion process; these are the type arguments used to invoke the target method when the resulting delegate is invoked. > *Example*: > @@ -976,7 +1004,7 @@ A method group conversion can refer to a generic method, either by explicitly sp > > *end example* -Method groups may influence overload resolution, and participate in type inference. See [§11.6](expressions.md#116-function-members) for further details. +Method groups may influence overload resolution, and participate in type inference. See [§12.6](expressions.md#126-function-members) for further details. The run-time evaluation of a method group conversion proceeds as follows: @@ -988,4 +1016,4 @@ the target object of the delegate is determined from the instance expression ass - Otherwise, the selected method is part of a static method call, and the target object of the delegate is `null`. - A delegate instance of delegate type `D` is obtained with a reference to the method that was determined at compile-time and a reference to the target object computed above, as follows: - The conversion is permitted (but not required) to use an existing delegate instance that already contains these references. -- If an existing instance was not reused, a new one is created ([§19.5](delegates.md#195-delegate-instantiation)). If there is not enough memory available to allocate the new instance, a `System.OutOfMemoryException` is thrown. Otherwise the instance is initialized with the given references. +- If an existing instance was not reused, a new one is created ([§20.5](delegates.md#205-delegate-instantiation)). If there is not enough memory available to allocate the new instance, a `System.OutOfMemoryException` is thrown. Otherwise the instance is initialized with the given references. diff --git a/standard/delegates.md b/standard/delegates.md index 375da6bd6..201c5dcd5 100644 --- a/standard/delegates.md +++ b/standard/delegates.md @@ -1,19 +1,25 @@ -# 19 Delegates +# 20 Delegates -## 19.1 General +## 20.1 General A delegate declaration defines a class that is derived from the class `System.Delegate`. A delegate instance encapsulates an ***invocation list***, which is a list of one or more methods, each of which is referred to as a ***callable entity***. For instance methods, a callable entity consists of an instance and a method on that instance. For static methods, a callable entity consists of just a method. Invoking a delegate instance with an appropriate set of arguments causes each of the delegate’s callable entities to be invoked with the given set of arguments. -> *Note*: An interesting and useful property of a delegate instance is that it does not know or care about the classes of the methods it encapsulates; all that matters is that those methods be compatible ([§19.4](delegates.md#194-delegate-compatibility)) with the delegate’s type. This makes delegates perfectly suited for “anonymous” invocation. *end note* +> *Note*: An interesting and useful property of a delegate instance is that it does not know or care about the classes of the methods it encapsulates; all that matters is that those methods be compatible ([§20.4](delegates.md#204-delegate-compatibility)) with the delegate’s type. This makes delegates perfectly suited for “anonymous” invocation. *end note* -## 19.2 Delegate declarations +## 20.2 Delegate declarations -A *delegate_declaration* is a *type_declaration* ([§13.7](namespaces.md#137-type-declarations)) that declares a new delegate type. +A *delegate_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-type-declarations)) that declares a new delegate type. ```ANTLR delegate_declaration - : attributes? delegate_modifier* 'delegate' return_type identifier - variant_type_parameter_list? '(' formal_parameter_list? ')' + : attributes? delegate_modifier* 'delegate' return_type delegate_header + | attributes? delegate_modifier* 'delegate' ref_kind ref_return_type + delegate_header + ; + +delegate_header + : identifier '(' formal_parameter_list? ')' ';' + | identifier variant_type_parameter_list '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';' ; @@ -27,27 +33,31 @@ delegate_modifier ; ``` -*unsafe_modifier* is defined in [§22.2](unsafe-code.md#222-unsafe-contexts). +*unsafe_modifier* is defined in [§23.2](unsafe-code.md#232-unsafe-contexts). It is a compile-time error for the same modifier to appear multiple times in a delegate declaration. -A delegate declaration shall not supply any *type_parameter_constraints_clause*s unless it also supplies a *variant_type_parameter_list*. - A delegate declaration that supplies a *variant_type_parameter_list* is a generic delegate declaration. Additionally, any delegate nested inside a generic class declaration or a generic struct declaration is itself a generic delegate declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)). -The `new` modifier is only permitted on delegates declared within another type, in which case it specifies that such a delegate hides an inherited member by the same name, as described in [§14.3.5](classes.md#1435-the-new-modifier). +The `new` modifier is only permitted on delegates declared within another type, in which case it specifies that such a delegate hides an inherited member by the same name, as described in [§15.3.5](classes.md#1535-the-new-modifier). The `public`, `protected`, `internal`, and `private` modifiers control the accessibility of the delegate type. Depending on the context in which the delegate declaration occurs, some of these modifiers might not be permitted ([§7.5.2](basic-concepts.md#752-declared-accessibility)). The delegate’s type name is *identifier*. -The optional *formal_parameter_list* specifies the parameters of the delegate, and *return_type* indicates the return type of the delegate. +As with methods ([§15.6.1](classes.md#1561-general)), if `ref` is present, the delegate returns-by-ref; otherwise, if *return_type* is `void`, the delegate returns-no-value; otherwise, the delegate returns-by-value. + +The optional *formal_parameter_list* specifies the parameters of the delegate. + +The *return_type* of a returns-by-value or returns-no-value delegate declaration specifies the type of the result, if any, returned by the delegate. + +The *ref_return_type* of a returns-by-ref delegate declaration specifies the type of the variable referenced by the *variable_reference* ([§9.5](variables.md#95-variable-references)) returned by the delegate. -The optional *variant_type_parameter_list* ([§17.2.3](interfaces.md#1723-variant-type-parameter-lists)) specifies the type parameters to the delegate itself. +The optional *variant_type_parameter_list* ([§18.2.3](interfaces.md#1823-variant-type-parameter-lists)) specifies the type parameters to the delegate itself. -The return type of a delegate type shall be either `void`, or output-safe ([§17.2.3.2](interfaces.md#17232-variance-safety)). +The return type of a delegate type shall be either `void`, or output-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). -All the formal parameter types of a delegate type shall be input-safe ([§17.2.3.2](interfaces.md#17232-variance-safety)). In addition, any output or reference parameter types shall also be output-safe. +All the formal parameter types of a delegate type shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). In addition, any output or reference parameter types shall also be output-safe. > *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* @@ -69,26 +79,29 @@ Delegate types in C# are name equivalent, not structurally equivalent. Like other generic type declarations, type arguments shall be given to create a constructed delegate type. The parameter types and return type of a constructed delegate type are created by substituting, for each type parameter in the delegate declaration, the corresponding type argument of the constructed delegate type. -The only way to declare a delegate type is via a *delegate_declaration*. Every delegate type is a reference type that is derived from `System.Delegate`. The members required for every delegate type are detailed in [§19.3](delegates.md#193-delegate-members). Delegate types are implicitly `sealed`, so it is not permissible to derive any type from a delegate type. It is also not permissible to declare a non-delegate class type deriving from `System.Delegate`. `System.Delegate` is not itself a delegate type; it is a class type from which all delegate types are derived. +The only way to declare a delegate type is via a *delegate_declaration*. Every delegate type is a reference type that is derived from `System.Delegate`. The members required for every delegate type are detailed in [§20.3](delegates.md#203-delegate-members). Delegate types are implicitly `sealed`, so it is not permissible to derive any type from a delegate type. It is also not permissible to declare a non-delegate class type deriving from `System.Delegate`. `System.Delegate` is not itself a delegate type; it is a class type from which all delegate types are derived. -## 19.3 Delegate members +## 20.3 Delegate members -Every delegate type inherits members from the `Delegate` class as described in [§14.3.4](classes.md#1434-inheritance). In addition, every delegate type must provide a non-generic `Invoke` method whose parameter list matches the *formal_parameter_list* in the delegate declaration, and whose return type matches the *return_type* in the delegate declaration. The `Invoke` method shall be at least as accessible as the containing delegate type. Calling the `Invoke` method on a delegate type is semantically equivalent to using the delegate invocation syntax ([§19.6](delegates.md#196-delegate-invocation)) . +Every delegate type inherits members from the `Delegate` class as described in [§15.3.4](classes.md#1534-inheritance). In addition, every delegate type must provide a non-generic `Invoke` method whose parameter list matches the *formal_parameter_list* in the delegate declaration, whose return type matches the *return_type* or *ref_return_type* in the delegate declaration, and for returns-by-ref delegates whose *ref_kind* matches that in the delegate declaration. The `Invoke` method shall be at least as accessible as the containing delegate type. Calling the `Invoke` method on a delegate type is semantically equivalent to using the delegate invocation syntax ([§20.6](delegates.md#206-delegate-invocation)) . Implementations may define additional members in the delegate type. Except for instantiation, any operation that can be applied to a class or class instance can also be applied to a delegate class or instance, respectively. In particular, it is possible to access members of the `System.Delegate` type via the usual member access syntax. -## 19.4 Delegate compatibility +## 20.4 Delegate compatibility A method or delegate type `M` is ***compatible*** with a delegate type `D` if all of the following are true: -- `D` and `M` have the same number of parameters, and each parameter in `D` has the same `ref` or `out` modifiers as the corresponding parameter in `M`. -- For each value parameter (a parameter with no `ref` or `out` modifier), an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) or implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exists from the parameter type in `D` to the corresponding parameter type in `M`. -- For each `ref` or `out` parameter, the parameter type in `D` is the same as the parameter type in `M`. -- An identity or implicit reference conversion exists from the return type of `M` to the return type of `D`. +- `D` and `M` have the same number of parameters, and each parameter in `D` has the same `in`, `out`, or `ref` modifiers as the corresponding parameter in `M`. +- For each value parameter, an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) or implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exists from the parameter type in `D` to the corresponding parameter type in `M`. +- For each `in`, `out`, or `ref` parameter, the parameter type in `D` is the same as the parameter type in `M`. +- One of the following is true: + - `D` and `M` are both *returns-no-value* + - `D` and `M` are returns-by-value ([§15.6.1](classes.md#1561-general), [§20.2](delegates.md#202-delegate-declarations)), and an identity or implicit reference conversion exists from the return type of `M` to the return type of `D`. + - `D` and `M` are both returns-by-ref, an identity conversion exists between the return type of `M` and the return type of `D`, and both have the same *ref_kind*. -This definition of consistency allows covariance in return type and contravariance in parameter types. +This definition of compatibility allows covariance in return type and contravariance in parameter types. > *Example*: > @@ -163,13 +176,13 @@ This definition of consistency allows covariance in return type and contravarian > > *end note* -## 19.5 Delegate instantiation +## 20.5 Delegate instantiation -An instance of a delegate is created by a *delegate_creation_expression* ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)), a conversion to a delegate type, delegate combination or delegate removal. The newly created delegate instance then refers to one or more of: +An instance of a delegate is created by a *delegate_creation_expression* ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)), a conversion to a delegate type, delegate combination or delegate removal. The newly created delegate instance then refers to one or more of: - The static method referenced in the *delegate_creation_expression*, or - The target object (which cannot be `null`) and instance method referenced in the *delegate_creation_expression*, or -- Another delegate ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)). +- Another delegate ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)). > *Example*: > @@ -199,9 +212,9 @@ An instance of a delegate is created by a *delegate_creation_expression* ([§11. The set of methods encapsulated by a delegate instance is called an *invocation list*. When a delegate instance is created from a single method, it encapsulates that method, and its invocation list contains only one entry. However, when two non-`null` delegate instances are combined, their invocation lists are concatenated—in the order left operand then right operand—to form a new invocation list, which contains two or more entries. -When a new delegate is created from a single delegate the resultant invocation list has just one entry, which is the source delegate ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)). +When a new delegate is created from a single delegate the resultant invocation list has just one entry, which is the source delegate ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)). -Delegates are combined using the binary `+` ([§11.9.5](expressions.md#1195-addition-operator)) and `+=` operators ([§11.19.3](expressions.md#11193-compound-assignment)). A delegate can be removed from a combination of delegates, using the binary `-` ([§11.9.6](expressions.md#1196-subtraction-operator)) and `-=` operators ([§11.19.3](expressions.md#11193-compound-assignment)). Delegates can be compared for equality ([§11.11.9](expressions.md#11119-delegate-equality-operators)). +Delegates are combined using the binary `+` ([§12.10.5](expressions.md#12105-addition-operator)) and `+=` operators ([§12.21.4](expressions.md#12214-compound-assignment)). A delegate can be removed from a combination of delegates, using the binary `-` ([§12.10.6](expressions.md#12106-subtraction-operator)) and `-=` operators ([§12.21.4](expressions.md#12214-compound-assignment)). Delegates can be compared for equality ([§12.12.9](expressions.md#12129-delegate-equality-operators)). > *Example*: The following example shows the instantiation of a number of delegates, and their corresponding invocation lists: > @@ -241,7 +254,7 @@ Delegates are combined using the binary `+` ([§11.9.5](expressions.md#1195-add > When creating a delegate from another delegate with a *delegate_creation_expression* the result has an invocation list with a different structure from the original, but which results in the same methods being invoked in the same order. When `td3` is created from `cd3` its invocation list has just one member, but that member is a list of the methods `M1` and `M2` and those methods are invoked by `td3` in the same order as they are invoked by `cd3`. Similarly when `td4` is instantiated its invocation list has just two entries but it invokes the three methods `M1`, `M2`, and `M1`, in that order just as `cd4` does. > > The structure of the invocation list affects delegate subtraction. Delegate `cd6`, created by subtracting `cd2` (which invokes `M2`) from `cd4` (which invokes `M1`, `M2`, and `M1`) invokes `M1` and `M1`. However delegate `td6`, created by subtracting `cd2` (which invokes `M2`) from `td4` (which invokes `M1`, `M2`, and `M1`) still invokes `M1`, `M2` and `M1`, in that order, as `M2` is not a single entry in the list but a member of a nested list. -> For more examples of combining (as well as removing) delegates, see [§19.6](delegates.md#196-delegate-invocation). +> For more examples of combining (as well as removing) delegates, see [§20.6](delegates.md#206-delegate-invocation). > > *end example* @@ -249,11 +262,11 @@ Once instantiated, a delegate instance always refers to the same invocation list > *Note*: Remember, when two delegates are combined, or one is removed from another, a new delegate results with its own invocation list; the invocation lists of the delegates combined or removed remain unchanged. *end note* -## 19.6 Delegate invocation +## 20.6 Delegate invocation -C# provides special syntax for invoking a delegate. When a non-`null` delegate instance whose invocation list contains one entry, is invoked, it invokes the one method with the same arguments it was given, and returns the same value as the referred to method. (See [§11.7.8.4](expressions.md#11784-delegate-invocations) for detailed information on delegate invocation.) If an exception occurs during the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, as if that method had directly called the method to which that delegate referred. +C# provides special syntax for invoking a delegate. When a non-`null` delegate instance whose invocation list contains one entry, is invoked, it invokes the one method with the same arguments it was given, and returns the same value as the referred to method. (See [§12.8.9.4](expressions.md#12894-delegate-invocations) for detailed information on delegate invocation.) If an exception occurs during the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, as if that method had directly called the method to which that delegate referred. -Invocation of a delegate instance whose invocation list contains multiple entries, proceeds by invoking each of the methods in the invocation list, synchronously, in order. Each method so called is passed the same set of arguments as was given to the delegate instance. If such a delegate invocation includes reference parameters ([§14.6.2.3](classes.md#14623-reference-parameters)), each method invocation will occur with a reference to the same variable; changes to that variable by one method in the invocation list will be visible to methods further down the invocation list. If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list. If an exception occurs during processing of the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, and any methods further down the invocation list are not invoked. +Invocation of a delegate instance whose invocation list contains multiple entries, proceeds by invoking each of the methods in the invocation list, synchronously, in order. Each method so called is passed the same set of arguments as was given to the delegate instance. If such a delegate invocation includes reference parameters ([§15.6.2.4](classes.md#15624-reference-parameters)), each method invocation will occur with a reference to the same variable; changes to that variable by one method in the invocation list will be visible to methods further down the invocation list. If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list. If an exception occurs during processing of the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, and any methods further down the invocation list are not invoked. Attempting to invoke a delegate instance whose value is `null` results in an exception of type `System.NullReferenceException`. diff --git a/standard/documentation-comments.md b/standard/documentation-comments.md index 85439daae..774d409a8 100644 --- a/standard/documentation-comments.md +++ b/standard/documentation-comments.md @@ -12,7 +12,7 @@ This specification suggests a set of standard tags to be used in documentation c ## D.2 Introduction -Comments having a certain form can be used to direct a tool to produce XML from those comments and the source code elements that they precede. Such comments are *Single-Line_Comment*s ([§6.3.3](lexical-structure.md#633-comments)) that start with three slashes (`///`), or *Delimited_Comment*s ([§6.3.3](lexical-structure.md#633-comments)) that start with a slash and two asterisks (`/**`). They must immediately precede a user-defined type or a member that they annotate. Attribute sections ([§21.3](attributes.md#213-attribute-specification)) are considered part of declarations, so documentation comments must precede attributes applied to a type or member. +Comments having a certain form can be used to direct a tool to produce XML from those comments and the source code elements that they precede. Such comments are *Single-Line_Comment*s ([§6.3.3](lexical-structure.md#633-comments)) that start with three slashes (`///`), or *Delimited_Comment*s ([§6.3.3](lexical-structure.md#633-comments)) that start with a slash and two asterisks (`/**`). They must immediately precede a user-defined type or a member that they annotate. Attribute sections ([§22.3](attributes.md#223-attribute-specification)) are considered part of declarations, so documentation comments must precede attributes applied to a type or member. For expository purposes, the format of document comments is shown below as two grammar rules: *Single_Line_Doc_Comment* and *Delimited_Doc_Comment*. However, these rules are *not* part of the C\# grammar, but rather, they represent particular formats of *Single_Line_Comment* and *Delimited_Comment* lexer rules, respectively. @@ -553,7 +553,7 @@ public class Point ### D.3.16 \ -This tag can be used to describe a type or a member of a type. Use `` ([§D.3.12](documentation-comments.md#d312-remarks)) to describe the type itself. +This tag can be used to describe a type or a member of a type. Use `` ([§D.3.12](documentation-comments.md#d312-remarks)) to specify extra information about the type or member. **Syntax:** @@ -633,7 +633,7 @@ public class MyClass /// This method fetches data and returns a list of /// . /// - /// query to execute + /// query to execute public List FetchData(string query) { ... @@ -695,7 +695,7 @@ The documentation generator observes the following rules when it generates the I - For methods and properties with arguments, the argument list follows, enclosed in parentheses. For those without arguments, the parentheses are omitted. The arguments are separated by commas. The encoding of each argument is the same as a CLI signature, as follows: - Arguments are represented by their documentation name, which is based on their fully qualified name, modified as follows: - Arguments that represent generic types have an appended “`'`” character followed by the number of type parameters - - Arguments having the `out` or `ref` modifier have an `@` following their type name. Arguments passed by value or via `params` have no special notation. + - Arguments having the `in`, `out` or `ref` modifier have an `@` following their type name. Arguments passed by value or via `params` have no special notation. - Arguments that are arrays are represented as `[` *lowerbound* `:` *size* `,` … `,` *lowerbound* `:` *size* `]` where the number of commas is the rank less one, and the lower bounds and size of each dimension, if known, are represented in decimal. If a lower bound or size is not specified, it is omitted. If the lower bound and size for a particular dimension are omitted, the “`:`” is omitted as well. Jagged arrays are represented by one “`[]`” per level. - Arguments that have pointer types other than `void` are represented using a `*` following the type name. A `void` pointer is represented using a type name of `System.Void`. - Arguments that refer to generic type parameters defined on types are encoded using the “`` ` ``” character followed by the zero-based index of the type parameter. @@ -854,7 +854,7 @@ namespace Acme } public static void M0() { ... } - public void M1(char c, out float f, ref ValueType v) { ... } + public void M1(char c, out float f, ref ValueType v, in int i) { ... } public void M2(short[] x1, int[,] x2, long[][] x3) { ... } public void M3(long[][] x3, Widget[][,,] x4) { ... } public unsafe void M4(char *pc, Color **pf) { ... } @@ -881,7 +881,7 @@ IDs: "M:Acme.ValueType.M(System.Int32)" "M:Acme.Widget.NestedClass.M(System.Int32)" "M:Acme.Widget.M0" -"M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@)" +"M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@,System.Int32@)" "M:Acme.Widget.M2(System.Int16[],System.Int32[0:,0:],System.Int64[][])" "M:Acme.Widget.M3(System.Int64[][],Acme.Widget[0:,0:,0:][])" "M:Acme.Widget.M4(System.Char*,Color**)" @@ -1181,7 +1181,7 @@ Here is the output produced by one documentation generator when given the source This constructor initializes the new Point to - (,). + (,). xPosition is the new Point's x-coordinate. yPosition is the new Point's y-coordinate. diff --git a/standard/enums.md b/standard/enums.md index 8c308cb9f..4439edb88 100644 --- a/standard/enums.md +++ b/standard/enums.md @@ -1,6 +1,6 @@ -# 18 Enums +# 19 Enums -## 18.1 General +## 19.1 General An ***enum type*** is a distinct value type ([§8.3](types.md#83-value-types)) that declares a set of named constants. @@ -20,7 +20,7 @@ An ***enum type*** is a distinct value type ([§8.3](types.md#83-value-types)) t > > *end example* -## 18.2 Enum declarations +## 19.2 Enum declarations An enum declaration declares a new enum type. An enum declaration begins with the keyword `enum`, and defines the name, accessibility, underlying type, and members of the enum. @@ -44,7 +44,7 @@ enum_body ; ``` -Each enum type has a corresponding integral type called the ***underlying type*** of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the *enum_base* is present, it explicitly declares the underlying type. The underlying type shall be one of the *integral types* ([§8.3.6](types.md#836-integral-types)) other than `char`. The underlying type may be specified either by an `integral_type` ([§8.3.5](types.md#835-simple-types)), or an `integral_type_name`. The `integral_type_name` is resolved in the same way as `type_name` ([§7.8.1](basic-concepts.md#781-general)), including taking any using directives ([§13.5](namespaces.md#135-using-directives)) into account. +Each enum type has a corresponding integral type called the ***underlying type*** of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the *enum_base* is present, it explicitly declares the underlying type. The underlying type shall be one of the *integral types* ([§8.3.6](types.md#836-integral-types)) other than `char`. The underlying type may be specified either by an `integral_type` ([§8.3.5](types.md#835-simple-types)), or an `integral_type_name`. The `integral_type_name` is resolved in the same way as `type_name` ([§7.8.1](basic-concepts.md#781-general)), including taking any using directives ([§14.5](namespaces.md#145-using-directives)) into account. > *Note*: The `char` type cannot be used as an underlying type, either by keyword or via an `integral_type_name`. *end note* @@ -72,11 +72,11 @@ An enum declaration that does not explicitly declare an underlying type has an u -> *Note*: C# allows a trailing comma in an *enum_body*, just like it allows one in an *array_initializer* ([§16.7](arrays.md#167-array-initializers)). *end note* +> *Note*: C# allows a trailing comma in an *enum_body*, just like it allows one in an *array_initializer* ([§17.7](arrays.md#177-array-initializers)). *end note* An enum declaration cannot include a type parameter list, but any enum nested inside a generic class declaration or a generic struct declaration is a generic enum declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)). -## 18.3 Enum modifiers +## 19.3 Enum modifiers An *enum_declaration* may optionally include a sequence of enum modifiers: @@ -92,9 +92,9 @@ enum_modifier It is a compile-time error for the same modifier to appear multiple times in an enum declaration. -The modifiers of an enum declaration have the same meaning as those of a class declaration ([§14.2.2](classes.md#1422-class-modifiers)). However, the `abstract`, and `sealed`, and `static` modifiers are not permitted in an enum declaration. Enums cannot be abstract and do not permit derivation. +The modifiers of an enum declaration have the same meaning as those of a class declaration ([§15.2.2](classes.md#1522-class-modifiers)). However, the `abstract`, and `sealed`, and `static` modifiers are not permitted in an enum declaration. Enums cannot be abstract and do not permit derivation. -## 18.4 Enum members +## 19.4 Enum members The body of an enum type declaration defines zero or more enum members, which are the named constants of the enum type. No two enum members can have the same name. @@ -224,26 +224,26 @@ The associated value of an enum member may not, directly or indirectly, use the Enum members are named and scoped in a manner exactly analogous to fields within classes. The scope of an enum member is the body of its containing enum type. Within that scope, enum members can be referred to by their simple name. From all other code, the name of an enum member shall be qualified with the name of its enum type. Enum members do not have any declared accessibility—an enum member is accessible if its containing enum type is accessible. -## 18.5 The System.Enum type +## 19.5 The System.Enum type -The type `System.Enum` is the abstract base class of all enum types (this is distinct and different from the underlying type of the enum type), and the members inherited from `System.Enum` are available in any enum type. A boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)) exists from any enum type to `System.Enum`, and an unboxing conversion ([§10.3.6](conversions.md#1036-unboxing-conversions)) exists from `System.Enum` to any enum type. +The type `System.Enum` is the abstract base class of all enum types (this is distinct and different from the underlying type of the enum type), and the members inherited from `System.Enum` are available in any enum type. A boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)) exists from any enum type to `System.Enum`, and an unboxing conversion ([§10.3.7](conversions.md#1037-unboxing-conversions)) exists from `System.Enum` to any enum type. Note that `System.Enum` is not itself an *enum_type*. Rather, it is a *class_type* from which all *enum_type*s are derived. The type `System.Enum` inherits from the type `System.ValueType` ([§8.3.2](types.md#832-the-systemvaluetype-type)), which, in turn, inherits from type `object`. At run-time, a value of type `System.Enum` can be `null` or a reference to a boxed value of any enum type. -## 18.6 Enum values and operations +## 19.6 Enum values and operations Each enum type defines a distinct type; an explicit enumeration conversion ([§10.3.3](conversions.md#1033-explicit-enumeration-conversions)) is required to convert between an enum type and an integral type, or between two enum types. The set of values of the enum type is the same as the set of values of the underlying type and is not restricted to the values of the named constants. Any value of the underlying type of an enum can be cast to the enum type, and is a distinct valid value of that enum type. -Enum members have the type of their containing enum type (except within other enum member initializers: see [§18.4](enums.md#184-enum-members)). The value of an enum member declared in enum type `E` with associated value `v` is `(E)v`. +Enum members have the type of their containing enum type (except within other enum member initializers: see [§19.4](enums.md#194-enum-members)). The value of an enum member declared in enum type `E` with associated value `v` is `(E)v`. The following operators can be used on values of enum types: -- `==`, `!=`, `<`, `>`, `<=`, `>=` ([§11.11.6](expressions.md#11116-enumeration-comparison-operators)) -- binary `+` ([§11.9.5](expressions.md#1195-addition-operator)) -- binary `-` ([§11.9.6](expressions.md#1196-subtraction-operator)) -- `^`, `&`, `|` ([§11.12.3](expressions.md#11123-enumeration-logical-operators)) -- `~` ([§11.8.5](expressions.md#1185-bitwise-complement-operator)) -- `++`, `--` ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators) and [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)) -- `sizeof` ([§22.6.9](unsafe-code.md#2269-the-sizeof-operator)) +- `==`, `!=`, `<`, `>`, `<=`, `>=` ([§12.12.6](expressions.md#12126-enumeration-comparison-operators)) +- binary `+` ([§12.10.5](expressions.md#12105-addition-operator)) +- binary `-` ([§12.10.6](expressions.md#12106-subtraction-operator)) +- `^`, `&`, `|` ([§12.13.3](expressions.md#12133-enumeration-logical-operators)) +- `~` ([§12.9.5](expressions.md#1295-bitwise-complement-operator)) +- `++`, `--` ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators) and [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)) +- `sizeof` ([§23.6.9](unsafe-code.md#2369-the-sizeof-operator)) Every enum type automatically derives from the class `System.Enum` (which, in turn, derives from `System.ValueType` and `object`). Thus, inherited methods and properties of this class can be used on values of an enum type. diff --git a/standard/exceptions.md b/standard/exceptions.md index d950ff11c..cad888ca1 100644 --- a/standard/exceptions.md +++ b/standard/exceptions.md @@ -1,18 +1,18 @@ -# 20 Exceptions +# 21 Exceptions -## 20.1 General +## 21.1 General Exceptions in C# provide a structured, uniform, and type-safe way of handling both system level and application-level error conditions. -## 20.2 Causes of exceptions +## 21.2 Causes of exceptions Exception can be thrown in two different ways. -- A `throw` statement ([§12.10.6](statements.md#12106-the-throw-statement)) throws an exception immediately and unconditionally. Control never reaches the statement immediately following the `throw`. -- Certain exceptional conditions that arise during the processing of C# statements and expression cause an exception in certain circumstances when the operation cannot be completed normally. See [§20.5](exceptions.md#205-common-exception-classes) for a list of the various exceptions that can occur in this way. - > *Example*: An integer division operation ([§11.9.3](expressions.md#1193-division-operator)) throws a `System.DivideByZeroException` if the denominator is zero. *end example* +- A `throw` statement ([§13.10.6](statements.md#13106-the-throw-statement)) throws an exception immediately and unconditionally. Control never reaches the statement immediately following the `throw`. +- Certain exceptional conditions that arise during the processing of C# statements and expression cause an exception in certain circumstances when the operation cannot be completed normally. See [§21.5](exceptions.md#215-common-exception-classes) for a list of the various exceptions that can occur in this way. + > *Example*: An integer division operation ([§12.10.3](expressions.md#12103-division-operator)) throws a `System.DivideByZeroException` if the denominator is zero. *end example* -## 20.3 The System.Exception class +## 21.3 The System.Exception class The `System.Exception` class is the base type of all exceptions. This class has a few notable properties that all exceptions share: @@ -21,9 +21,9 @@ The `System.Exception` class is the base type of all exceptions. This class has The value of these properties can be specified in calls to the instance constructor for `System.Exception`. -## 20.4 How exceptions are handled +## 21.4 How exceptions are handled -Exceptions are handled by a `try` statement ([§12.11](statements.md#1211-the-try-statement)). +Exceptions are handled by a `try` statement ([§13.11](statements.md#1311-the-try-statement)). When an exception occurs, the system searches for the nearest catch clause that can handle the exception, as determined by the run-time type of the exception. First, the current method is searched for a lexically enclosing `try` statement, and the associated `catch` clauses of the `try` statement are considered in order. If that fails, the method that called the current method is searched for a lexically enclosing `try` statement that encloses the point of the call to the current method. This search continues until a `catch` clause is found that can handle the current exception, by naming an exception class that is of the same class, or a base class, of the run-time type of the exception being thrown. A `catch` clause that doesn’t name an exception class can handle any exception. @@ -31,11 +31,11 @@ Once a matching `catch` clause is found, the system prepares to transfer control If no matching `catch` clause is found: -- If the search for a matching `catch` clause reaches a static constructor ([§14.12](classes.md#1412-static-constructors)) or static field initializer, then a `System.TypeInitializationException` is thrown at the point that triggered the invocation of the static constructor. The inner exception of the `System.TypeInitializationException` contains the exception that was originally thrown. +- If the search for a matching `catch` clause reaches a static constructor ([§15.12](classes.md#1512-static-constructors)) or static field initializer, then a `System.TypeInitializationException` is thrown at the point that triggered the invocation of the static constructor. The inner exception of the `System.TypeInitializationException` contains the exception that was originally thrown. - Otherwise, if an exception occurs during finalizer execution, and that exception is not caught, then the behavior is unspecified. - Otherwise, if the search for matching `catch` clauses reaches the code that initially started the thread, then execution of the thread is terminated. The impact of such termination is implementation-defined. -## 20.5 Common exception classes +## 21.5 Common exception classes The following exceptions are thrown by certain C# operations. diff --git a/standard/expressions.md b/standard/expressions.md index ecaf85c89..d2c0df118 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1,46 +1,48 @@ -# 11 Expressions +# 12 Expressions -## 11.1 General +## 12.1 General An expression is a sequence of operators and operands. This clause defines the syntax, order of evaluation of operands and operators, and meaning of expressions. -## 11.2 Expression classifications +## 12.2 Expression classifications -### 11.2.1 General +### 12.2.1 General The result of an expression is classified as one of the following: - A value. Every value has an associated type. -- A variable. Every variable has an associated type, namely the declared type of the variable. +- A variable. Unless otherwise specified, a variable is explicitly typed and has an associated type, namely the declared type of the variable. An implicitly typed variable has no associated type. - A null literal. An expression with this classification can be implicitly converted to a reference type or nullable value type. - An anonymous function. An expression with this classification can be implicitly converted to a compatible delegate type or expression tree type. -- A property access. Every property access has an associated type, namely the type of the property. Furthermore, a property access may have an associated instance expression. When an accessor of an instance property access is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§11.7.12](expressions.md#11712-this-access)). -- An indexer access. Every indexer access has an associated type, namely the element type of the indexer. Furthermore, an indexer access has an associated instance expression and an associated argument list. When an accessor of an indexer access is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§11.7.12](expressions.md#11712-this-access)), and the result of evaluating the argument list becomes the parameter list of the invocation. -- Nothing. This occurs when the expression is an invocation of a method with a return type of `void`. An expression classified as nothing is only valid in the context of a *statement_expression* ([§12.7](statements.md#127-expression-statements)) or as the body of a *lambda_expression* ([§11.17](expressions.md#1117-anonymous-function-expressions)). +- A tuple. Every tuple has a fixed number of elements, each with an expression and an optional tuple element name. +- A property access. Every property access has an associated type, namely the type of the property. Furthermore, a property access may have an associated instance expression. When an accessor of an instance property access is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§12.8.13](expressions.md#12813-this-access)). +- An indexer access. Every indexer access has an associated type, namely the element type of the indexer. Furthermore, an indexer access has an associated instance expression and an associated argument list. When an accessor of an indexer access is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§12.8.13](expressions.md#12813-this-access)), and the result of evaluating the argument list becomes the parameter list of the invocation. +- Nothing. This occurs when the expression is an invocation of a method with a return type of `void`. An expression classified as nothing is only valid in the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)) or as the body of a *lambda_expression* ([§12.19](expressions.md#1219-anonymous-function-expressions)). For expressions which occur as subexpressions of larger expressions, with the noted restrictions, the result can also be classified as one of the following: -- A namespace. An expression with this classification can only appear as the left-hand side of a *member_access* ([§11.7.6](expressions.md#1176-member-access)). In any other context, an expression classified as a namespace causes a compile-time error. -- A type. An expression with this classification can only appear as the left-hand side of a *member_access* ([§11.7.6](expressions.md#1176-member-access)). In any other context, an expression classified as a type causes a compile-time error. -- A method group, which is a set of overloaded methods resulting from a member lookup ([§11.5](expressions.md#115-member-lookup)). A method group may have an associated instance expression and an associated type argument list. When an instance method is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§11.7.12](expressions.md#11712-this-access)). A method group is permitted in an *invocation_expression* ([§11.7.8](expressions.md#1178-invocation-expressions)) or a *delegate_creation_expression* ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)), and can be implicitly converted to a compatible delegate type ([§10.8](conversions.md#108-method-group-conversions)). In any other context, an expression classified as a method group causes a compile-time error. -- An event access. Every event access has an associated type, namely the type of the event. Furthermore, an event access may have an associated instance expression. An event access may appear as the left-hand operand of the `+=` and `-=` operators ([§11.19.4](expressions.md#11194-event-assignment)). In any other context, an expression classified as an event access causes a compile-time error. When an accessor of an instance event access is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§11.7.12](expressions.md#11712-this-access)). +- A namespace. An expression with this classification can only appear as the left-hand side of a *member_access* ([§12.8.7](expressions.md#1287-member-access)). In any other context, an expression classified as a namespace causes a compile-time error. +- A type. An expression with this classification can only appear as the left-hand side of a *member_access* ([§12.8.7](expressions.md#1287-member-access)). In any other context, an expression classified as a type causes a compile-time error. +- A method group, which is a set of overloaded methods resulting from a member lookup ([§12.5](expressions.md#125-member-lookup)). A method group may have an associated instance expression and an associated type argument list. When an instance method is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§12.8.13](expressions.md#12813-this-access)). A method group is permitted in an *invocation_expression* ([§12.8.9](expressions.md#1289-invocation-expressions)) or a *delegate_creation_expression* ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)), and can be implicitly converted to a compatible delegate type ([§10.8](conversions.md#108-method-group-conversions)). In any other context, an expression classified as a method group causes a compile-time error. +- An event access. Every event access has an associated type, namely the type of the event. Furthermore, an event access may have an associated instance expression. An event access may appear as the left operand of the `+=` and `-=` operators ([§12.21.5](expressions.md#12215-event-assignment)). In any other context, an expression classified as an event access causes a compile-time error. When an accessor of an instance event access is invoked, the result of evaluating the instance expression becomes the instance represented by `this` ([§12.8.13](expressions.md#12813-this-access)). - A throw expression, which may be used is several contexts to throw an exception in an expression. A throw expression may be converted by an implicit conversion to any type. -A property access or indexer access is always reclassified as a value by performing an invocation of the *get_accessor* or the *set_accessor*. The particular accessor is determined by the context of the property or indexer access: If the access is the target of an assignment, the *set_accessor* is invoked to assign a new value ([§11.19.2](expressions.md#11192-simple-assignment)). Otherwise, the *get_accessor* is invoked to obtain the current value ([§11.2.2](expressions.md#1122-values-of-expressions)). +A property access or indexer access is always reclassified as a value by performing an invocation of the *get_accessor* or the *set_accessor*. The particular accessor is determined by the context of the property or indexer access: If the access is the target of an assignment, the *set_accessor* is invoked to assign a new value ([§12.21.2](expressions.md#12212-simple-assignment)). Otherwise, the *get_accessor* is invoked to obtain the current value ([§12.2.2](expressions.md#1222-values-of-expressions)). An ***instance accessor*** is a property access on an instance, an event access on an instance, or an indexer access. -### 11.2.2 Values of expressions +### 12.2.2 Values of expressions Most of the constructs that involve an expression ultimately require the expression to denote a ***value***. In such cases, if the actual expression denotes a namespace, a type, a method group, or nothing, a compile-time error occurs. However, if the expression denotes a property access, an indexer access, or a variable, the value of the property, indexer, or variable is implicitly substituted: - The value of a variable is simply the value currently stored in the storage location identified by the variable. A variable shall be considered definitely assigned ([§9.4](variables.md#94-definite-assignment)) before its value can be obtained, or otherwise a compile-time error occurs. -- The value of a property access expression is obtained by invoking the *get_accessor* of the property. If the property has no *get_accessor*, a compile-time error occurs. Otherwise, a function member invocation ([§11.6.6](expressions.md#1166-function-member-invocation)) is performed, and the result of the invocation becomes the value of the property access expression. -- The value of an indexer access expression is obtained by invoking the *get_accessor* of the indexer. If the indexer has no *get_accessor*, a compile-time error occurs. Otherwise, a function member invocation ([§11.6.6](expressions.md#1166-function-member-invocation)) is performed with the argument list associated with the indexer access expression, and the result of the invocation becomes the value of the indexer access expression. +- The value of a property access expression is obtained by invoking the *get_accessor* of the property. If the property has no *get_accessor*, a compile-time error occurs. Otherwise, a function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)) is performed, and the result of the invocation becomes the value of the property access expression. +- The value of an indexer access expression is obtained by invoking the *get_accessor* of the indexer. If the indexer has no *get_accessor*, a compile-time error occurs. Otherwise, a function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)) is performed with the argument list associated with the indexer access expression, and the result of the invocation becomes the value of the indexer access expression. +- The value of a tuple expression is obtained by applying an implicit tuple conversion ([§10.2.13](conversions.md#10213-implicit-tuple-conversions)) to the type of the tuple expression. It is an error to obtain the value of a tuple expression that does not have a type. -## 11.3 Static and Dynamic Binding +## 12.3 Static and Dynamic Binding -### 11.3.1 General +### 12.3.1 General ***Binding*** is the process of determining what an operation refers to, based on the type or value of expressions (arguments, operands, receivers). For instance, the binding of a method call is determined based on the type of the receiver and arguments. The binding of an operator is determined based on the type of its operands. @@ -59,12 +61,14 @@ The following operations in C# are subject to binding: - Object creation: new `C(e₁,...,eᵥ)` - Overloaded unary operators: `+`, `-`, `!`, `~`, `++`, `--`, `true`, `false` - Overloaded binary operators: `+`, `-`, `*`, `/`, `%`, `&`, `&&`, `|`, `||`, `??`, `^`, `<<`, `>>`, `==`, `!=`, `>`, `<`, `>=`, `<=` -- Assignment operators: `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=` +- Assignment operators: `=`, `= ref`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=` - Implicit and explicit conversions When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of subexpressions are used in the selection process. However, when one of the subexpressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound. -### 11.3.2 Binding-time +It is a compile time error if a method invocation is dynamically bound and any of the parameters, including the receiver, has the `in` modifier. + +### 12.3.2 Binding-time Static binding takes place at compile-time, whereas dynamic binding takes place at run-time. In the following subclauses, the term ***binding-time*** refers to either compile-time or run-time, depending on when the binding takes place. @@ -85,7 +89,7 @@ Static binding takes place at compile-time, whereas dynamic binding takes place > > *end example* -### 11.3.3 Dynamic binding +### 12.3.3 Dynamic binding **This subclause is informative.** @@ -95,7 +99,7 @@ The mechanism by which a dynamic object implements its own semantics is implemen While the purpose of dynamic binding is to allow interoperation with dynamic objects, C# allows dynamic binding on all objects, whether they are dynamic or not. This allows for a smoother integration of dynamic objects, as the results of operations on them may not themselves be dynamic objects, but are still of a type unknown to the programmer at compile-time. Also, dynamic binding can help eliminate error-prone reflection-based code even when no objects involved are dynamic objects. -### 11.3.4 Types of subexpressions +### 12.3.4 Types of subexpressions When an operation is statically bound, the type of a subexpression (e.g., a receiver, and argument, an index or an operand) is always considered to be the compile-time type of that expression. @@ -105,9 +109,9 @@ When an operation is dynamically bound, the type of a subexpression is determine - A subexpression whose compile-time type is a type parameter is considered to have the type which the type parameter is bound to at run-time - Otherwise, the subexpression is considered to have its compile-time type. -## 11.4 Operators +## 12.4 Operators -### 11.4.1 General +### 12.4.1 General Expressions are constructed from operands and operators. The operators of an expression indicate which operations to apply to the operands. @@ -119,15 +123,15 @@ There are three kinds of operators: - Binary operators. The binary operators take two operands and all use infix notation (such as `x + y`). - Ternary operator. Only one ternary operator, `?:`, exists; it takes three operands and uses infix notation (`c ? x : y`). -The order of evaluation of operators in an expression is determined by the *precedence* and *associativity* of the operators ([§11.4.2](expressions.md#1142-operator-precedence-and-associativity)). +The order of evaluation of operators in an expression is determined by the *precedence* and *associativity* of the operators ([§12.4.2](expressions.md#1242-operator-precedence-and-associativity)). Operands in an expression are evaluated from left to right. > *Example*: In `F(i) + G(i++) * H(i)`, method `F` is called using the old value of `i`, then method `G` is called with the old value of `i`, and, finally, method `H` is called with the new value of i. This is separate from and unrelated to operator precedence. *end example* -Certain operators can be ***overloaded***. Operator overloading ([§11.4.3](expressions.md#1143-operator-overloading)) permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type. +Certain operators can be ***overloaded***. Operator overloading ([§12.4.3](expressions.md#1243-operator-overloading)) permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type. -### 11.4.2 Operator precedence and associativity +### 12.4.2 Operator precedence and associativity When an expression contains multiple operators, the ***precedence*** of the operators controls the order in which the individual operators are evaluated. @@ -143,21 +147,21 @@ The precedence of an operator is established by the definition of its associated > > | **Subclause** | **Category** | **Operators** | > | ----------------- | ------------------------------- | -------------------------------------------------------| -> | [§11.7](expressions.md#117-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` | -> | [§11.8](expressions.md#118-unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` `await x` | -> | [§11.9](expressions.md#119-arithmetic-operators) | Multiplicative | `*` `/` `%` | -> | [§11.9](expressions.md#119-arithmetic-operators) | Additive | `+` `-` | -> | [§11.10](expressions.md#1110-shift-operators) | Shift | `<<` `>>` | -> | [§11.11](expressions.md#1111-relational-and-type-testing-operators) | Relational and type-testing | `<` `>` `<=` `>=` `is` `as` | -> | [§11.11](expressions.md#1111-relational-and-type-testing-operators) | Equality | `==` `!=` | -> | [§11.12](expressions.md#1112-logical-operators) | Logical AND | `&` | -> | [§11.12](expressions.md#1112-logical-operators) | Logical XOR | `^` | -> | [§11.12](expressions.md#1112-logical-operators) | Logical OR | `\|` | -> | [§11.13](expressions.md#1113-conditional-logical-operators) | Conditional AND | `&&` | -> | [§11.13](expressions.md#1113-conditional-logical-operators) | Conditional OR | `\|\|` | -> | [§11.14](expressions.md#1114-the-null-coalescing-operator) and [§11.15](expressions.md#1115-the-throw-expression-operator) | Null coalescing and throw expression | `??` `throw x` | -> | [§11.16](expressions.md#1116-conditional-operator) | Conditional | `?:` | -> | [§11.19](expressions.md#1119-assignment-operators) and [§11.17](expressions.md#1117-anonymous-function-expressions) | Assignment and lambda expression | `=` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | +> | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` | +> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` `await x` | +> | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` | +> | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` | +> | [§12.11](expressions.md#1211-shift-operators) | Shift | `<<` `>>` | +> | [§12.12](expressions.md#1212-relational-and-type-testing-operators) | Relational and type-testing | `<` `>` `<=` `>=` `is` `as` | +> | [§12.12](expressions.md#1212-relational-and-type-testing-operators) | Equality | `==` `!=` | +> | [§12.13](expressions.md#1213-logical-operators) | Logical AND | `&` | +> | [§12.13](expressions.md#1213-logical-operators) | Logical XOR | `^` | +> | [§12.13](expressions.md#1213-logical-operators) | Logical OR | `\|` | +> | [§12.14](expressions.md#1214-conditional-logical-operators) | Conditional AND | `&&` | +> | [§12.14](expressions.md#1214-conditional-logical-operators) | Conditional OR | `\|\|` | +> | [§12.15](expressions.md#1215-the-null-coalescing-operator) and [§12.16](expressions.md#1216-the-throw-expression-operator) | Null coalescing and throw expression | `??` `throw x` | +> | [§12.18](expressions.md#1218-conditional-operator) | Conditional | `?:` | +> | [§12.21](expressions.md#1221-assignment-operators) and [§12.19](expressions.md#1219-anonymous-function-expressions) | Assignment and lambda expression | `=` `= ref` `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `&=` `^=` `\|=` `=>` | > > *end note* @@ -172,9 +176,9 @@ Precedence and associativity can be controlled using parentheses. > *Example*: `x + y * z` first multiplies `y` by `z` and then adds the result to `x`, but `(x + y) * z` first adds `x` and `y` and then multiplies the result by `z`. *end example* -### 11.4.3 Operator overloading +### 12.4.3 Operator overloading -All unary and binary operators have predefined implementations. In addition, user-defined implementations can be introduced by including operator declarations ([§14.10](classes.md#1410-operators)) in classes and structs. User-defined operator implementations always take precedence over predefined operator implementations: Only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered, as described in [§11.4.4](expressions.md#1144-unary-operator-overload-resolution) and [§11.4.5](expressions.md#1145-binary-operator-overload-resolution). +All unary and binary operators have predefined implementations. In addition, user-defined implementations can be introduced by including operator declarations ([§15.10](classes.md#1510-operators)) in classes and structs. User-defined operator implementations always take precedence over predefined operator implementations: Only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered, as described in [§12.4.4](expressions.md#1244-unary-operator-overload-resolution) and [§12.4.5](expressions.md#1245-binary-operator-overload-resolution). The ***overloadable unary operators*** are: @@ -182,7 +186,7 @@ The ***overloadable unary operators*** are: + - ! ~ ++ -- true false ``` -> *Note*: Although `true` and `false` are not used explicitly in expressions (and therefore are not included in the precedence table in [§11.4.2](expressions.md#1142-operator-precedence-and-associativity)), they are considered operators because they are invoked in several expression contexts: Boolean expressions ([§11.22](expressions.md#1122-boolean-expressions)) and expressions involving the conditional ([§11.16](expressions.md#1116-conditional-operator)) and conditional logical operators ([§11.13](expressions.md#1113-conditional-logical-operators)). *end note* +> *Note*: Although `true` and `false` are not used explicitly in expressions (and therefore are not included in the precedence table in [§12.4.2](expressions.md#1242-operator-precedence-and-associativity)), they are considered operators because they are invoked in several expression contexts: Boolean expressions ([§12.24](expressions.md#1224-boolean-expressions)) and expressions involving the conditional ([§12.18](expressions.md#1218-conditional-operator)) and conditional logical operators ([§12.14](expressions.md#1214-conditional-logical-operators)). *end note* The ***overloadable binary operators*** are: @@ -194,19 +198,19 @@ Only the operators listed above can be overloaded. In particular, it is not poss When a binary operator is overloaded, the corresponding compound assignment operator, if any, is also implicitly overloaded. -> *Example*: An overload of operator `*` is also an overload of operator `*=`. This is described further in [§11.19](expressions.md#1119-assignment-operators). *end example* +> *Example*: An overload of operator `*` is also an overload of operator `*=`. This is described further in [§12.21](expressions.md#1221-assignment-operators). *end example* -The assignment operator itself `(=)` cannot be overloaded. An assignment always performs a simple store of a value into a variable ([§11.19.2](expressions.md#11192-simple-assignment)). +The assignment operator itself `(=)` cannot be overloaded. An assignment always performs a simple store of a value into a variable ([§12.21.2](expressions.md#12212-simple-assignment)). Cast operations, such as `(T)x`, are overloaded by providing user-defined conversions ([§10.5](conversions.md#105-user-defined-conversions)). > *Note*: User-defined conversions do not affect the behavior of the `is` or `as` operators. *end note* -Element access, such as `a[x]`, is not considered an overloadable operator. Instead, user-defined indexing is supported through indexers ([§14.9](classes.md#149-indexers)). +Element access, such as `a[x]`, is not considered an overloadable operator. Instead, user-defined indexing is supported through indexers ([§15.9](classes.md#159-indexers)). In expressions, operators are referenced using operator notation, and in declarations, operators are referenced using functional notation. The following table shows the relationship between operator and functional notations for unary and binary operators. In the first entry, «op» denotes any overloadable unary prefix operator. In the second entry, «op» denotes the unary postfix `++` and `--` operators. In the third entry, «op» denotes any overloadable binary operator. -> *Note*: For an example of overloading the `++` and `--` operators see [§14.10.2](classes.md#14102-unary-operators). *end note* +> *Note*: For an example of overloading the `++` and `--` operators see [§15.10.2](classes.md#15102-unary-operators). *end note* **Operator notation** | **Functional notation** ---------------------- | ------------------------- @@ -220,52 +224,52 @@ User-defined operator declarations always require at least one of the parameters User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. -> *Example*: The `/` operator is always a binary operator, always has the precedence level specified in [§11.4.2](expressions.md#1142-operator-precedence-and-associativity), and is always left-associative. *end example* +> *Example*: The `/` operator is always a binary operator, always has the precedence level specified in [§12.4.2](expressions.md#1242-operator-precedence-and-associativity), and is always left-associative. *end example* > *Note*: While it is possible for a user-defined operator to perform any computation it pleases, implementations that produce results other than those that are intuitively expected are strongly discouraged. For example, an implementation of operator `==` should compare the two operands for equality and return an appropriate `bool` result. *end note* -The descriptions of individual operators in [§11.8](expressions.md#118-unary-operators) through [§11.19](expressions.md#1119-assignment-operators) specify the predefined implementations of the operators and any additional rules that apply to each operator. The descriptions make use of the terms ***unary operator overload resolution***, ***binary operator overload resolution***, ***numeric promotion***, and lifted operator definitions of which are found in the following subclauses. +The descriptions of individual operators in [§12.9](expressions.md#129-unary-operators) through [§12.21](expressions.md#1221-assignment-operators) specify the predefined implementations of the operators and any additional rules that apply to each operator. The descriptions make use of the terms ***unary operator overload resolution***, ***binary operator overload resolution***, ***numeric promotion***, and lifted operator definitions of which are found in the following subclauses. -### 11.4.4 Unary operator overload resolution +### 12.4.4 Unary operator overload resolution An operation of the form `«op» x` or `x «op»`, where «op» is an overloadable unary operator, and `x` is an expression of type `X`, is processed as follows: -- The set of candidate user-defined operators provided by `X` for the operation `operator «op»(x)` is determined using the rules of [§11.4.6](expressions.md#1146-candidate-user-defined-operators). +- The set of candidate user-defined operators provided by `X` for the operation `operator «op»(x)` is determined using the rules of [§12.4.6](expressions.md#1246-candidate-user-defined-operators). - If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary `operator «op»` implementations, including their lifted forms, become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator. The predefined operators provided by an enum or delegate type are only included in this set when the binding-time type—or the underlying type if it is a nullable type—of either operand is the enum or delegate type. -- The overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution) are applied to the set of candidate operators to select the best operator with respect to the argument list `(x)`, and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs. +- The overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution) are applied to the set of candidate operators to select the best operator with respect to the argument list `(x)`, and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs. -### 11.4.5 Binary operator overload resolution +### 12.4.5 Binary operator overload resolution An operation of the form `x «op» y`, where «op» is an overloadable binary operator, `x` is an expression of type `X`, and `y` is an expression of type `Y`, is processed as follows: -- The set of candidate user-defined operators provided by `X` and `Y` for the operation `operator «op»(x, y)` is determined. The set consists of the union of the candidate operators provided by `X` and the candidate operators provided by `Y`, each determined using the rules of [§11.4.6](expressions.md#1146-candidate-user-defined-operators). For the combined set, candidates are merged as follows: +- The set of candidate user-defined operators provided by `X` and `Y` for the operation `operator «op»(x, y)` is determined. The set consists of the union of the candidate operators provided by `X` and the candidate operators provided by `Y`, each determined using the rules of [§12.4.6](expressions.md#1246-candidate-user-defined-operators). For the combined set, candidates are merged as follows: - If `X` and `Y` are the same type, or if `X` and `Y` are derived from a common base type, then shared candidate operators only occur in the combined set once. - If there is an identity conversion between `X` and `Y`, an operator `«op»Y` provided by `Y` has the same return type as an `«op»X` provided by `X` and the operand types of `«op»Y` have an identity conversion to the corresponding operand types of `«op»X` then only `«op»X` occurs in the set. - If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary `operator «op»` implementations, including their lifted forms, become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator. For predefined enum and delegate operators, the only operators considered are those provided by an enum or delegate type that is the binding-time type of one of the operands. -- The overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution) are applied to the set of candidate operators to select the best operator with respect to the argument list `(x, y)`, and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs. +- The overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution) are applied to the set of candidate operators to select the best operator with respect to the argument list `(x, y)`, and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs. -### 11.4.6 Candidate user-defined operators +### 12.4.6 Candidate user-defined operators Given a type `T` and an operation `operator «op»(A)`, where «op» is an overloadable operator and `A` is an argument list, the set of candidate user-defined operators provided by `T` for operator `«op»(A)` is determined as follows: - Determine the type `T₀`. If `T` is a nullable value type, `T₀` is its underlying type; otherwise, `T₀` is equal to `T`. -- For all `operator «op»` declarations in `T₀` and all lifted forms of such operators, if at least one operator is applicable ([§11.6.4.2](expressions.md#11642-applicable-function-member)) with respect to the argument list `A`, then the set of candidate operators consists of all such applicable operators in `T₀`. +- For all `operator «op»` declarations in `T₀` and all lifted forms of such operators, if at least one operator is applicable ([§12.6.4.2](expressions.md#12642-applicable-function-member)) with respect to the argument list `A`, then the set of candidate operators consists of all such applicable operators in `T₀`. - Otherwise, if `T₀` is `object`, the set of candidate operators is empty. - Otherwise, the set of candidate operators provided by `T₀` is the set of candidate operators provided by the direct base class of `T₀`, or the effective base class of `T₀` if `T₀` is a type parameter. -### 11.4.7 Numeric promotions +### 12.4.7 Numeric promotions -#### 11.4.7.1 General +#### 12.4.7.1 General **This subclause is informative.** -[§11.4.7](expressions.md#1147-numeric-promotions) and its subclauses are a summary of the combined effect of: +[§12.4.7](expressions.md#1247-numeric-promotions) and its subclauses are a summary of the combined effect of: - the rules for implicit numeric conversions ([§10.2.3](conversions.md#1023-implicit-numeric-conversions)); -- the rules for better conversion ([§11.6.4.6](expressions.md#11646-better-conversion-target)); and -- the available arithmetic ([§11.9](expressions.md#119-arithmetic-operators)), relational ([§11.11](expressions.md#1111-relational-and-type-testing-operators)), and integral logical ([§11.12.2](expressions.md#11122-integer-logical-operators)) operators. +- the rules for better conversion ([§12.6.4.7](expressions.md#12647-better-conversion-target)); and +- the available arithmetic ([§12.10](expressions.md#1210-arithmetic-operators)), relational ([§12.12](expressions.md#1212-relational-and-type-testing-operators)), and integral logical ([§12.13.2](expressions.md#12132-integer-logical-operators)) operators. Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects. @@ -281,13 +285,13 @@ double operator *(double x, double y); decimal operator *(decimal x, decimal y); ``` -When overload resolution rules ([§11.6.4](expressions.md#1164-overload-resolution)) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. +When overload resolution rules ([§12.6.4](expressions.md#1264-overload-resolution)) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. > *Example*: For the operation `b * s`, where `b` is a `byte` and `s` is a `short`, overload resolution selects `operator *(int, int)` as the best operator. Thus, the effect is that `b` and `s` are converted to `int`, and the type of the result is `int`. Likewise, for the operation `i * d`, where `i` is an `int` and `d` is a `double`, `overload` resolution selects `operator *(double, double)` as the best operator. *end example* **End of informative text.** -#### 11.4.7.2 Unary numeric promotions +#### 12.4.7.2 Unary numeric promotions **This subclause is informative.** @@ -295,7 +299,7 @@ Unary numeric promotion occurs for the operands of the predefined `+`, `–`, an **End of informative text.** -#### 11.4.7.3 Binary numeric promotions +#### 12.4.7.3 Binary numeric promotions **This subclause is informative.** @@ -338,37 +342,37 @@ In both of the above cases, a cast expression can be used to explicitly convert **End of informative text.** -### 11.4.8 Lifted operators +### 12.4.8 Lifted operators ***Lifted operators*** permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following: - For the unary operators `+`, `++`, `-`, `--`, `!`, and `~`, a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single `?` modifier to the operand and result types. The lifted operator produces a `null` value if the operand is `null`. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result. -- For the binary operators `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, and `>>`, a lifted form of an operator exists if the operand and result types are all non-nullable value types. The lifted form is constructed by adding a single `?` modifier to each operand and result type. The lifted operator produces a `null` value if one or both operands are `null` (an exception being the `&` and `|` operators of the `bool?` type, as described in [§11.12.5](expressions.md#11125-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result. +- For the binary operators `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, and `>>`, a lifted form of an operator exists if the operand and result types are all non-nullable value types. The lifted form is constructed by adding a single `?` modifier to each operand and result type. The lifted operator produces a `null` value if one or both operands are `null` (an exception being the `&` and `|` operators of the `bool?` type, as described in [§12.13.5](expressions.md#12135-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result. - For the equality operators `==` and `!=`, a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is `bool`. The lifted form is constructed by adding a single `?` modifier to each operand type. The lifted operator considers two `null` values equal, and a `null` value unequal to any non-`null` value. If both operands are non-`null`, the lifted operator unwraps the operands and applies the underlying operator to produce the `bool` result. - For the relational operators `<`, `>`, `<=`, and `>=`, a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is `bool`. The lifted form is constructed by adding a single `?` modifier to each operand type. The lifted operator produces the value `false` if one or both operands are `null`. Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the `bool` result. -## 11.5 Member lookup +## 12.5 Member lookup -### 11.5.1 General +### 12.5.1 General -A member lookup is the process whereby the meaning of a name in the context of a type is determined. A member lookup can occur as part of evaluating a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) or a *member_access* ([§11.7.6](expressions.md#1176-member-access)) in an expression. If the *simple_name* or *member_access* occurs as the *primary_expression* of an *invocation_expression* ([§11.7.8.2](expressions.md#11782-method-invocations)), the member is said to be *invoked*. +A member lookup is the process whereby the meaning of a name in the context of a type is determined. A member lookup can occur as part of evaluating a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) or a *member_access* ([§12.8.7](expressions.md#1287-member-access)) in an expression. If the *simple_name* or *member_access* occurs as the *primary_expression* of an *invocation_expression* ([§12.8.9.2](expressions.md#12892-method-invocations)), the member is said to be *invoked*. -If a member is a method or event, or if it is a constant, field or property of either a delegate type ([§19](delegates.md#19-delegates)) or the type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)), then the member is said to be *invocable.* +If a member is a method or event, or if it is a constant, field or property of either a delegate type ([§20](delegates.md#20-delegates)) or the type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)), then the member is said to be *invocable.* Member lookup considers not only the name of a member but also the number of type parameters the member has and whether the member is accessible. For the purposes of member lookup, generic methods and nested generic types have the number of type parameters indicated in their respective declarations and all other members have zero type parameters. A member lookup of a name `N` with `K` type arguments in a type `T` is processed as follows: - First, a set of accessible members named `N` is determined: - - If `T` is a type parameter, then the set is the union of the sets of accessible members named `N` in each of the types specified as a primary constraint or secondary constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)) for `T`, along with the set of accessible members named `N` in `object`. - - Otherwise, the set consists of all accessible ([§7.5](basic-concepts.md#75-member-access)) members named `N` in `T`, including inherited members and the accessible members named `N` in `object`. If `T` is a constructed type, the set of members is obtained by substituting type arguments as described in [§14.3.3](classes.md#1433-members-of-constructed-types). Members that include an `override` modifier are excluded from the set. -- Next, if `K` is zero, all nested types whose declarations include type parameters are removed. If `K` is not zero, all members with a different number of type parameters are removed. When `K` is zero, methods having type parameters are not removed, since the type inference process ([§11.6.3](expressions.md#1163-type-inference)) might be able to infer the type arguments. + - If `T` is a type parameter, then the set is the union of the sets of accessible members named `N` in each of the types specified as a primary constraint or secondary constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)) for `T`, along with the set of accessible members named `N` in `object`. + - Otherwise, the set consists of all accessible ([§7.5](basic-concepts.md#75-member-access)) members named `N` in `T`, including inherited members and the accessible members named `N` in `object`. If `T` is a constructed type, the set of members is obtained by substituting type arguments as described in [§15.3.3](classes.md#1533-members-of-constructed-types). Members that include an `override` modifier are excluded from the set. +- Next, if `K` is zero, all nested types whose declarations include type parameters are removed. If `K` is not zero, all members with a different number of type parameters are removed. When `K` is zero, methods having type parameters are not removed, since the type inference process ([§12.6.3](expressions.md#1263-type-inference)) might be able to infer the type arguments. - Next, if the member is invoked, all non-invocable members are removed from the set. - Next, members that are hidden by other members are removed from the set. For every member `S.M` in the set, where `S` is the type in which the member `M` is declared, the following rules are applied: - If `M` is a constant, field, property, event, or enumeration member, then all members declared in a base type of `S` are removed from the set. - If `M` is a type declaration, then all non-types declared in a base type of `S` are removed from the set, and all type declarations with the same number of type parameters as `M` declared in a base type of `S` are removed from the set. - If `M` is a method, then all non-method members declared in a base type of `S` are removed from the set. -- Next, interface members that are hidden by class members are removed from the set. This step only has an effect if `T` is a type parameter and `T` has both an effective base class other than `object` and a non-empty effective interface set ([§14.2.5](classes.md#1425-type-parameter-constraints)). For every member `S.M` in the set, where `S` is the type in which the member `M` is declared, the following rules are applied if `S` is a class declaration other than `object`: +- Next, interface members that are hidden by class members are removed from the set. This step only has an effect if `T` is a type parameter and `T` has both an effective base class other than `object` and a non-empty effective interface set ([§15.2.5](classes.md#1525-type-parameter-constraints)). For every member `S.M` in the set, where `S` is the type in which the member `M` is declared, the following rules are applied if `S` is a class declaration other than `object`: - If `M` is a constant, field, property, event, enumeration member, or type declaration, then all members declared in an interface declaration are removed from the set. - If `M` is a method, then all non-method members declared in an interface declaration are removed from the set, and all methods with the same signature as `M` declared in an interface declaration are removed from the set. - Finally, having removed hidden members, the result of the lookup is determined: @@ -376,11 +380,11 @@ A member lookup of a name `N` with `K` type arguments in a type `T` is proces - Otherwise, if the set contains only methods, then this group of methods is the result of the lookup. - Otherwise, the lookup is ambiguous, and a binding-time error occurs. -For member lookups in types other than type parameters and interfaces, and member lookups in interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effect of the lookup rules is simply that derived members hide base members with the same name or signature. Such single-inheritance lookups are never ambiguous. The ambiguities that can possibly arise from member lookups in multiple-inheritance interfaces are described in [§17.4.6](interfaces.md#1746-interface-member-access). +For member lookups in types other than type parameters and interfaces, and member lookups in interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effect of the lookup rules is simply that derived members hide base members with the same name or signature. Such single-inheritance lookups are never ambiguous. The ambiguities that can possibly arise from member lookups in multiple-inheritance interfaces are described in [§18.4.6](interfaces.md#1846-interface-member-access). -> *Note*: This phase only accounts for one kind of ambiguity. If the member lookup results in a method group, further uses of method group may fail due to ambiguity, for example as described in [§11.6.4.1](expressions.md#11641-general) and [§11.6.6.2](expressions.md#11662-invocations-on-boxed-instances). *end note* +> *Note*: This phase only accounts for one kind of ambiguity. If the member lookup results in a method group, further uses of method group may fail due to ambiguity, for example as described in [§12.6.4.1](expressions.md#12641-general) and [§12.6.6.2](expressions.md#12662-invocations-on-boxed-instances). *end note* -### 11.5.2 Base types +### 12.5.2 Base types For purposes of member lookup, a type `T` is considered to have the following base types: @@ -393,9 +397,9 @@ For purposes of member lookup, a type `T` is considered to have the following b - If `T` is an *array_type*, the base types of `T` are the class types `System.Array` and `object`. - If `T` is a *delegate_type*, the base types of `T` are the class types `System.Delegate` and `object`. -## 11.6 Function members +## 12.6 Function members -### 11.6.1 General +### 12.6.1 General Function members are members that contain executable statements. Function members are always members of types and cannot be members of namespaces. C# defines the following categories of function members: @@ -410,114 +414,117 @@ Function members are members that contain executable statements. Function member Except for finalizers and static constructors (which cannot be invoked explicitly), the statements contained in function members are executed through function member invocations. The actual syntax for writing a function member invocation depends on the particular function member category. -The argument list ([§11.6.2](expressions.md#1162-argument-lists)) of a function member invocation provides actual values or variable references for the parameters of the function member. +The argument list ([§12.6.2](expressions.md#1262-argument-lists)) of a function member invocation provides actual values or variable references for the parameters of the function member. -Invocations of generic methods may employ type inference to determine the set of type arguments to pass to the method. This process is described in [§11.6.3](expressions.md#1163-type-inference). +Invocations of generic methods may employ type inference to determine the set of type arguments to pass to the method. This process is described in [§12.6.3](expressions.md#1263-type-inference). -Invocations of methods, indexers, operators, and instance constructors employ overload resolution to determine which of a candidate set of function members to invoke. This process is described in [§11.6.4](expressions.md#1164-overload-resolution). +Invocations of methods, indexers, operators, and instance constructors employ overload resolution to determine which of a candidate set of function members to invoke. This process is described in [§12.6.4](expressions.md#1264-overload-resolution). -Once a particular function member has been identified at binding-time, possibly through overload resolution, the actual run-time process of invoking the function member is described in [§11.6.6](expressions.md#1166-function-member-invocation). +Once a particular function member has been identified at binding-time, possibly through overload resolution, the actual run-time process of invoking the function member is described in [§12.6.6](expressions.md#1266-function-member-invocation). > *Note*: The following table summarizes the processing that takes place in constructs involving the six categories of function members that can be explicitly invoked. In the table, `e`, `x`, `y`, and `value` indicate expressions classified as variables or values, `T` indicates an expression classified as a type, `F` is the simple name of a method, and `P` is the simple name of a property. +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +> +>
ConstructExampleDescription
Method invocationF(x, y)Overload resolution is applied to select the best method F in the containing class or struct. The method is invoked with the argument list (x, y). If the method is not static, the instance expression is this.
T.F(x, y)Overload resolution is applied to select the best method F in the class or struct T. A binding-time error occurs if the method is not static. The method is invoked with the argument list (x, y).
e.F(x, y)Overload resolution is applied to select the best method F in the class, struct, or interface given by the type of e. A binding-time error occurs if the method is static. The method is invoked with the instance expression e and the argument list (x, y).
Property accessPThe get accessor of the property P in the containing class or struct is invoked. A compile-time error occurs if P is write-only. If P is not static, the instance expression is this.
P = valueThe set accessor of the property P in the containing class or struct is invoked with the argument list (value). A compile-time error occurs if P is read-only. If P is not static, the instance expression is this.
T.PThe get accessor of the property P in the class or struct T is invoked. A compile-time error occurs if P is not static or if P is write-only.
T.P = valueThe set accessor of the property P in the class or struct T is invoked with the argument list (value). A compile-time error occurs if P is not static or if P is read-only.
e.PThe get accessor of the property P in the class, struct, or interface given by the type of E is invoked with the instance expression e. A binding-time error occurs if P is static or if P is write-only.
e.P = valueThe set accessor of the property P in the class, struct, or interface given by the type of E is invoked with the instance expression e and the argument list (value). A binding-time error occurs if P is static or if P is read-only.
Event accessE += valueThe add accessor of the event E in the containing class or struct is invoked. If E is not static, the instance expression is this.
E -= valueThe remove accessor of the event E in the containing class or struct is invoked. If E is not static, the instance expression is this.
T.E += valueThe add accessor of the event E in the class or struct T is invoked. A binding-time error occurs if E is not static.
T.E -= valueThe remove accessor of the event E in the class or struct T is invoked. A binding-time error occurs if E is not static.
e.E += valueThe add accessor of the event E in the class, struct, or interface given by the type of E is invoked with the instance expression e. A binding-time error occurs if E is static.
e.E -= valueThe remove accessor of the event E in the class, struct, or interface given by the type of E is invoked with the instance expression e. A binding-time error occurs if E is static.
Indexer accesse[x, y]Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. The get accessor of the indexer is invoked with the instance expression e and the argument list (x, y). A binding-time error occurs if the indexer is write-only.
e[x, y] = valueOverload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. The set accessor of the indexer is invoked with the instance expression e and the argument list (x, y, value). A binding-time error occurs if the indexer is read-only. +>
Operator invocation-xOverload resolution is applied to select the best unary operator in the class or struct given by the type of x. The selected operator is invoked with the argument list (x).
x + yOverload resolution is applied to select the best binary operator in the classes or structs given by the types of x and y. The selected operator is invoked with the argument list (x, y).
Instance constructor invocationnew T(x, y)Overload resolution is applied to select the best instance constructor in the class or struct T. The instance constructor is invoked with the argument list (x, y).
+> +> *end note* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConstructExampleDescription
Method invocationF(x, y)Overload resolution is applied to select the best method F in the containing class or struct. The method is invoked with the argument list (x, y). If the method is not static, the instance expression is this.
T.F(x, y)Overload resolution is applied to select the best method F in the class or struct T. A binding-time error occurs if the method is not static. The method is invoked with the argument list (x, y).
e.F(x, y)Overload resolution is applied to select the best method F in the class, struct, or interface given by the type of e. A binding-time error occurs if the method is static. The method is invoked with the instance expression e and the argument list (x, y).
Property accessPThe get accessor of the property P in the containing class or struct is invoked. A compile-time error occurs if P is write-only. If P is not static, the instance expression is this.
P = valueThe set accessor of the property P in the containing class or struct is invoked with the argument list (value). A compile-time error occurs if P is read-only. If P is not static, the instance expression is this.
T.PThe get accessor of the property P in the class or struct T is invoked. A compile-time error occurs if P is not static or if P is write-only.
T.P = valueThe set accessor of the property P in the class or struct T is invoked with the argument list (value). A compile-time error occurs if P is not static or if P is read-only.
e.PThe get accessor of the property P in the class, struct, or interface given by the type of E is invoked with the instance expression e. A binding-time error occurs if P is static or if P is write-only.
e.P = valueThe set accessor of the property P in the class, struct, or interface given by the type of E is invoked with the instance expression e and the argument list (value). A binding-time error occurs if P is static or if P is read-only.
Event accessE += valueThe add accessor of the event E in the containing class or struct is invoked. If E is not static, the instance expression is this.
E -= valueThe remove accessor of the event E in the containing class or struct is invoked. If E is not static, the instance expression is this.
T.E += valueThe add accessor of the event E in the class or struct T is invoked. A binding-time error occurs if E is not static.
T.E -= valueThe remove accessor of the event E in the class or struct T is invoked. A binding-time error occurs if E is not static.
e.E += valueThe add accessor of the event E in the class, struct, or interface given by the type of E is invoked with the instance expression e. A binding-time error occurs if E is static.
e.E -= valueThe remove accessor of the event E in the class, struct, or interface given by the type of E is invoked with the instance expression e. A binding-time error occurs if E is static.
Indexer accesse[x, y]Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. The get accessor of the indexer is invoked with the instance expression e and the argument list (x, y). A binding-time error occurs if the indexer is write-only.
e[x, y] = valueOverload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. The set accessor of the indexer is invoked with the instance expression e and the argument list (x, y, value). A binding-time error occurs if the indexer is read-only.
Operator invocation-xOverload resolution is applied to select the best unary operator in the class or struct given by the type of x. The selected operator is invoked with the argument list (x).
x + yOverload resolution is applied to select the best binary operator in the classes or structs given by the types of x and y. The selected operator is invoked with the argument list (x, y).
Instance constructor invocationnew T(x, y)Overload resolution is applied to select the best instance constructor in the class or struct T. The instance constructor is invoked with the argument list (x, y).
- -### 11.6.2 Argument lists - -#### 11.6.2.1 General +### 12.6.2 Argument lists + +#### 12.6.2.1 General Every function member and delegate invocation includes an argument list, which provides actual values or variable references for the parameters of the function member. The syntax for specifying the argument list of a function member invocation depends on the function member category: @@ -527,7 +534,7 @@ Every function member and delegate invocation includes an argument list, which p - For events, the argument list consists of the expression specified as the right operand of the `+=` or `-=` operator. - For user-defined operators, the argument list consists of the single operand of the unary operator or the two operands of the binary operator. -The arguments of properties ([§14.7](classes.md#147-properties)), events ([§14.8](classes.md#148-events)), and user-defined operators ([§14.10](classes.md#1410-operators)) are always passed as value parameters ([§14.6.2.2](classes.md#14622-value-parameters)). The arguments of indexers ([§14.9](classes.md#149-indexers)) are always passed as value parameters ([§14.6.2.2](classes.md#14622-value-parameters)) or parameter arrays ([§14.6.2.5](classes.md#14625-parameter-arrays)). Reference and output parameters are not supported for these categories of function members. +The arguments of properties ([§15.7](classes.md#157-properties)) and events ([§15.8](classes.md#158-events)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)). The arguments of user-defined operators ([§15.10](classes.md#1510-operators)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)) or input parameters ([§9.2.8](variables.md#928-input-parameters)). The arguments of indexers ([§15.9](classes.md#159-indexers)) are always passed as value parameters ([§15.6.2.2](classes.md#15622-value-parameters)), input parameters ([§9.2.8](variables.md#928-input-parameters)), or parameter arrays ([§15.6.2.6](classes.md#15626-parameter-arrays)). Output and reference parameters are not supported for these categories of function members. The arguments of an instance constructor, method, indexer, or delegate invocation are specified as an *argument_list*: @@ -546,6 +553,7 @@ argument_name argument_value : expression + | 'in' variable_reference | 'ref' variable_reference | 'out' variable_reference ; @@ -555,15 +563,16 @@ An *argument_list* consists of one or more *argument*s, separated by commas. Eac The *argument_value* can take one of the following forms: -- An *expression*, indicating that the argument is passed as a value parameter ([§14.6.2.2](classes.md#14622-value-parameters)). -- The keyword `ref` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as a reference parameter ([§14.6.2.3](classes.md#14623-reference-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as a reference parameter. -- The keyword `out` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an output parameter ([§14.6.2.4](classes.md#14624-output-parameters)). A variable is considered definitely assigned ([§9.4](variables.md#94-definite-assignment)) following a function member invocation in which the variable is passed as an output parameter. +- An *expression*, indicating that the argument is passed as a value parameter or is transformed into an input parameter and then passed as that, as determined by ([§12.6.4.2](expressions.md#12642-applicable-function-member) and described in [§12.6.2.3](expressions.md#12623-run-time-evaluation-of-argument-lists). +- The keyword `in` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an input parameter ([§15.6.2.3](classes.md#15623-input-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter. +- The keyword `ref` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as a reference parameter ([§15.6.2.4](classes.md#15624-reference-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as a reference parameter. +- The keyword `out` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an output parameter ([§15.6.2.5](classes.md#15625-output-parameters)). A variable is considered definitely assigned ([§9.4](variables.md#94-definite-assignment)) following a function member invocation in which the variable is passed as an output parameter. -The form determines the ***parameter-passing mode*** of the argument: *value*, *reference*, or *output*, respectively. +The form determines the ***parameter-passing mode*** of the argument: *value*, *input*, *reference*, or *output*, respectively. However, as mentioned above, an argument with value passing mode, might be transformed into one with input passing mode. -Passing a volatile field ([§14.5.4](classes.md#1454-volatile-fields)) as a reference parameter or output parameter causes a warning, since the field may not be treated as volatile by the invoked method. +Passing a volatile field ([§15.5.4](classes.md#1554-volatile-fields)) as an input, output, or reference parameter causes a warning, since the field may not be treated as volatile by the invoked method. -#### 11.6.2.2 Corresponding parameters +#### 12.6.2.2 Corresponding parameters For each argument in an argument list there has to be a corresponding parameter in the function member or delegate being invoked. @@ -588,14 +597,31 @@ The corresponding parameters for function member arguments are established as fo - An unnamed argument corresponds to no parameter when it is after an out-of-position named argument or a named argument that corresponds to a parameter array. > *Note*: This prevents `void M(bool a = true, bool b = true, bool c = true);` being invoked by `M(c: false, valueB);`. The first argument is used out-of-position (the argument is used in first position, but the parameter named `c` is in third position), so the following arguments should be named. In other words, non-trailing named arguments are only allowed when the name and the position result in finding the same corresponding parameter. *end note* -#### 11.6.2.3 Run-time evaluation of argument lists +#### 12.6.2.3 Run-time evaluation of argument lists + +During the run-time processing of a function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows: -During the run-time processing of a function member invocation ([§11.6.6](expressions.md#1166-function-member-invocation)), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows: +- For a value argument, if the parameter’s passing mode is value + - the argument expression is evaluated and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to the corresponding parameter type is performed. The resulting value becomes the initial value of the value parameter in the function member invocation. + - otherwise, the parameter’s passing mode is input. If the argument is a variable reference and there exists an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) between the argument’s type and the parameter’s type, the resulting storage location becomes the storage location represented by the parameter in the function member invocation. Otherwise, a storage location is created with the same type as that of the corresponding parameter. The argument expression is evaluated and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to the corresponding parameter type is performed. The resulting value is stored within that storage location. That storage location is represented by the input parameter in the function member invocation. -- For a value parameter, the argument expression is evaluated and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) to the corresponding parameter type is performed. The resulting value becomes the initial value of the value parameter in the function member invocation. -- For a reference or output parameter, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. If the variable reference given as a reference or output parameter is an array element of a *reference_type*, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. If this check fails, a `System.ArrayTypeMismatchException` is thrown. + > *Example*: Given the following declarations and method calls: + > + > + > ```csharp + > static void M1(in int p1) { ... } + > int i = 10; + > M1(i); // i is passed as an input argument + > M1(i + 5); // transformed to a temporary input argument + > ``` + > + > In the `M1(i)` method call, `i` itself is passed as an input argument, because it is classified as a variable and has the same type `int` as the input parameter. In the `M1(i + 5)` method call, an unnamed `int` variable is created, initialized with the argument’s value, and then passed as an input argument. See [§12.6.4.2](expressions.md#12642-applicable-function-member) and [§12.6.4.4](expressions.md#12644-better-parameter-passing-mode). + > + > *end example* -Methods, indexers, and instance constructors may declare their right-most parameter to be a parameter array ([§14.6.2.5](classes.md#14625-parameter-arrays)). Such function members are invoked either in their normal form or in their expanded form depending on which is applicable ([§11.6.4.2](expressions.md#11642-applicable-function-member)): +- For an input, output, or reference argument, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. For an input or reference argument, the variable must be definitely assigned at the point of the method call. If the variable reference given as an output, or reference is an array element of a *reference_type*, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. If this check fails, a `System.ArrayTypeMismatchException` is thrown. + +Methods, indexers, and instance constructors may declare their right-most parameter to be a parameter array ([§15.6.2.6](classes.md#15626-parameter-arrays)). Such function members are invoked either in their normal form or in their expanded form depending on which is applicable ([§12.6.4.2](expressions.md#12642-applicable-function-member)): - When a function member with a parameter array is invoked in its normal form, the argument given for the parameter array shall be a single expression that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the parameter array type. In this case, the parameter array acts precisely like a value parameter. - When a function member with a parameter array is invoked in its expanded form, the invocation shall specify zero or more positional arguments for the parameter array, where each argument is an expression that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the element type of the parameter array. In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument. @@ -629,7 +655,7 @@ The expressions of an argument list are always evaluated in textual order. > > *end example* -The array co-variance rules ([§16.6](arrays.md#166-array-covariance)) permit a value of an array type `A[]` to be a reference to an instance of an array type `B[]`, provided an implicit reference conversion exists from `B` to `A`. Because of these rules, when an array element of a *reference_type* is passed as a reference or output parameter, a run-time check is required to ensure that the actual element type of the array is *identical* to that of the parameter. +The array co-variance rules ([§17.6](arrays.md#176-array-covariance)) permit a value of an array type `A[]` to be a reference to an instance of an array type `B[]`, provided an implicit reference conversion exists from `B` to `A`. Because of these rules, when an array element of a *reference_type* is passed as an output or reference argument, a run-time check is required to ensure that the actual element type of the array is *identical* to that of the parameter. > *Example*: In the following code > @@ -653,7 +679,7 @@ The array co-variance rules ([§16.6](arrays.md#166-array-covariance)) permit a > > *end example* -When a function member with a parameter array is invoked in its expanded form with at least one expanded argument, the invocation is processed as if an array creation expression with an array initializer ([§11.7.15.5](expressions.md#117155-array-creation-expressions)) was inserted around the expanded arguments. An empty array is passed when there are no arguments for the parameter array; it is unspecified whether the reference passed is to a newly allocated or existing empty array. +When a function member with a parameter array is invoked in its expanded form with at least one expanded argument, the invocation is processed as if an array creation expression with an array initializer ([§12.8.16.5](expressions.md#128165-array-creation-expressions)) was inserted around the expanded arguments. An empty array is passed when there are no arguments for the parameter array; it is unspecified whether the reference passed is to a newly allocated or existing empty array. > *Example*: Given the declaration > @@ -677,13 +703,13 @@ When a function member with a parameter array is invoked in its expanded form wi > > *end example* -When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. +When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. (This can involve the creation of a storage location, as described above.) > *Note*: Because these are always constant, their evaluation will not impact the evaluation of the remaining arguments. *end note* -### 11.6.3 Type inference +### 12.6.3 Type inference -#### 11.6.3.1 General +#### 12.6.3.1 General When a generic method is called without specifying type arguments, a ***type inference*** process attempts to infer type arguments for the call. The presence of type inference allows a more convenient syntax to be used for calling a generic method, and allows the programmer to avoid specifying redundant type information. @@ -713,9 +739,9 @@ When a generic method is called without specifying type arguments, a ***type inf > > *end example* -Type inference occurs as part of the binding-time processing of a method invocation ([§11.7.8.2](expressions.md#11782-method-invocations)) and takes place before the overload resolution step of the invocation. When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution. If overload resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the type arguments for the invocation. If type inference for a particular method fails, that method does not participate in overload resolution. The failure of type inference, in and of itself, does not cause a binding-time error. However, it often leads to a binding-time error when overload resolution then fails to find any applicable methods. +Type inference occurs as part of the binding-time processing of a method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)) and takes place before the overload resolution step of the invocation. When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution. If overload resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the type arguments for the invocation. If type inference for a particular method fails, that method does not participate in overload resolution. The failure of type inference, in and of itself, does not cause a binding-time error. However, it often leads to a binding-time error when overload resolution then fails to find any applicable methods. -If each supplied argument does not correspond to exactly one parameter in the method ([§11.6.2.2](expressions.md#11622-corresponding-parameters)), or there is a non-optional parameter with no corresponding argument, then inference immediately fails. Otherwise, assume that the generic method has the following signature: +If each supplied argument does not correspond to exactly one parameter in the method ([§12.6.2.2](expressions.md#12622-corresponding-parameters)), or there is a non-optional parameter with no corresponding argument, then inference immediately fails. Otherwise, assume that the generic method has the following signature: `Tₑ M(T₁ p₁ ... Tₓ pₓ)` @@ -727,59 +753,61 @@ During the process of inference each type parameter `Xᵢ` is either *fixed* to Type inference takes place in phases. Each phase will try to infer type arguments for more type variables based on the findings of the previous phase. The first phase makes some initial inferences of bounds, whereas the second phase fixes type variables to specific types and infers further bounds. The second phase may have to be repeated a number of times. -> *Note*: Type inference is also used in other contexts including for conversion of method groups ([§11.6.3.14](expressions.md#116314-type-inference-for-conversion-of-method-groups)) and finding the best common type of a set of expressions ([§11.6.3.15](expressions.md#116315-finding-the-best-common-type-of-a-set-of-expressions)). *end note* +> *Note*: Type inference is also used in other contexts including for conversion of method groups ([§12.6.3.14](expressions.md#126314-type-inference-for-conversion-of-method-groups)) and finding the best common type of a set of expressions ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)). *end note* -#### 11.6.3.2 The first phase +#### 12.6.3.2 The first phase For each of the method arguments `Eᵢ`: -- If `Eᵢ` is an anonymous function, an *explicit parameter type inference* ([§11.6.3.8](expressions.md#11638-explicit-parameter-type-inferences)) is made *from* `Eᵢ` *to* `Tᵢ` -- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a value parameter ([§14.6.2.2](classes.md#14622-value-parameters)) then a *lower-bound inference* ([§11.6.3.10](expressions.md#116310-lower-bound-inferences)) is made *from* `U` *to* `Tᵢ`. -- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a reference ([§14.6.2.3](classes.md#14623-reference-parameters)) or output ([§14.6.2.4](classes.md#14624-output-parameters)) parameter then an *exact inference* ([§11.6.3.9](expressions.md#11639-exact-inferences)) is made *from* `U` *to* `Tᵢ`. +- If `Eᵢ` is an anonymous function, an *explicit parameter type inference* ([§12.6.3.8](expressions.md#12638-explicit-parameter-type-inferences)) is made *from* `Eᵢ` *to* `Tᵢ` +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a value parameter ([§15.6.2.2](classes.md#15622-value-parameters)) then a *lower-bound inference* ([§12.6.3.10](expressions.md#126310-lower-bound-inferences)) is made *from* `U` *to* `Tᵢ`. +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is a reference parameter ([§15.6.2.4](classes.md#15624-reference-parameters)), or output parameter ([§15.6.2.5](classes.md#15625-output-parameters)) then an *exact inference* ([§12.6.3.9](expressions.md#12639-exact-inferences)) is made *from* `U` *to* `Tᵢ`. +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is an input parameter ([§15.6.2.3](classes.md#15623-input-parameters)) and `Ei` is an input argument, then an *exact inference* ([§12.6.3.9](expressions.md#12639-exact-inferences)) is made *from* `U` *to* `Tᵢ`. +- Otherwise, if `Eᵢ` has a type `U` and `xᵢ` is an input parameter ([§15.6.2.3](classes.md#15623-input-parameters)) then a *lower bound inference* ([§12.6.3.10](expressions.md#126310-lower-bound-inferences)) is made *from* `U` *to* `Tᵢ`. - Otherwise, no inference is made for this argument. -#### 11.6.3.3 The second phase +#### 12.6.3.3 The second phase The second phase proceeds as follows: -- All *unfixed* type variables `Xᵢ` which do not *depend on* ([§11.6.3.6](expressions.md#11636-dependence)) any `Xₑ` are fixed ([§11.6.3.12](expressions.md#116312-fixing)). +- All *unfixed* type variables `Xᵢ` which do not *depend on* ([§12.6.3.6](expressions.md#12636-dependence)) any `Xₑ` are fixed ([§12.6.3.12](expressions.md#126312-fixing)). - If no such type variables exist, all *unfixed* type variables `Xᵢ` are *fixed* for which all of the following hold: - There is at least one type variable `Xₑ` that *depends on* `Xᵢ` - `Xᵢ` has a non-empty set of bounds - If no such type variables exist and there are still *unfixed* type variables, type inference fails. - Otherwise, if no further *unfixed* type variables exist, type inference succeeds. -- Otherwise, for all arguments `Eᵢ` with corresponding parameter type `Tᵢ` where the *output types* ([§11.6.3.5](expressions.md#11635-output-types)) contain *unfixed* type variables `Xₑ` but the *input types* ([§11.6.3.4](expressions.md#11634-input-types)) do not, an *output type inference* ([§11.6.3.7](expressions.md#11637-output-type-inferences)) is made *from* `Eᵢ` *to* `Tᵢ`. Then the second phase is repeated. +- Otherwise, for all arguments `Eᵢ` with corresponding parameter type `Tᵢ` where the *output types* ([§12.6.3.5](expressions.md#12635-output-types)) contain *unfixed* type variables `Xₑ` but the *input types* ([§12.6.3.4](expressions.md#12634-input-types)) do not, an *output type inference* ([§12.6.3.7](expressions.md#12637-output-type-inferences)) is made *from* `Eᵢ` *to* `Tᵢ`. Then the second phase is repeated. -#### 11.6.3.4 Input types +#### 12.6.3.4 Input types If `E` is a method group or implicitly typed anonymous function and `T` is a delegate type or expression tree type then all the parameter types of `T` are *input types of* `E` *with type* `T`. -#### 11.6.3.5 Output types +#### 12.6.3.5 Output types If `E` is a method group or an anonymous function and `T` is a delegate type or expression tree type then the return type of `T` is an *output type of* `E` *with type* `T`. -#### 11.6.3.6 Dependence +#### 12.6.3.6 Dependence An *unfixed* type variable `Xᵢ` *depends directly on* an *unfixed* type variable `Xₑ` if for some argument `Eᵥ` with type `Tᵥ` `Xₑ` occurs in an *input type* of `Eᵥ` with type `Tᵥ` and `Xᵢ` occurs in an *output type* of `Eᵥ` with type `Tᵥ`. `Xₑ` *depends on* `Xᵢ` if `Xₑ` *depends directly on* `Xᵢ` or if `Xᵢ` *depends directly on* `Xᵥ` and `Xᵥ` *depends on* `Xₑ`. Thus “*depends on*” is the transitive but not reflexive closure of “*depends directly on*”. -#### 11.6.3.7 Output type inferences +#### 12.6.3.7 Output type inferences An *output type inference* is made *from* an expression `E` *to* a type T in the following way: -- If `E` is an anonymous function with inferred return type `U` ([§11.6.3.13](expressions.md#116313-inferred-return-type)) and `T` is a delegate type or expression tree type with return type `Tₓ`, then a *lower-bound inference* ([§11.6.3.10](expressions.md#116310-lower-bound-inferences)) is made *from* `U` *to* `Tₓ`. +- If `E` is an anonymous function with inferred return type `U` ([§12.6.3.13](expressions.md#126313-inferred-return-type)) and `T` is a delegate type or expression tree type with return type `Tₓ`, then a *lower-bound inference* ([§12.6.3.10](expressions.md#126310-lower-bound-inferences)) is made *from* `U` *to* `Tₓ`. - Otherwise, if `E` is a method group and `T` is a delegate type or expression tree type with parameter types `T₁...Tᵥ` and return type `Tₓ`, and overload resolution of `E` with the types `T₁...Tᵥ` yields a single method with return type `U`, then a *lower-bound inference* is made *from* `U` *to* `Tₓ`. - Otherwise, if `E` is an expression with type `U`, then a *lower-bound inference* is made *from* `U` *to* `T`. - Otherwise, no inferences are made. -#### 11.6.3.8 Explicit parameter type inferences +#### 12.6.3.8 Explicit parameter type inferences An *explicit parameter type inference* is made *from* an expression `E` *to* a type `T` in the following way: -- If `E` is an explicitly typed anonymous function with parameter types `U₁...Uᵥ` and `T` is a delegate type or expression tree type with parameter types `V₁...Vᵥ` then for each `Uᵢ` an *exact inference* ([§11.6.3.9](expressions.md#11639-exact-inferences)) is made *from* `Uᵢ` *to* the corresponding `Vᵢ`. +- If `E` is an explicitly typed anonymous function with parameter types `U₁...Uᵥ` and `T` is a delegate type or expression tree type with parameter types `V₁...Vᵥ` then for each `Uᵢ` an *exact inference* ([§12.6.3.9](expressions.md#12639-exact-inferences)) is made *from* `Uᵢ` *to* the corresponding `Vᵢ`. -#### 11.6.3.9 Exact inferences +#### 12.6.3.9 Exact inferences An *exact inference* *from* a type `U` *to* a type `V` is made as follows: @@ -791,7 +819,7 @@ An *exact inference* *from* a type `U` *to* a type `V` is made as follows: If any of these cases apply then an *exact inference* is made from each `Uᵢ` to the corresponding `Vᵢ`. - Otherwise, no inferences are made. -#### 11.6.3.10 Lower-bound inferences +#### 12.6.3.10 Lower-bound inferences A *lower-bound inference from* a type `U` *to* a type `V` is made as follows: @@ -811,7 +839,7 @@ A *lower-bound inference from* a type `U` *to* a type `V` is made as follows: - If it is invariant then an *exact inference* is made. - Otherwise, no inferences are made. -#### 11.6.3.11 Upper-bound inferences +#### 12.6.3.11 Upper-bound inferences An *upper-bound inference from* a type `U` *to* a type `V` is made as follows: @@ -821,7 +849,7 @@ An *upper-bound inference from* a type `U` *to* a type `V` is made as follows: - `U` is one of `IEnumerable`, `ICollection`, `IReadOnlyList`, `IReadOnlyCollection` or `IList` and `V` is a single-dimensional array type `Vₑ[]` - `U` is the type `U1?` and `V` is the type `V1?` - `U` is constructed class, struct, interface or delegate type `C` and `V` is a `class, struct, interface` or `delegate` type which is `identical` to, `inherits` from (directly or indirectly), or implements (directly or indirectly) a unique type `C` - - (The “uniqueness” restriction means that if we have interface `C{} class V: C>, C>{}`, then no inference is made when inferring from `C` to `V`. Inferences are not made from `U₁` to either `X` or `Y`.) + - (The “uniqueness” restriction means that given an interface `C{} class V: C>, C>{}`, then no inference is made when inferring from `C` to `V`. Inferences are not made from `U₁` to either `X` or `Y`.) If any of these cases apply then an inference is made from each `Uᵢ` to the corresponding `Vᵢ` as follows: - If `Uᵢ` is not known to be a reference type then an *exact inference* is made - Otherwise, if `V` is an array type then an *upper-bound inference* is made @@ -831,29 +859,29 @@ An *upper-bound inference from* a type `U` *to* a type `V` is made as follows: - If it is invariant then an *exact inference* is made. - Otherwise, no inferences are made. -#### 11.6.3.12 Fixing +#### 12.6.3.12 Fixing An *unfixed* type variable `Xᵢ` with a set of bounds is *fixed* as follows: - The set of *candidate types* `Uₑ` starts out as the set of all types in the set of bounds for `Xᵢ`. -- We then examine each bound for `Xᵢ` in turn: For each exact bound U of `Xᵢ` all types `Uₑ` that are not identical to `U` are removed from the candidate set. For each lower bound `U` of `Xᵢ` all types `Uₑ` to which there is *not* an implicit conversion from `U` are removed from the candidate set. For each upper-bound U of `Xᵢ` all types `Uₑ` from which there is *not* an implicit conversion to `U` are removed from the candidate set. +- Each bound for `Xᵢ` is examined in turn: For each exact bound U of `Xᵢ` all types `Uₑ` that are not identical to `U` are removed from the candidate set. For each lower bound `U` of `Xᵢ` all types `Uₑ` to which there is *not* an implicit conversion from `U` are removed from the candidate set. For each upper-bound U of `Xᵢ` all types `Uₑ` from which there is *not* an implicit conversion to `U` are removed from the candidate set. - If among the remaining candidate types `Uₑ` there is a unique type `V` to which there is an implicit conversion from all the other candidate types, then `Xᵢ` is fixed to `V`. - Otherwise, type inference fails. -#### 11.6.3.13 Inferred return type +#### 12.6.3.13 Inferred return type The inferred return type of an anonymous function `F` is used during type inference and overload resolution. The inferred return type can only be determined for an anonymous function where all parameter types are known, either because they are explicitly given, provided through an anonymous function conversion or inferred during type inference on an enclosing generic method invocation. The ***inferred effective return type*** is determined as follows: - If the body of `F` is an *expression* that has a type, then the inferred effective return type of `F` is the type of that expression. -- If the body of `F` is a *block* and the set of expressions in the block’s `return` statements has a best common type `T` ([§11.6.3.15](expressions.md#116315-finding-the-best-common-type-of-a-set-of-expressions)), then the inferred effective return type of `F` is `T`. +- If the body of `F` is a *block* and the set of expressions in the block’s `return` statements has a best common type `T` ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)), then the inferred effective return type of `F` is `T`. - Otherwise, an effective return type cannot be inferred for `F`. The ***inferred return type*** is determined as follows: -- If `F` is async and the body of `F` is either an expression classified as nothing ([§11.2](expressions.md#112-expression-classifications)), or a block where no `return` statements have expressions, the inferred return type is `System.Threading.Tasks.Task`. -- If `F` is async and has an inferred effective return type `T`, the inferred return type is `System.Threading.Tasks.Task`. +- If `F` is async and the body of `F` is either an expression classified as nothing ([§12.2](expressions.md#122-expression-classifications)), or a block where no `return` statements have expressions, the inferred return type is `«TaskType»` ([§15.15.1](classes.md#15151-general)). +- If `F` is async and has an inferred effective return type `T`, the inferred return type is `«TaskType»»`([§15.15.1](classes.md#15151-general)). - If `F` is non-async and has an inferred effective return type `T`, the inferred return type is `T`. - Otherwise, a return type cannot be inferred for `F`. @@ -885,7 +913,7 @@ The ***inferred return type*** is determined as follows: > IEnumerable names = customers.Select(c => c.Name); > ``` > -> The extension method invocation ([§11.7.8.3](expressions.md#11783-extension-method-invocations)) of `Select` is processed by rewriting the invocation to a static method invocation: +> The extension method invocation ([§12.8.9.3](expressions.md#12893-extension-method-invocations)) of `Select` is processed by rewriting the invocation to a static method invocation: > > ```csharp > IEnumerable names = Enumerable.Select(customers, c => c.Name); @@ -912,16 +940,16 @@ The ***inferred return type*** is determined as follows: > > static void M() > { -> double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds); +> double hours = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalHours); > } > } > ``` > -> type inference for the invocation proceeds as follows: First, the argument “1:15:30” is related to the value parameter, inferring `X` to be string. Then, the parameter of the first anonymous function, `s`, is given the inferred type `string`, and the expression `TimeSpan.Parse(s)` is related to the return type of `f1`, inferring `Y` to be `System.TimeSpan`. Finally, the parameter of the second anonymous function, `t`, is given the inferred type `System.TimeSpan`, and the expression `t.TotalSeconds` is related to the return type of `f2`, inferring `Z` to be `double`. Thus, the result of the invocation is of type `double`. +> type inference for the invocation proceeds as follows: First, the argument “1:15:30” is related to the value parameter, inferring `X` to be string. Then, the parameter of the first anonymous function, `s`, is given the inferred type `string`, and the expression `TimeSpan.Parse(s)` is related to the return type of `f1`, inferring `Y` to be `System.TimeSpan`. Finally, the parameter of the second anonymous function, `t`, is given the inferred type `System.TimeSpan`, and the expression `t.TotalHours` is related to the return type of `f2`, inferring `Z` to be `double`. Thus, the result of the invocation is of type `double`. > > *end example* -#### 11.6.3.14 Type inference for conversion of method groups +#### 12.6.3.14 Type inference for conversion of method groups Similar to calls of generic methods, type inference shall also be applied when a method group `M` containing a generic method is converted to a given delegate type `D` ([§10.8](conversions.md#108-method-group-conversions)). Given a method @@ -931,78 +959,108 @@ and the method group `M` being assigned to the delegate type `D` the task of t `M` -becomes compatible ([§19.2](delegates.md#192-delegate-declarations)) with `D`. +becomes compatible ([§20.2](delegates.md#202-delegate-declarations)) with `D`. Unlike the type inference algorithm for generic method calls, in this case, there are only argument *types*, no argument *expressions*. In particular, there are no anonymous functions and hence no need for multiple phases of inference. Instead, all `Xᵢ` are considered *unfixed*, and a *lower-bound inference* is made *from* each argument type `Uₑ` of `D` *to* the corresponding parameter type `Tₑ` of `M`. If for any of the `Xᵢ` no bounds were found, type inference fails. Otherwise, all `Xᵢ` are *fixed* to corresponding `Sᵢ`, which are the result of type inference. -#### 11.6.3.15 Finding the best common type of a set of expressions +#### 12.6.3.15 Finding the best common type of a set of expressions In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and the return types of anonymous functions with *block* bodies are found in this way. The best common type for a set of expressions `E₁...Eᵥ` is determined as follows: - A new *unfixed* type variable `X` is introduced. -- For each expression `Ei` an *output type inference* ([§11.6.3.7](expressions.md#11637-output-type-inferences)) is performed from it to `X`. -- `X` is *fixed* ([§11.6.3.12](expressions.md#116312-fixing)), if possible, and the resulting type is the best common type. +- For each expression `Ei` an *output type inference* ([§12.6.3.7](expressions.md#12637-output-type-inferences)) is performed from it to `X`. +- `X` is *fixed* ([§12.6.3.12](expressions.md#126312-fixing)), if possible, and the resulting type is the best common type. - Otherwise inference fails. > *Note*: Intuitively this inference is equivalent to calling a method `void M(X x₁ ... X xᵥ)` with the `Eᵢ` as arguments and inferring `X`. *end note* -### 11.6.4 Overload resolution +### 12.6.4 Overload resolution -#### 11.6.4.1 General +#### 12.6.4.1 General Overload resolution is a binding-time mechanism for selecting the best function member to invoke given an argument list and a set of candidate function members. Overload resolution selects the function member to invoke in the following distinct contexts within C#: -- Invocation of a method named in an *invocation_expression* ([§11.7.8](expressions.md#1178-invocation-expressions)). -- Invocation of an instance constructor named in an *object_creation_expression* ([§11.7.15.2](expressions.md#117152-object-creation-expressions)). -- Invocation of an indexer accessor through an *element_access* ([§11.7.10](expressions.md#11710-element-access)). -- Invocation of a predefined or user-defined operator referenced in an expression ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution) and [§11.4.5](expressions.md#1145-binary-operator-overload-resolution)). +- Invocation of a method named in an *invocation_expression* ([§12.8.9](expressions.md#1289-invocation-expressions)). +- Invocation of an instance constructor named in an *object_creation_expression* ([§12.8.16.2](expressions.md#128162-object-creation-expressions)). +- Invocation of an indexer accessor through an *element_access* ([§12.8.11](expressions.md#12811-element-access)). +- Invocation of a predefined or user-defined operator referenced in an expression ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution) and [§12.4.5](expressions.md#1245-binary-operator-overload-resolution)). -Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way. For instance, the set of candidates for a method invocation does not include methods marked override ([§11.5](expressions.md#115-member-lookup)), and methods in a base class are not candidates if any method in a derived class is applicable ([§11.7.8.2](expressions.md#11782-method-invocations)). +Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way. For instance, the set of candidates for a method invocation does not include methods marked override ([§12.5](expressions.md#125-member-lookup)), and methods in a base class are not candidates if any method in a derived class is applicable ([§12.8.9.2](expressions.md#12892-method-invocations)). Once the candidate function members and the argument list have been identified, the selection of the best function member is the same in all cases: -- First, the set of candidate function members is reduced to those function members that are applicable with respect to the given argument list ([§11.6.4.2](expressions.md#11642-applicable-function-member)). If this reduced set is empty, a compile-time error occurs. -- Then, the best function member from the set of applicable candidate function members is located. If the set contains only one function member, then that function member is the best function member. Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in [§11.6.4.3](expressions.md#11643-better-function-member). If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a binding-time error occurs. +- First, the set of candidate function members is reduced to those function members that are applicable with respect to the given argument list ([§12.6.4.2](expressions.md#12642-applicable-function-member)). If this reduced set is empty, a compile-time error occurs. +- Then, the best function member from the set of applicable candidate function members is located. If the set contains only one function member, then that function member is the best function member. Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in [§12.6.4.3](expressions.md#12643-better-function-member). If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a binding-time error occurs. The following subclauses define the exact meanings of the terms *applicable function member* and *better function member*. -#### 11.6.4.2 Applicable function member +#### 12.6.4.2 Applicable function member A function member is said to be an ***applicable function member*** with respect to an argument list `A` when all of the following are true: -- Each argument in `A` corresponds to a parameter in the function member declaration as described in [§11.6.2.2](expressions.md#11622-corresponding-parameters), at most one argument corresponds to each parameter, and any parameter to which no argument corresponds is an optional parameter. +- Each argument in `A` corresponds to a parameter in the function member declaration as described in [§12.6.2.2](expressions.md#12622-corresponding-parameters), at most one argument corresponds to each parameter, and any parameter to which no argument corresponds is an optional parameter. - For each argument in `A`, the parameter-passing mode of the argument is identical to the parameter-passing mode of the corresponding parameter, and - for a value parameter or a parameter array, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter, or - - for a `ref` or `out` parameter, there is an identity conversion between the type of the argument expression and the type of the corresponding parameter + - for a `ref` or `out` parameter, there is an identity conversion between the type of the argument expression (if any) and the type of the corresponding parameter + - for an `in` parameter when the corresponding argument has the `in` modifier, there is an identity conversion between the type of the argument expression (if any) and the type of the corresponding parameter + - for an `in` parameter when the corresponding argument omits the `in` modifier, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter. For a function member that includes a parameter array, if the function member is applicable by the above rules, it is said to be applicable in its ***normal form***. If a function member that includes a parameter array is not applicable in its normal form, the function member might instead be applicable in its ***expanded form***: - The expanded form is constructed by replacing the parameter array in the function member declaration with zero or more value parameters of the element type of the parameter array such that the number of arguments in the argument list `A` matches the total number of parameters. If `A` has fewer arguments than the number of fixed parameters in the function member declaration, the expanded form of the function member cannot be constructed and is thus not applicable. -- Otherwise, the expanded form is applicable if for each argument in `A` the parameter-passing mode of the argument is identical to the parameter-passing mode of the corresponding parameter, and - - for a fixed value parameter or a value parameter created by the expansion, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter, or - - for a `ref` or `out` parameter, the type of the argument expression is identical to the type of the corresponding parameter. +- Otherwise, the expanded form is applicable if for each argument in `A`, one of the following is true: + - the parameter-passing mode of the argument is identical to the parameter-passing mode of the corresponding parameter, and + - for a fixed value parameter or a value parameter created by the expansion, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter, or + - for an `in`, `out`, or `ref` parameter, the type of the argument expression is identical to the type of the corresponding parameter. + - the parameter-passing mode of the argument is value, and the parameter-passing mode of the corresponding parameter is input, and an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from the argument expression to the type of the corresponding parameter -Additional rules determine whether a method is applicable or not based on the context of the expression: +When the implicit conversion from the argument type to the parameter type of an `in` parameter is a dynamic implicit conversion ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions)), the results are undefined. +> *Example*: Given the following declarations and method calls: +> +> +> ```csharp +> public static void M1(int p1) { ... } +> public static void M1(in int p1) { ... } +> public static void M2(in int p1) { ... } +> public static void Test() +> { +> int i = 10; uint ui = 34U; +> +> M1(in i); // M1(in int) is applicable +> M1(in ui); // no exact type match, so M1(in int) is not applicable +> M1(i); // M1(int) and M1(in int) are applicable +> M1(i + 5); // M1(int) and M1(in int) are applicable +> M1(100u); // no implicit conversion exists, so M1(int) is not applicable +> +> M2(in i); // M2(in int) is applicable +> M2(i); // M2(in int) is applicable +> M2(i + 5); // M2(in int) is applicable +> } +> ``` +> +> *end example* + - A static method is only applicable if the method group results from a *simple_name* or a *member_access* through a type. - An instance method is only applicable if the method group results from a *simple_name*, a *member_access* through a variable or value, or a *base_access*. - - If the method group results from a *simple_name*, an instance method is only applicable if `this` access is permitted [§11.7.12](expressions.md#11712-this-access). -- When the method group results from a *member_access* which could be via either an instance or a type as described in [§11.7.6.2](expressions.md#11762-identical-simple-names-and-type-names), both instance and static methods are applicable. + - If the method group results from a *simple_name*, an instance method is only applicable if `this` access is permitted [§12.8.13](expressions.md#12813-this-access). +- When the method group results from a *member_access* which could be via either an instance or a type as described in [§12.8.7.2](expressions.md#12872-identical-simple-names-and-type-names), both instance and static methods are applicable. - A generic method whose type arguments (explicitly specified or inferred) do not all satisfy their constraints is not applicable. -- In the context of a method group conversion, there must exist an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) between the method return type and the delegate’s return type. Otherwise, the candidate method is not applicable. +- In the context of a method group conversion, there must exist an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) or an implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) from the method return type to the delegate’s return type. Otherwise, the candidate method is not applicable. -#### 11.6.4.3 Better function member +#### 12.6.4.3 Better function member -For the purposes of determining the better function member, a stripped-down argument list `A` is constructed containing just the argument expressions themselves in the order they appear in the original argument list. +For the purposes of determining the better function member, a stripped-down argument list `A` is constructed containing just the argument expressions themselves in the order they appear in the original argument list, and leaving out any `out` or `ref` arguments. Parameter lists for each of the candidate function members are constructed in the following way: - The expanded form is used if the function member was applicable only in the expanded form. - Optional parameters with no corresponding arguments are removed from the parameter list +- `ref` and `out` parameters are removed from the parameter list - The parameters are reordered so that they occur at the same position as the corresponding argument in the argument list. Given an argument list `A` with a set of argument expressions `{E₁, E₂, ..., Eᵥ}` and two applicable function members `Mᵥ` and `Mₓ` with parameter types `{P₁, P₂, ..., Pᵥ}` and `{Q₁, Q₂, ..., Qᵥ}`, `Mᵥ` is defined to be a ***better function member*** than `Mₓ` if @@ -1020,42 +1078,60 @@ In case the parameter type sequences `{P₁, P₂, ..., Pᵥ}` and `{Q₁, Q₂ - Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other. - An array type is more specific than another array type (with the same number of dimensions) if the element type of the first is more specific than the element type of the second. - Otherwise if one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better. -- If neither function member was found to be better, and all parameters of `Mᵥ` have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in `Mₓ`, then `Mᵥ` is better than `Mₓ`. Otherwise, no function member is better. +- If neither function member was found to be better, and all parameters of `Mᵥ` have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in `Mₓ`, then `Mᵥ` is better than `Mₓ`. +- If for at least one parameter `Mᵥ` uses the ***better parameter-passing choice*** ([§12.6.4.4](expressions.md#12644-better-parameter-passing-mode)) than the corresponding parameter in `Mₓ` and none of the parameters in `Mₓ` use the better parameter-passing choice than `Mᵥ`, `Mᵥ` is better than `Mₓ`. +- Otherwise, no function member is better. + +#### 12.6.4.4 Better parameter-passing mode + +It is permitted to have corresponding parameters in two overloaded methods differ only by parameter-passing mode provided one of the two parameters has value-passing mode, as follows: + + +```csharp +public static void M1(int p1) { ... } +public static void M1(in int p1) { ... } +``` + +Given `int i = 10;`, according to [§12.6.4.2](expressions.md#12642-applicable-function-member), the calls `M1(i)` and `M1(i + 5)` result in both overloads being applicable. In such cases, the method with the parameter-passing mode of value is the ***better parameter-passing mode choice***. -#### 11.6.4.4 Better conversion from expression +> *Note*: No such choice need exist for arguments of input, output, or reference passing modes, as those arguments only match the exact same parameter passing modes. *end note* + +#### 12.6.4.5 Better conversion from expression Given an implicit conversion `C₁` that converts from an expression `E` to a type `T₁`, and an implicit conversion `C₂` that converts from an expression `E` to a type `T₂`, `C₁` is a ***better conversion*** than `C₂` if one of the following holds: -- `E` exactly matches `T₁` and `E` does not exactly match `T₂` ([§11.6.4.5](expressions.md#11645-exactly-matching-expression)) -- `E` exactly matches both or neither of `T₁` and `T₂`, and `T₁` is a better conversion target than `T₂` ([§11.6.4.6](expressions.md#11646-better-conversion-target)) -- `E` is a method group ([§11.2](expressions.md#112-expression-classifications)), `T₁` is compatible ([§19.4](delegates.md#194-delegate-compatibility)) with the single best method from the method group for conversion `C₁`, and `T₂` is not compatible with the single best method from the method group for conversion `C₂` +- `E` exactly matches `T₁` and `E` does not exactly match `T₂` ([§12.6.4.6](expressions.md#12646-exactly-matching-expression)) +- `E` exactly matches both or neither of `T₁` and `T₂`, and `T₁` is a better conversion target than `T₂` ([§12.6.4.7](expressions.md#12647-better-conversion-target)) +- `E` is a method group ([§12.2](expressions.md#122-expression-classifications)), `T₁` is compatible ([§20.4](delegates.md#204-delegate-compatibility)) with the single best method from the method group for conversion `C₁`, and `T₂` is not compatible with the single best method from the method group for conversion `C₂` -#### 11.6.4.5 Exactly matching expression +#### 12.6.4.6 Exactly matching expression Given an expression `E` and a type `T`, `E` ***exactly matches*** `T` if one of the following holds: - `E` has a type `S`, and an identity conversion exists from `S` to `T` - `E` is an anonymous function, `T` is either a delegate type `D` or an expression tree type `Expression` and one of the following holds: - - An inferred return type `X` exists for `E` in the context of the parameter list of `D` ([§11.6.3.12](expressions.md#116312-fixing)), and an identity conversion exists from `X` to the return type of `D` - - Either `E` is non-async and `D` has a return type `Y` or `E` is async and `D` has a return type `Task`, and one of the following holds: + - An inferred return type `X` exists for `E` in the context of the parameter list of `D` ([§12.6.3.12](expressions.md#126312-fixing)), and an identity conversion exists from `X` to the return type of `D` + - `E` is an `async` lambda with no return value, and `S` is a non-generic `«TaskType»` + - Either `E` is non-async and `D` has a return type `Y` or `E` is async and `D` has a return type `«TaskType»`([§15.15.1](classes.md#15151-general)), and one of the following holds: - The body of `E` is an expression that exactly matches `Y` - The body of `E` is a block where every return statement returns an expression that exactly matches `Y` -#### 11.6.4.6 Better conversion target +#### 12.6.4.7 Better conversion target Given two types `T₁` and `T₂`, `T₁` is a ***better conversion target*** than `T₂` if one of the following holds: - An implicit conversion from `T₁` to `T₂` exists and no implicit conversion from `T₂` to `T₁` exists -- `T₁` is `Task`, `T₂` is `Task`, and `S₁` is a better conversion target than `S₂` +- `T₁` is `«TaskType»`([§15.15.1](classes.md#15151-general)), `T₂` is `«TaskType»`, and `S₁` is a better conversion target than `S₂` +- `T₁` is `«TaskType»`([§15.15.1](classes.md#15151-general)), `T₂` is `«TaskType»`, and `T₁` is more specialized than `T₂` - `T₁` is `S₁` or `S₁?` where `S₁` is a signed integral type, and `T₂` is `S₂` or `S₂?` where `S₂` is an unsigned integral type. Specifically: - `S₁` is `sbyte` and `S₂` is `byte`, `ushort`, `uint`, or `ulong` - `S₁` is `short` and `S₂` is `ushort`, `uint`, or `ulong` - `S₁` is `int` and `S₂` is `uint`, or `ulong` - `S₁` is `long` and `S₂` is `ulong` -#### 11.6.4.7 Overloading in generic classes +#### 12.6.4.8 Overloading in generic classes -> *Note*: While signatures as declared shall be unique ([§8.6](types.md#86-expression-tree-types)), it is possible that substitution of type arguments results in identical signatures. In such a situation, overload resolution will pick the most specific ([§11.6.4.3](expressions.md#11643-better-function-member)) of the original signatures (before substitution of type arguments), if it exists, and otherwise report an error. *end note* +> *Note*: While signatures as declared shall be unique ([§8.6](types.md#86-expression-tree-types)), it is possible that substitution of type arguments results in identical signatures. In such a situation, overload resolution will pick the most specific ([§12.6.4.3](expressions.md#12643-better-function-member)) of the original signatures (before substitution of type arguments), if it exists, and otherwise report an error. *end note* @@ -1068,39 +1144,39 @@ Given two types `T₁` and `T₂`, `T₁` is a ***better conversion target*** th > > public abstract class G1 > { -> public abstract int F1(U u); // Overload resolution for G.F1 -> public abstract int F1(int i); // will pick non-generic +> public abstract int F1(U u); // Overload resolution for G.F1 +> public abstract int F1(int i); // will pick non-generic > -> public abstract void F2(I1 a); // Valid overload +> public abstract void F2(I1 a); // Valid overload > public abstract void F2(I2 a); > } > > abstract class G2 > { -> public abstract void F3(U u, V v); // Valid, but overload resolution for -> public abstract void F3(V v, U u); // G2.F3 will fail +> public abstract void F3(U u, V v); // Valid, but overload resolution for +> public abstract void F3(V v, U u); // G2.F3 will fail > -> public abstract void F4(U u, I1 v); // Valid, but overload resolution for -> public abstract void F4(I1 v, U u); // G2,int>.F4 will fail +> public abstract void F4(U u, I1 v); // Valid, but overload resolution for +> public abstract void F4(I1 v, U u); // G2,int>.F4 will fail > > public abstract void F5(U u1, I1 v2); // Valid overload > public abstract void F5(V v1, U u2); > -> public abstract void F6(ref U u); // Valid overload +> public abstract void F6(ref U u); // Valid overload > public abstract void F6(out V v); > } > ``` > > *end example* -### 11.6.5 Compile-time checking of dynamic member invocation +### 12.6.5 Compile-time checking of dynamic member invocation Even though overload resolution of a dynamically bound operation takes place at run-time, it is sometimes possible at compile-time to know the list of function members from which an overload will be chosen: -- For a delegate invocation ([§11.7.8.4](expressions.md#11784-delegate-invocations)), the list is a single function member with the same parameter list as the *delegate_type* of the invocation -- For a method invocation ([§11.7.8.2](expressions.md#11782-method-invocations)) on a type, or on a value whose static type is not dynamic, the set of accessible methods in the method group is known at compile-time. -- For an object creation expression ([§11.7.15.2](expressions.md#117152-object-creation-expressions)) the set of accessible constructors in the type is known at compile-time. -- For an indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)) the set of accessible indexers in the receiver is known at compile-time. +- For a delegate invocation ([§12.8.9.4](expressions.md#12894-delegate-invocations)), the list is a single function member with the same parameter list as the *delegate_type* of the invocation +- For a method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)) on a type, or on a value whose static type is not dynamic, the set of accessible methods in the method group is known at compile-time. +- For an object creation expression ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) the set of accessible constructors in the type is known at compile-time. +- For an indexer access ([§12.8.11.3](expressions.md#128113-indexer-access)) the set of accessible indexers in the receiver is known at compile-time. In these cases a limited compile-time check is performed on each member in the known set of function members, to see if it can be known for certain never to be invoked at run-time. For each function member `F` a modified parameter and argument list are constructed: @@ -1109,7 +1185,7 @@ In these cases a limited compile-time check is performed on each member in the k For `F` to pass the check, all of the following shall hold: -- The modified parameter list for `F` is applicable to the modified argument list in terms of [§11.6.4.2](expressions.md#11642-applicable-function-member). +- The modified parameter list for `F` is applicable to the modified argument list in terms of [§12.6.4.2](expressions.md#12642-applicable-function-member). - All constructed types in the modified parameter list satisfy their constraints ([§8.4.5](types.md#845-satisfying-constraints)). - If the type parameters of `F` were substituted in the step above, their constraints are satisfied. - If `F` is a static method, the method group shall not have resulted from a *member_access* whose receiver is known at compile-time to be a variable or value. @@ -1117,41 +1193,46 @@ For `F` to pass the check, all of the following shall hold: If no candidate passes this test, a compile-time error occurs. -### 11.6.6 Function member invocation +### 12.6.6 Function member invocation -#### 11.6.6.1 General +#### 12.6.6.1 General This subclause describes the process that takes place at run-time to invoke a particular function member. It is assumed that a binding-time process has already determined the particular member to invoke, possibly by applying overload resolution to a set of candidate function members. For purposes of describing the invocation process, function members are divided into two categories: - Static function members. These are static methods, static property accessors, and user-defined operators. Static function members are always non-virtual. -- Instance function members. These are instance methods, instance constructors, instance property accessors, and indexer accessors. Instance function members are either non-virtual or virtual, and are always invoked on a particular instance. The instance is computed by an instance expression, and it becomes accessible within the function member as `this` ([§11.7.12](expressions.md#11712-this-access)). For an instance constructor, the instance expression is taken to be the newly allocated object. +- Instance function members. These are instance methods, instance constructors, instance property accessors, and indexer accessors. Instance function members are either non-virtual or virtual, and are always invoked on a particular instance. The instance is computed by an instance expression, and it becomes accessible within the function member as `this` ([§12.8.13](expressions.md#12813-this-access)). For an instance constructor, the instance expression is taken to be the newly allocated object. The run-time processing of a function member invocation consists of the following steps, where `M` is the function member and, if `M` is an instance member, `E` is the instance expression: - If `M` is a static function member: - - The argument list is evaluated as described in [§11.6.2](expressions.md#1162-argument-lists). + - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - `M` is invoked. - Otherwise, if the type of `E` is a value-type `V`, and `M` is declared or overridden in `V`: - `E` is evaluated. If this evaluation causes an exception, then no further steps are executed. For an instance constructor, this evaluation consists of allocating storage (typically from an execution stack) for the new object. In this case `E` is classified as a variable. - - If `E` is not classified as a variable, then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. - - The argument list is evaluated as described in [§11.6.2](expressions.md#1162-argument-lists). + - If `E` is not classified as a variable, or if `V` is not a readonly struct type ([§16.2.2](structs.md#1622-struct-modifiers)), and `E` is one of: + - an input parameter ([§15.6.2.3](classes.md#15623-input-parameters)), or + - a `readonly` field ([§15.5.3](classes.md#1553-readonly-fields)), or + - a `readonly` reference variable or return ([§9.7](variables.md#97-reference-variables-and-returns)), + + then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` can be written is it possible for the caller to observe the changes that `M` makes to `this`. + - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - `M` is invoked. The variable referenced by `E` becomes the variable referenced by `this`. - Otherwise: - `E` is evaluated. If this evaluation causes an exception, then no further steps are executed. - - The argument list is evaluated as described in [§11.6.2](expressions.md#1162-argument-lists). + - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - If the type of `E` is a *value_type*, a boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)) is performed to convert `E` to a *class_type*, and `E` is considered to be of that *class_type* in the following steps. If the *value_type* is an *enum_type*, the *class_type* is `System.Enum;` otherwise, it is `System.ValueType`. - The value of `E` is checked to be valid. If the value of `E` is null, a `System.NullReferenceException` is thrown and no further steps are executed. - The function member implementation to invoke is determined: - - If the binding-time type of `E` is an interface, the function member to invoke is the implementation of `M` provided by the run-time type of the instance referenced by `E`. This function member is determined by applying the interface mapping rules ([§17.6.5](interfaces.md#1765-interface-mapping)) to determine the implementation of `M` provided by the run-time type of the instance referenced by `E`. - - Otherwise, if `M` is a virtual function member, the function member to invoke is the implementation of `M` provided by the run-time type of the instance referenced by `E`. This function member is determined by applying the rules for determining the most derived implementation ([§14.6.4](classes.md#1464-virtual-methods)) of `M` with respect to the run-time type of the instance referenced by `E`. + - If the binding-time type of `E` is an interface, the function member to invoke is the implementation of `M` provided by the run-time type of the instance referenced by `E`. This function member is determined by applying the interface mapping rules ([§18.6.5](interfaces.md#1865-interface-mapping)) to determine the implementation of `M` provided by the run-time type of the instance referenced by `E`. + - Otherwise, if `M` is a virtual function member, the function member to invoke is the implementation of `M` provided by the run-time type of the instance referenced by `E`. This function member is determined by applying the rules for determining the most derived implementation ([§15.6.4](classes.md#1564-virtual-methods)) of `M` with respect to the run-time type of the instance referenced by `E`. - Otherwise, `M` is a non-virtual function member, and the function member to invoke is `M` itself. - The function member implementation determined in the step above is invoked. The object referenced by `E` becomes the object referenced by this. -The result of the invocation of an instance constructor ([§11.7.15.2](expressions.md#117152-object-creation-expressions)) is the value created. The result of the invocation of any other function member is the value, if any, returned ([§12.10.5](statements.md#12105-the-return-statement)) from its body. +The result of the invocation of an instance constructor ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) is the value created. The result of the invocation of any other function member is the value, if any, returned ([§13.10.5](statements.md#13105-the-return-statement)) from its body. -#### 11.6.6.2 Invocations on boxed instances +#### 12.6.6.2 Invocations on boxed instances A function member implemented in a *value_type* can be invoked through a boxed instance of that *value_type* in the following situations: @@ -1164,9 +1245,23 @@ In these situations, the boxed instance is considered to contain a variable of t > *Note*: In particular, this means that when a function member is invoked on a boxed instance, it is possible for the function member to modify the value contained in the boxed instance. *end note* -## 11.7 Primary expressions +## 12.7 Deconstruction + +Deconstruction is a process whereby an expression gets turned into a tuple of individual expressions. Deconstruction is used when the target of a simple assignment is a tuple expression, in order to obtain values to assign to each of that tuple’s elements. + +An expression `E` is ***deconstructed*** to a tuple expression with `n` elements in the following way: + +- If `E` is a tuple expression with `n` elements, the result of deconstruction is the expression `E` itself. +- Otherwise, if `E` has a tuple type `(T1, ..., Tn)` with `n` elements, then `E` is evaluated into a temporary variable `__v`, and the result of deconstruction is the expression `(__v.Item1, ..., __v.Itemn)`. +- Otherwise, if the expression `E.Deconstruct(out var __v1, ..., out var __vn)` resolves at compile-time to a unique instance or extension method, that expression is evaluated, and the result of deconstruction is the expression `(__v1, ..., __vn)`. Such a method is referred to as a ***deconstructor***. +- Otherwise, `E` cannot be deconstructed. + +Here, `__v` and `__v1, ..., __vn` refer to otherwise invisible and inaccessible temporary variables. +> *Note*: An expression of type `dynamic` cannot be deconstructed. *end note* + +## 12.8 Primary expressions -### 11.7.1 General +### 12.8.1 General Primary expressions include the simplest forms of expressions. @@ -1181,6 +1276,7 @@ primary_no_array_creation_expression | interpolated_string_expression | simple_name | parenthesized_expression + | tuple_expression | member_access | null_conditional_member_access | invocation_expression @@ -1202,12 +1298,13 @@ primary_no_array_creation_expression | anonymous_method_expression | pointer_member_access // unsafe code support | pointer_element_access // unsafe code support + | stackalloc_expression ; ``` -> *Note*: These grammar rules are not ANTLR-ready as they are part of a set of mutually left-recursive rules (`primary_expression`, `primary_no_array_creation_expression`, `member_access`, `invocation_expression`, `element_access`, `post_increment_expression`, `post_decrement_expression`, `pointer_member_access` and `pointer_element_access`) which ANTLR does not handle. Standard techniques can be used to transform the grammar to remove the mutual left-recursion. This has not been done as not all parsing strategies require it (e.g. an LALR parser would not) and doing so would obfuscate the structure and description. +> *Note*: These grammar rules are not ANTLR-ready as they are part of a set of mutually left-recursive rules (`primary_expression`, `primary_no_array_creation_expression`, `member_access`, `invocation_expression`, `element_access`, `post_increment_expression`, `post_decrement_expression`, `pointer_member_access` and `pointer_element_access`) which ANTLR does not handle. Standard techniques can be used to transform the grammar to remove the mutual left-recursion. This has not been done as not all parsing strategies require it (e.g. an LALR parser would not) and doing so would obfuscate the structure and description. *end note* -*pointer_member_access* ([§22.6.3](unsafe-code.md#2263-pointer-member-access)) and *pointer_element_access* ([§22.6.4](unsafe-code.md#2264-pointer-element-access)) are only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*pointer_member_access* ([§23.6.3](unsafe-code.md#2363-pointer-member-access)) and *pointer_element_access* ([§23.6.4](unsafe-code.md#2364-pointer-element-access)) are only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). Primary expressions are divided between *array_creation_expression*s and *primary_no_array_creation_expression*s. Treating *array_creation_expression* in this way, rather than listing it along with the other simple expression forms, enables the grammar to disallow potentially confusing code such as @@ -1223,11 +1320,11 @@ which would otherwise be interpreted as object o = (new int[3])[1]; ``` -### 11.7.2 Literals +### 12.8.2 Literals A *primary_expression* that consists of a *literal* ([§6.4.5](lexical-structure.md#645-literals)) is classified as a value. -### 11.7.3 Interpolated string expressions +### 12.8.3 Interpolated string expressions An *interpolated_string_expression* consists of `$` or `$@` immediately followed by text within `"` characters. Within the quoted text there are zero or more ***interpolations*** delimited by `{` and `}` characters, each of which encloses an *expression* and optional formatting specifications. @@ -1353,22 +1450,22 @@ Six of the lexical rules defined above are *context sensitive* as follows: | *Interpolated_Regular_String_End* | Only recognised after an *Interpolated_Regular_String_Start* and only if any intervening tokens are either *Interpolated_Regular_String_Mid*s or tokens that can be part of *regular_interpolation*s, including tokens for any *interpolated_regular_string_expression*s contained within such interpolations. | | *Interpolated_Verbatim_String_Mid* *Verbatim_Interpolation_Format* *Interpolated_Verbatim_String_End* | Recognition of these three rules follows that of the corresponding rules above with each mentioned *regular* grammar rule replaced by the corresponding *verbatim* one. | -> *Note:* The above rules are context sensitive as their definitions overlap with those of +> *Note*: The above rules are context sensitive as their definitions overlap with those of other tokens in the language. *end note* -> *Note:* The above grammar is not ANTLR-ready due to the context sensitive lexical rules. As with +> *Note*: The above grammar is not ANTLR-ready due to the context sensitive lexical rules. As with other lexer generators ANTLR supports context sensitive lexical rules, for example using its *lexical modes*, but this is an implementation detail and therefore not part of this Standard. *end note* An *interpolated_string_expression* is classified as a value. If it is immediately converted to `System.IFormattable` or `System.FormattableString` with an implicit interpolated string conversion ([§10.2.5](conversions.md#1025-implicit-interpolated-string-conversions)), the interpolated string expression has that type. Otherwise, it has the type `string`. -> *Note:* The differences between the possible types an *interpolated_string_expression* may be determined from the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). *end note* +> *Note*: The differences between the possible types an *interpolated_string_expression* may be determined from the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). *end note* The meaning of an interpolation, both *regular_interpolation* and *verbatim_interpolation*, is to format the value of the *expression* as a `string` either according to the format specified by the *Regular_Interpolation_Format* or *Verbatim_Interpolation_Format*, or according to a default format for the type of *expression*. The formatted string is then modified by the *interpolation_minimum_width*, if any, to produce the final `string` to be interpolated into the *interpolated_string_expression*. -> *Note:* How the default format for a type is determined is detailed in the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). Descriptions of standard formats, which are identical for *Regular_Interpolation_Format* and *Verbatim_Interpolation_Format*, may be found in the documentation for `System.IFormattable` ([§C.4](standard-library.md#c4-format-specifications)) and in other types in the standard library ([§C](standard-library.md#annex-c-standard-library)). *end note* +> *Note*: How the default format for a type is determined is detailed in the documentation for `System.String` ([§C.2](standard-library.md#c2-standard-library-types-defined-in-isoiec-23271)) and `System.FormattableString` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)). Descriptions of standard formats, which are identical for *Regular_Interpolation_Format* and *Verbatim_Interpolation_Format*, may be found in the documentation for `System.IFormattable` ([§C.4](standard-library.md#c4-format-specifications)) and in other types in the standard library ([§C](standard-library.md#annex-c-standard-library)). *end note* In an *interpolation_minimum_width* the *constant_expression* shall have an implicit conversion to `int`. Let the *field width* be the absolute value of this *constant_expression* and the *alignment* be the sign (positive or negative) of the value of this *constant_expression*: @@ -1434,7 +1531,7 @@ Then: *end example* -### 11.7.4 Simple names +### 12.8.4 Simple names A *simple_name* consists of an identifier, optionally followed by a type argument list: @@ -1446,14 +1543,14 @@ simple_name A *simple_name* is either of the form `I` or of the form `I`, where `I` is a single identifier and `I` is an optional *type_argument_list*. When no *type_argument_list* is specified, consider `e` to be zero. The *simple_name* is evaluated and classified as follows: -- If `e` is zero and the *simple_name* appears within a *block* and if the *block*’s (or an enclosing *block*’s) local variable declaration space ([§7.3](basic-concepts.md#73-declarations)) contains a local variable, parameter or constant with name `I`, then the *simple_name* refers to that local variable, parameter or constant and is classified as a variable or value. -- If `e` is zero and the *simple_name* appears within a generic method declaration but outside the *attributes* of its *method_header,* and if that declaration includes a type parameter with name `I`, then the *simple_name* refers to that type parameter. -- Otherwise, for each instance type `T` ([§14.3.2](classes.md#1432-the-instance-type)), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any): +- If `e` is zero and the *simple_name* appears within a local variable declaration space ([§7.3](basic-concepts.md#73-declarations)) that directly contains a local variable, parameter or constant with name `I`, then the *simple_name* refers to that local variable, parameter or constant and is classified as a variable or value. +- If `e` is zero and the *simple_name* appears within a generic method declaration but outside the *attributes* of its *method_declaration*, and if that declaration includes a type parameter with name `I`, then the *simple_name* refers to that type parameter. +- Otherwise, for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any): - If `e` is zero and the declaration of `T` includes a type parameter with name `I`, then the *simple_name* refers to that type parameter. - - Otherwise, if a member lookup ([§11.5](expressions.md#115-member-lookup)) of `I` in `T` with `e` type arguments produces a match: - - If `T` is the instance type of the immediately enclosing class or struct type and the lookup identifies one or more methods, the result is a method group with an associated instance expression of `this`. If a type argument list was specified, it is used in calling a generic method ([§11.7.8.2](expressions.md#11782-method-invocations)). - - Otherwise, if `T` is the instance type of the immediately enclosing class or struct type, if the lookup identifies an instance member, and if the reference occurs within the *block* of an instance constructor, an instance method, or an instance accessor ([§11.2.1](expressions.md#1121-general)), the result is the same as a member access ([§11.7.6](expressions.md#1176-member-access)) of the form `this.I`. This can only happen when `e` is zero. - - Otherwise, the result is the same as a member access ([§11.7.6](expressions.md#1176-member-access)) of the form `T.I` or `T.I`. + - Otherwise, if a member lookup ([§12.5](expressions.md#125-member-lookup)) of `I` in `T` with `e` type arguments produces a match: + - If `T` is the instance type of the immediately enclosing class or struct type and the lookup identifies one or more methods, the result is a method group with an associated instance expression of `this`. If a type argument list was specified, it is used in calling a generic method ([§12.8.9.2](expressions.md#12892-method-invocations)). + - Otherwise, if `T` is the instance type of the immediately enclosing class or struct type, if the lookup identifies an instance member, and if the reference occurs within the *block* of an instance constructor, an instance method, or an instance accessor ([§12.2.1](expressions.md#1221-general)), the result is the same as a member access ([§12.8.7](expressions.md#1287-member-access)) of the form `this.I`. This can only happen when `e` is zero. + - Otherwise, the result is the same as a member access ([§12.8.7](expressions.md#1287-member-access)) of the form `T.I` or `T.I`. - Otherwise, for each namespace `N`, starting with the namespace in which the *simple_name* occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located: - If `e` is zero and `I` is the name of a namespace in `N`, then: - If the location where the *simple_name* occurs is enclosed by a namespace declaration for `N` and the namespace declaration contains an *extern_alias_directive* or *using_alias_directive* that associates the name `I` with a namespace or type, then the *simple_name* is ambiguous and a compile-time error occurs. @@ -1466,9 +1563,10 @@ A *simple_name* is either of the form `I` or of the form `I`, - Otherwise, if the namespaces imported by the *using_namespace_directive*s of the namespace declaration contain exactly one type having name `I` and `e` type parameters, then the *simple_name* refers to that type constructed with the given type arguments. - Otherwise, if the namespaces imported by the *using_namespace_directive*s of the namespace declaration contain more than one type having name `I` and `e` type parameters, then the *simple_name* is ambiguous and a compile-time error occurs. > *Note*: This entire step is exactly parallel to the corresponding step in the processing of a *namespace_or_type_name* ([§7.8](basic-concepts.md#78-namespace-and-type-names)). *end note* +- Otherwise, if `e` is zero and `I` is the identifier `_`, the *simple_name* is a *simple discard*, which is a form of declaration expression ([§12.17](expressions.md#1217-declaration-expressions)). - Otherwise, the *simple_name* is undefined and a compile-time error occurs. -### 11.7.5 Parenthesized expressions +### 12.8.5 Parenthesized expressions A *parenthesized_expression* consists of an *expression* enclosed in parentheses. @@ -1480,9 +1578,74 @@ parenthesized_expression A *parenthesized_expression* is evaluated by evaluating the *expression* within the parentheses. If the *expression* within the parentheses denotes a namespace or type, a compile-time error occurs. Otherwise, the result of the *parenthesized_expression* is the result of the evaluation of the contained *expression*. -### 11.7.6 Member access +### 12.8.6 Tuple expressions -#### 11.7.6.1 General +A *tuple_expression* represents a tuple, and consists of two or more comma-separated and optionally-named *expression*s enclosed in parentheses. A *deconstruction_expression* is a shorthand syntax for a tuple containing implicitly typed declaration expressions. + +```ANTLR +tuple_expression + : '(' tuple_element (',' tuple_element)+ ')' + | deconstruction_expression + ; + +tuple_element + : (identifier ':')? expression + ; + +deconstruction_expression + : 'var' deconstruction_tuple + ; + +deconstruction_tuple + : '(' deconstruction_element (',' deconstruction_element)+ ')' + ; + +deconstruction_element + : deconstruction_tuple + | identifier + ; +``` + +A *tuple_expression* is classified as a tuple. + +A *deconstruction_expression* `var (e1, ..., en)` is shorthand for the *tuple_expression* `(var e1, ..., var en)` and follows the same behavior. This applies recursively to any nested *deconstruction_tuple*s in the *deconstruction_expression*. Each identifier nested within a *deconstruction_expression* thus introduces a declaration expression ([§12.17](expressions.md#1217-declaration-expressions)). As a result, a *deconstruction_expression* can only occur on the left side of a simple assignment. + +A tuple expression has a type if and only if each of its element expressions `Ei` has a type `Ti`. The type shall be a tuple type of the same arity as the tuple expression, where each element is given by the following: + +- If the tuple element in the corresponding position has a name `Ni`, then the tuple type element shall be `Ti Ni`. +- Otherwise, if `Ei` is of the form `Ni` or `E.Ni` or `E?.Ni` then the tuple type element shall be `Ti Ni`, *unless* any of the following holds: + - Another element of the tuple expression has the name `Ni`, or + - Another tuple element without a name has a tuple element expression of the form `Ni` or `E.Ni` or `E?.Ni`, or + - `Ni` is of the form `ItemX`, where `X` is a sequence of non-`0`-initiated decimal digits that could represent the position of a tuple element, and `X` does not represent the position of the element. +- Otherwise, the tuple type element shall be `Ti`. + +A tuple expression is evaluated by evaluating each of its element expressions in order from left to right. + +A tuple value can be obtained from a tuple expression by converting it to a tuple type ([§10.2.13](conversions.md#10213-implicit-tuple-conversions)), by reclassifying it as a value ([§12.2.2](expressions.md#1222-values-of-expressions))) or by making it the target of a deconstructing assignment ([§12.21.2](expressions.md#12212-simple-assignment)). + +> *Example*: +> +> +> ```csharp +> (int i, string) t1 = (i: 1, "One"); +> (long l, string) t2 = (l: 2, null); +> var t3 = (i: 3, "Three"); // (int i, string) +> var t4 = (i: 4, null); // Error: no type +> ``` +> +> In this example, all four tuple expressions are valid. The first two, `t1` and `t2`, do not use the type of the tuple expression, but instead apply an implicit tuple conversion. In the case of `t2`, the implicit tuple conversion relies on the implicit conversions from `2` to `long` and from `null` to `string`. The third tuple expression has a type `(int i, string)`, and can therefore be reclassified as a value of that type. The declaration of `t4`, on the other hand, is an error: The tuple expression has no type because its second element has no type. +> +> ```csharp +> if ((x, y).Equals((1, 2))) { ... }; +> ``` +> +> This example shows that tuples can sometimes lead to multiple layers of parentheses, especially when the tuple expression is the sole argument to a method invocation. +> +> *end example* + +### 12.8.7 Member access + +#### 12.8.7.1 General A *member_access* consists of a *primary_expression*, a *predefined_type*, or a *qualified_alias_member*, followed by a “`.`” token, followed by an *identifier*, optionally followed by a *type_argument_list*. @@ -1500,17 +1663,17 @@ predefined_type ; ``` -The *qualified_alias_member* production is defined in [§13.8](namespaces.md#138-qualified-alias-member). +The *qualified_alias_member* production is defined in [§14.8](namespaces.md#148-qualified-alias-member). A *member_access* is either of the form `E.I` or of the form `E.I`, where `E` is a *primary_expression*, *predefined_type* or *qualified_alias_member,* `I` is a single identifier, and `` is an optional *type_argument_list*. When no *type_argument_list* is specified, consider `e` to be zero. -A *member_access* with a *primary_expression* of type `dynamic` is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case, the compiler classifies the member access as a property access of type `dynamic`. The rules below to determine the meaning of the *member_access* are then applied at run-time, using the run-time type instead of the compile-time type of the *primary_expression*. If this run-time classification leads to a method group, then the member access shall be the *primary_expression* of an *invocation_expression*. +A *member_access* with a *primary_expression* of type `dynamic` is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compiler classifies the member access as a property access of type `dynamic`. The rules below to determine the meaning of the *member_access* are then applied at run-time, using the run-time type instead of the compile-time type of the *primary_expression*. If this run-time classification leads to a method group, then the member access shall be the *primary_expression* of an *invocation_expression*. The *member_access* is evaluated and classified as follows: - If `e` is zero and `E` is a namespace and `E` contains a nested namespace with name `I`, then the result is that namespace. - Otherwise, if `E` is a namespace and `E` contains an accessible type having name `I` and `K` type parameters, then the result is that type constructed with the given type arguments. -- If `E` is classified as a type, if `E` is not a type parameter, and if a member lookup ([§11.5](expressions.md#115-member-lookup)) of `I` in `E` with `K` type parameters produces a match, then `E.I` is evaluated and classified as follows: +- If `E` is classified as a type, if `E` is not a type parameter, and if a member lookup ([§12.5](expressions.md#125-member-lookup)) of `I` in `E` with `K` type parameters produces a match, then `E.I` is evaluated and classified as follows: > *Note*: When the result of such a member lookup is a method group and `K` is zero, the method group can contain methods having type parameters. This allows such methods to be considered for type argument inferencing. *end note* - If `I` identifies a type, then the result is that type constructed with any given type arguments. - If `I` identifies one or more methods, then the result is a method group with no associated instance expression. @@ -1519,13 +1682,13 @@ The *member_access* is evaluated and classified as follows: - If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field `I` in `E`. - Otherwise, the result is a variable, namely the static field `I` in `E`. - If `I` identifies a static event: - - If the reference occurs within the class or struct in which the event is declared, and the event was declared without *event_accessor_declarations* ([§14.8.1](classes.md#1481-general)), then `E.I` is processed exactly as if `I` were a static field. + - If the reference occurs within the class or struct in which the event is declared, and the event was declared without *event_accessor_declarations* ([§15.8.1](classes.md#1581-general)), then `E.I` is processed exactly as if `I` were a static field. - Otherwise, the result is an event access with no associated instance expression. - If `I` identifies a constant, then the result is a value, namely the value of that constant. - If `I` identifies an enumeration member, then the result is a value, namely the value of that enumeration member. - Otherwise, `E.I` is an invalid member reference, and a compile-time error occurs. -- If `E` is a property access, indexer access, variable, or value, the type of which is `T`, and a member lookup ([§11.5](expressions.md#115-member-lookup)) of `I` in `T` with `K` type arguments produces a match, then `E.I` is evaluated and classified as follows: - - First, if `E` is a property or indexer access, then the value of the property or indexer access is obtained ([§11.2.2](expressions.md#1122-values-of-expressions)) and E is reclassified as a value. +- If `E` is a property access, indexer access, variable, or value, the type of which is `T`, and a member lookup ([§12.5](expressions.md#125-member-lookup)) of `I` in `T` with `K` type arguments produces a match, then `E.I` is evaluated and classified as follows: + - First, if `E` is a property or indexer access, then the value of the property or indexer access is obtained ([§12.2.2](expressions.md#1222-values-of-expressions)) and E is reclassified as a value. - If `I` identifies one or more methods, then the result is a method group with an associated instance expression of `E`. - If `I` identifies an instance property, then the result is a property access with an associated instance expression of `E` and an associated type that is the type of the property. If `T` is a class type, the associated type is picked from the first declaration or override of the property found when starting with `T`, and searching through its base classes. - If `T` is a *class_type* and `I` identifies an instance field of that *class_type*: @@ -1536,13 +1699,13 @@ The *member_access* is evaluated and classified as follows: - If `E` is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field `I` in the struct instance given by `E`. - Otherwise, the result is a variable, namely the field `I` in the struct instance given by `E`. - If `I` identifies an instance event: - - If the reference occurs within the class or struct in which the event is declared, and the event was declared without *event_accessor_declarations* ([§14.8.1](classes.md#1481-general)), and the reference does not occur as the left-hand side of `a +=` or `-=` operator, then `E.I` is processed exactly as if `I` was an instance field. + - If the reference occurs within the class or struct in which the event is declared, and the event was declared without *event_accessor_declarations* ([§15.8.1](classes.md#1581-general)), and the reference does not occur as the left-hand side of `a +=` or `-=` operator, then `E.I` is processed exactly as if `I` was an instance field. - Otherwise, the result is an event access with an associated instance expression of `E`. -- Otherwise, an attempt is made to process `E.I` as an extension method invocation ([§11.7.8.3](expressions.md#11783-extension-method-invocations)). If this fails, `E.I` is an invalid member reference, and a binding-time error occurs. +- Otherwise, an attempt is made to process `E.I` as an extension method invocation ([§12.8.9.3](expressions.md#12893-extension-method-invocations)). If this fails, `E.I` is an invalid member reference, and a binding-time error occurs. -#### 11.7.6.2 Identical simple names and type names +#### 12.8.7.2 Identical simple names and type names -In a member access of the form `E.I`, if `E` is a single identifier, and if the meaning of `E` as a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) is a constant, field, property, local variable, or parameter with the same type as the meaning of `E` as a *type_name* ([§7.8.1](basic-concepts.md#781-general)), then both possible meanings of `E` are permitted. The member lookup of `E.I` is never ambiguous, since `I` shall necessarily be a member of the type `E` in both cases. In other words, the rule simply permits access to the static members and nested types of `E` where a compile-time error would otherwise have occurred. +In a member access of the form `E.I`, if `E` is a single identifier, and if the meaning of `E` as a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) is a constant, field, property, local variable, or parameter with the same type as the meaning of `E` as a *type_name* ([§7.8.1](basic-concepts.md#781-general)), then both possible meanings of `E` are permitted. The member lookup of `E.I` is never ambiguous, since `I` shall necessarily be a member of the type `E` in both cases. In other words, the rule simply permits access to the static members and nested types of `E` where a compile-time error would otherwise have occurred. > *Example*: > @@ -1576,9 +1739,9 @@ In a member access of the form `E.I`, if `E` is a single identifier, and if the > > *end example* -### 11.7.7 Null Conditional Member Access +### 12.8.8 Null Conditional Member Access -A *null_conditional_member_access* is a conditional version of *member_access* ([§11.7.6](expressions.md#1176-member-access)) and it is a binding time error if the result type is `void`. For a null conditional expression where the result type may be `void` see ([§11.7.9](expressions.md#1179-null-conditional-invocation-expression)). +A *null_conditional_member_access* is a conditional version of *member_access* ([§12.8.7](expressions.md#1287-member-access)) and it is a binding time error if the result type is `void`. For a null conditional expression where the result type may be `void` see ([§12.8.10](expressions.md#12810-null-conditional-invocation-expression)). A *null_conditional_member_access* consists of a *primary_expression* followed by the two tokens “`?`” and “`.`”, followed by an *identifier* with an optional *type_argument_list*, followed by zero or more *dependent_access*es. @@ -1599,23 +1762,47 @@ null_conditional_projection_initializer ; ``` -A *null_conditional_member_access* expression `E` is of the form `P?.A`. Let `T` be the type of the expression `P.A`. The meaning of `E` is determined as follows: +A *null_conditional_member_access* expression `E` is of the form `P?.A`. The meaning of `E` is determined as follows: -- If `T` is a type parameter that is not known to be a reference type or a non-nullable value type, a compile-time error occurs. -- If `T` is a non-nullable value type, then the type of `E` is `T?`, and the meaning of `E` is the same as the meaning of: +- If the type of `P` is a nullable value type: - ```csharp - ((object)P == null) ? (T?)null : P.A - ``` + Let `T` be the type of `P.Value.A`. - Except that `P` is evaluated only once. -- Otherwise the type of `E` is `T`, and the meaning of `E` is the same as the meaning of: + - If `T` is a type parameter that is not known to be either a reference type or a non-nullable value type, a compile-time error occurs. + - If `T` is a non-nullable value type, then the type of `E` is `T?`, and the meaning of `E` is the same as the meaning of: - ```csharp - ((object)P == null) ? null : P.A - ``` + ```csharp + ((object)P == null) ? (T?)null : P.Value.A + ``` + + Except that `P` is evaluated only once. + - Otherwise the type of `E` is `T`, and the meaning of `E` is the same as the meaning of: + + ```csharp + ((object)P == null) ? (T)null : P.Value.A + ``` + + Except that `P` is evaluated only once. + +- Otherwise: + + Let `T` be the type of the expression `P.A`. + + - If `T` is a type parameter that is not known to be either a reference type or a non-nullable value type, a compile-time error occurs. + - If `T` is a non-nullable value type, then the type of `E` is `T?`, and the meaning of `E` is the same as the meaning of: + + ```csharp + ((object)P == null) ? (T?)null : P.A + ``` - Except that `P` is evaluated only once. + Except that `P` is evaluated only once. + - Otherwise the type of `E` is `T`, and the meaning of `E` is the same as the meaning of: + + ```csharp + ((object)P == null) ? (T)null : P.A + ``` + + Except that `P` is evaluated only once. > *Note*: In an expression of the form: > @@ -1623,15 +1810,15 @@ A *null_conditional_member_access* expression `E` is of the form `P?.A`. Let `T > P?.A₀?.A₁ > ``` > -> then if `P` evaluates to `null` neither `A₀` or `A₁` are evaluated. The same is true if an expression is a sequence of *null_conditional_member_access* or *null_conditional_element_access* [§11.7.11](expressions.md#11711-null-conditional-element-access) operations. +> then if `P` evaluates to `null` neither `A₀` or `A₁` are evaluated. The same is true if an expression is a sequence of *null_conditional_member_access* or *null_conditional_element_access* [§12.8.12](expressions.md#12812-null-conditional-element-access) operations. > > *end note* -A *null_conditional_projection_initializer* is a restriction of *null_conditional_member_access* and has the same semantics. It only occurs as a projection initializer in an anonymous object creation expression ([§11.7.15.7](expressions.md#117157-anonymous-object-creation-expressions)). +A *null_conditional_projection_initializer* is a restriction of *null_conditional_member_access* and has the same semantics. It only occurs as a projection initializer in an anonymous object creation expression ([§12.8.16.7](expressions.md#128167-anonymous-object-creation-expressions)). -### 11.7.8 Invocation expressions +### 12.8.9 Invocation expressions -#### 11.7.8.1 General +#### 12.8.9.1 General An *invocation_expression* is used to invoke a method. @@ -1641,23 +1828,24 @@ invocation_expression ; ``` -An *invocation_expression* is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)) if at least one of the following holds: +An *invocation_expression* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) if at least one of the following holds: - The *primary_expression* has compile-time type `dynamic`. - At least one argument of the optional *argument_list* has compile-time type `dynamic`. -In this case, the compiler classifies the *invocation_expression* as a value of type `dynamic`. The rules below to determine the meaning of the *invocation_expression* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and arguments that have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the method invocation undergoes a limited compile-time check as described in [§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation). +In this case, the compiler classifies the *invocation_expression* as a value of type `dynamic`. The rules below to determine the meaning of the *invocation_expression* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and arguments that have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the method invocation undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation). -The *primary_expression* of an *invocation_expression* shall be a method group or a value of a *delegate_type*. If the *primary_expression* is a method group, the *invocation_expression* is a method invocation ([§11.7.8.2](expressions.md#11782-method-invocations)). If the *primary_expression* is a value of a *delegate_type*, the *invocation_expression* is a delegate invocation ([§11.7.8.4](expressions.md#11784-delegate-invocations)). If the *primary_expression* is neither a method group nor a value of a *delegate_type*, a binding-time error occurs. +The *primary_expression* of an *invocation_expression* shall be a method group or a value of a *delegate_type*. If the *primary_expression* is a method group, the *invocation_expression* is a method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)). If the *primary_expression* is a value of a *delegate_type*, the *invocation_expression* is a delegate invocation ([§12.8.9.4](expressions.md#12894-delegate-invocations)). If the *primary_expression* is neither a method group nor a value of a *delegate_type*, a binding-time error occurs. -The optional *argument_list* ([§11.6.2](expressions.md#1162-argument-lists)) provides values or variable references for the parameters of the method. +The optional *argument_list* ([§12.6.2](expressions.md#1262-argument-lists)) provides values or variable references for the parameters of the method. The result of evaluating an *invocation_expression* is classified as follows: -- If the *invocation_expression* invokes a method or delegate that returns void, the result is nothing. An expression that is classified as nothing is permitted only in the context of a *statement_expression* ([§12.7](statements.md#127-expression-statements)) or as the body of a *lambda_expression* ([§11.17](expressions.md#1117-anonymous-function-expressions)). Otherwise a binding-time error occurs. -- Otherwise, the result is a value, with an associated type of the return type of the method or delegate after any type argument substitutions ([§11.7.8.2](expressions.md#11782-method-invocations)) have been performed. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. +- If the *invocation_expression* invokes a returns-no-value method ([§15.6.1](classes.md#1561-general)) or a returns-no-value delegate, the result is nothing. An expression that is classified as nothing is permitted only in the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)) or as the body of a *lambda_expression* ([§12.19](expressions.md#1219-anonymous-function-expressions)). Otherwise, a binding-time error occurs. +- Otherwise, if the *invocation_expression* invokes a returns-by-ref method ([§15.6.1](classes.md#1561-general)) or a returns-by-ref delegate, the result is a variable with an associated type of the return type of the method or delegate. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. +- Otherwise, the *invocation_expression* invokes a returns-by-value method ([§15.6.1](classes.md#1561-general)) or returns-by-value delegate, and the result is a value, with an associated type of the return type of the method or delegate. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. -#### 11.7.8.2 Method invocations +#### 12.8.9.2 Method invocations For a method invocation, the *primary_expression* of the *invocation_expression* shall be a method group. The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the *argument_list*. @@ -1666,25 +1854,25 @@ The binding-time processing of a method invocation of the form `M(A)`, where `M` - The set of candidate methods for the method invocation is constructed. For each method `F` associated with the method group `M`: - If `F` is non-generic, `F` is a candidate when: - `M` has no type argument list, and - - `F` is applicable with respect to `A` ([§11.6.4.2](expressions.md#11642-applicable-function-member)). + - `F` is applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)). - If `F` is generic and `M` has no type argument list, `F` is a candidate when: - - Type inference ([§11.6.3](expressions.md#1163-type-inference)) succeeds, inferring a list of type arguments for the call, and - - Once the inferred type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of `F` satisfy their constraints ([§8.4.5](types.md#845-satisfying-constraints)), and the parameter list of `F` is applicable with respect to `A` ([§11.6.4.2](expressions.md#11642-applicable-function-member)) + - Type inference ([§12.6.3](expressions.md#1263-type-inference)) succeeds, inferring a list of type arguments for the call, and + - Once the inferred type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of `F` satisfy their constraints ([§8.4.5](types.md#845-satisfying-constraints)), and the parameter list of `F` is applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)) - If `F` is generic and `M` includes a type argument list, `F` is a candidate when: - `F` has the same number of method type parameters as were supplied in the type argument list, and - - Once the type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of `F` satisfy their constraints ([§8.4.5](types.md#845-satisfying-constraints)), and the parameter list of `F` is applicable with respect to `A` ([§11.6.4.2](expressions.md#11642-applicable-function-member)). + - Once the type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of `F` satisfy their constraints ([§8.4.5](types.md#845-satisfying-constraints)), and the parameter list of `F` is applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)). - The set of candidate methods is reduced to contain only methods from the most derived types: For each method `C.F` in the set, where `C` is the type in which the method `F` is declared, all methods declared in a base type of `C` are removed from the set. Furthermore, if `C` is a class type other than `object`, all methods declared in an interface type are removed from the set. > *Note*: This latter rule only has an effect when the method group was the result of a member lookup on a type parameter having an effective base class other than `object` and a non-empty effective interface set. *end note* -- If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned, and instead an attempt is made to process the invocation as an extension method invocation ([§11.7.8.3](expressions.md#11783-extension-method-invocations)). If this fails, then no applicable methods exist, and a binding-time error occurs. -- The best method of the set of candidate methods is identified using the overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution). If a single best method cannot be identified, the method invocation is ambiguous, and a binding-time error occurs. When performing overload resolution, the parameters of a generic method are considered after substituting the type arguments (supplied or inferred) for the corresponding method type parameters. +- If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned, and instead an attempt is made to process the invocation as an extension method invocation ([§12.8.9.3](expressions.md#12893-extension-method-invocations)). If this fails, then no applicable methods exist, and a binding-time error occurs. +- The best method of the set of candidate methods is identified using the overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). If a single best method cannot be identified, the method invocation is ambiguous, and a binding-time error occurs. When performing overload resolution, the parameters of a generic method are considered after substituting the type arguments (supplied or inferred) for the corresponding method type parameters. -Once a method has been selected and validated at binding-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in [§11.6.6](expressions.md#1166-function-member-invocation). +Once a method has been selected and validated at binding-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in [§12.6.6](expressions.md#1266-function-member-invocation). > *Note*: The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected. If no method was found, try instead to process the invocation as an extension-method invocation. *end note* -#### 11.7.8.3 Extension method invocations +#### 12.8.9.3 Extension method invocations -In a method invocation ([§11.6.6.2](expressions.md#11662-invocations-on-boxed-instances)) of one of the forms +In a method invocation ([§12.6.6.2](expressions.md#12662-invocations-on-boxed-instances)) of one of the forms ```csharp «expr» . «identifier» ( ) @@ -1717,10 +1905,10 @@ The search for `C` proceeds as follows: - If the given namespace or compilation unit directly contains non-generic type declarations `Cᵢ` with eligible extension methods `Mₑ`, then the set of those extension methods is the candidate set. - If namespaces imported by using namespace directives in the given namespace or compilation unit directly contain non-generic type declarations `Cᵢ` with eligible extension methods `Mₑ`, then the set of those extension methods is the candidate set. - If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs. -- Otherwise, overload resolution is applied to the candidate set as described in [§11.6.4](expressions.md#1164-overload-resolution). If no single best method is found, a compile-time error occurs. +- Otherwise, overload resolution is applied to the candidate set as described in [§12.6.4](expressions.md#1264-overload-resolution). If no single best method is found, a compile-time error occurs. - `C` is the type within which the best method is declared as an extension method. -Using `C` as a target, the method call is then processed as a static method invocation ([§11.6.6](expressions.md#1166-function-member-invocation)). +Using `C` as a target, the method call is then processed as a static method invocation ([§12.6.6](expressions.md#1266-function-member-invocation)). > *Note*: Unlike an instance method invocation, no exception is thrown when *expr* evaluates to a null reference. Instead, this `null` value is passed to the extension method as it would be via a regular static method invocation. It is up to the extension method implementation to decide how to respond to such a call. *end note* @@ -1815,24 +2003,24 @@ The preceding rules mean that instance methods take precedence over extension me > > *end example* -#### 11.7.8.4 Delegate invocations +#### 12.8.9.4 Delegate invocations -For a delegate invocation, the *primary_expression* of the *invocation_expression* shall be a value of a *delegate_type*. Furthermore, considering the *delegate_type* to be a function member with the same parameter list as the *delegate_type*, the *delegate_type* shall be applicable ([§11.6.4.2](expressions.md#11642-applicable-function-member)) with respect to the *argument_list* of the *invocation_expression*. +For a delegate invocation, the *primary_expression* of the *invocation_expression* shall be a value of a *delegate_type*. Furthermore, considering the *delegate_type* to be a function member with the same parameter list as the *delegate_type*, the *delegate_type* shall be applicable ([§12.6.4.2](expressions.md#12642-applicable-function-member)) with respect to the *argument_list* of the *invocation_expression*. The run-time processing of a delegate invocation of the form `D(A)`, where `D` is a *primary_expression* of a *delegate_type* and `A` is an optional *argument_list*, consists of the following steps: - `D` is evaluated. If this evaluation causes an exception, no further steps are executed. - The argument list `A` is evaluated. If this evaluation causes an exception, no further steps are executed. - The value of `D` is checked to be valid. If the value of `D` is `null`, a `System.NullReferenceException` is thrown and no further steps are executed. -- Otherwise, `D` is a reference to a delegate instance. Function member invocations ([§11.6.6](expressions.md#1166-function-member-invocation)) are performed on each of the callable entities in the invocation list of the delegate. For callable entities consisting of an instance and instance method, the instance for the invocation is the instance contained in the callable entity. +- Otherwise, `D` is a reference to a delegate instance. Function member invocations ([§12.6.6](expressions.md#1266-function-member-invocation)) are performed on each of the callable entities in the invocation list of the delegate. For callable entities consisting of an instance and instance method, the instance for the invocation is the instance contained in the callable entity. -See [§19.6](delegates.md#196-delegate-invocation) for details of multiple invocation lists without parameters. +See [§20.6](delegates.md#206-delegate-invocation) for details of multiple invocation lists without parameters. -### 11.7.9 Null Conditional Invocation Expression +### 12.8.10 Null Conditional Invocation Expression -A *null_conditional_invocation_expression* is syntactically either a *null_conditional_member_access* ([§11.7.7](expressions.md#1177-null-conditional-member-access)) or *null_conditional_element_access* ([§11.7.11](expressions.md#11711-null-conditional-element-access)) where the final *dependent_access* is an invocation expression ([§11.7.8](expressions.md#1178-invocation-expressions)). +A *null_conditional_invocation_expression* is syntactically either a *null_conditional_member_access* ([§12.8.8](expressions.md#1288-null-conditional-member-access)) or *null_conditional_element_access* ([§12.8.12](expressions.md#12812-null-conditional-element-access)) where the final *dependent_access* is an invocation expression ([§12.8.9](expressions.md#1289-invocation-expressions)). -A *null_conditional_invocation_expression* occurs within the context of a *statement_expression* ([§12.7](statements.md#127-expression-statements)), *anonymous_function_body* ([§11.17.1](expressions.md#11171-general)), or *method_body* ([§14.6.1](classes.md#1461-general)). +A *null_conditional_invocation_expression* occurs within the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)), *anonymous_function_body* ([§12.19.1](expressions.md#12191-general)), or *method_body* ([§15.6.1](classes.md#1561-general)). Unlike the syntactically equivalent *null_conditional_member_access* or *null_conditional_element_access*, a *null_conditional_invocation_expression* may be classified as nothing. @@ -1868,11 +2056,11 @@ When `E` occurs as a *anonymous_function_body* or *method_body* the meaning of ` { return E; } ``` - and in turn the meaning of this *block* depends on whether `E` is syntactically equivalent to a *null_conditional_member_access* ([§11.7.7](expressions.md#1177-null-conditional-member-access)) or *null_conditional_element_access* ([§11.7.11](expressions.md#11711-null-conditional-element-access)). + and in turn the meaning of this *block* depends on whether `E` is syntactically equivalent to a *null_conditional_member_access* ([§12.8.8](expressions.md#1288-null-conditional-member-access)) or *null_conditional_element_access* ([§12.8.12](expressions.md#12812-null-conditional-element-access)). -### 11.7.10 Element access +### 12.8.11 Element access -#### 11.7.10.1 General +#### 12.8.11.1 General An *element_access* consists of a *primary_no_array_creation_expression*, followed by a “`[`” token, followed by an *argument_list*, followed by a “`]`” token. The *argument_list* consists of one or more *argument*s, separated by commas. @@ -1882,18 +2070,18 @@ element_access ; ``` -The *argument_list* of an *element_access* is not allowed to contain `ref` or `out` arguments. +The *argument_list* of an *element_access* is not allowed to contain `out` or `ref` arguments. -An *element_access* is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)) if at least one of the following holds: +An *element_access* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) if at least one of the following holds: - The *primary_no_array_creation_expression* has compile-time type `dynamic`. - At least one expression of the *argument_list* has compile-time type `dynamic` and the *primary_no_array_creation_expression* does not have an array type. -In this case, the compiler classifies the *element_access* as a value of type `dynamic`. The rules below to determine the meaning of the *element_access* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_no_array_creation_expression* and *argument_list* expressions which have the compile-time type `dynamic`. If the *primary_no_array_creation_expression* does not have compile-time type `dynamic`, then the element access undergoes a limited compile-time check as described in [§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation). +In this case, the compiler classifies the *element_access* as a value of type `dynamic`. The rules below to determine the meaning of the *element_access* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_no_array_creation_expression* and *argument_list* expressions which have the compile-time type `dynamic`. If the *primary_no_array_creation_expression* does not have compile-time type `dynamic`, then the element access undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation). -If the *primary_no_array_creation_expression* of an *element_access* is a value of an *array_type*, the *element_access* is an array access ([§11.7.10.2](expressions.md#117102-array-access)). Otherwise, the *primary_no_array_creation_expression* shall be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the *element_access* is an indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)). +If the *primary_no_array_creation_expression* of an *element_access* is a value of an *array_type*, the *element_access* is an array access ([§12.8.11.2](expressions.md#128112-array-access)). Otherwise, the *primary_no_array_creation_expression* shall be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the *element_access* is an indexer access ([§12.8.11.3](expressions.md#128113-indexer-access)). -#### 11.7.10.2 Array access +#### 12.8.11.2 Array access For an array access, the *primary_no_array_creation_expression* of the *element_access* shall be a value of an *array_type*. Furthermore, the *argument_list* of an array access is not allowed to contain named arguments. The number of expressions in the *argument_list* shall be the same as the rank of the *array_type*, and each expression shall be of type `int`, `uint`, `long`, or `ulong,` or shall be implicitly convertible to one or more of these types. @@ -1907,7 +2095,7 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p - The value of each expression in the *argument_list* is checked against the actual bounds of each dimension of the array instance referenced by `P`. If one or more values are out of range, a `System.IndexOutOfRangeException` is thrown and no further steps are executed. - The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access. -#### 11.7.10.3 Indexer access +#### 12.8.11.3 Indexer access For an indexer access, the *primary_no_array_creation_expression* of the *element_access* shall be a variable or value of a class, struct, or interface type, and this type shall implement one or more indexers that are applicable with respect to the *argument_list* of the *element_access*. @@ -1915,16 +2103,16 @@ The binding-time processing of an indexer access of the form `P[A]`, where `P` i - The set of indexers provided by `T` is constructed. The set consists of all indexers declared in `T` or a base type of `T` that are not override declarations and are accessible in the current context ([§7.5](basic-concepts.md#75-member-access)). - The set is reduced to those indexers that are applicable and not hidden by other indexers. The following rules are applied to each indexer `S.I` in the set, where `S` is the type in which the indexer `I` is declared: - - If `I` is not applicable with respect to `A` ([§11.6.4.2](expressions.md#11642-applicable-function-member)), then `I` is removed from the set. - - If `I` is applicable with respect to `A` ([§11.6.4.2](expressions.md#11642-applicable-function-member)), then all indexers declared in a base type of `S` are removed from the set. - - If `I` is applicable with respect to `A` ([§11.6.4.2](expressions.md#11642-applicable-function-member)) and `S` is a class type other than `object`, all indexers declared in an interface are removed from the set. + - If `I` is not applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)), then `I` is removed from the set. + - If `I` is applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)), then all indexers declared in a base type of `S` are removed from the set. + - If `I` is applicable with respect to `A` ([§12.6.4.2](expressions.md#12642-applicable-function-member)) and `S` is a class type other than `object`, all indexers declared in an interface are removed from the set. - If the resulting set of candidate indexers is empty, then no applicable indexers exist, and a binding-time error occurs. -- The best indexer of the set of candidate indexers is identified using the overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution). If a single best indexer cannot be identified, the indexer access is ambiguous, and a binding-time error occurs. +- The best indexer of the set of candidate indexers is identified using the overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). If a single best indexer cannot be identified, the indexer access is ambiguous, and a binding-time error occurs. - The index expressions of the *argument_list* are evaluated in order, from left to right. The result of processing the indexer access is an expression classified as an indexer access. The indexer access expression references the indexer determined in the step above, and has an associated instance expression of `P` and an associated argument list of `A`, and an associated type that is the type of the indexer. If `T` is a class type, the associated type is picked from the first declaration or override of the indexer found when starting with `T` and searching through its base classes. -Depending on the context in which it is used, an indexer access causes invocation of either the *get_accessor* or the *set_accessor* of the indexer. If the indexer access is the target of an assignment, the *set_accessor* is invoked to assign a new value ([§11.19.2](expressions.md#11192-simple-assignment)). In all other cases, the *get_accessor* is invoked to obtain the current value ([§11.2.2](expressions.md#1122-values-of-expressions)). +Depending on the context in which it is used, an indexer access causes invocation of either the *get_accessor* or the *set_accessor* of the indexer. If the indexer access is the target of an assignment, the *set_accessor* is invoked to assign a new value ([§12.21.2](expressions.md#12212-simple-assignment)). In all other cases, the *get_accessor* is invoked to obtain the current value ([§12.2.2](expressions.md#1222-values-of-expressions)). -### 11.7.11 Null Conditional Element Access +### 12.8.12 Null Conditional Element Access A *null_conditional_element_access* consists of a *primary_no_array_creation_expression* followed by the two tokens “`?`” and “`[`”, followed by an *argument_list*, followed by a “`]`” token, followed by zero or more *dependent_access*es. @@ -1935,25 +2123,49 @@ null_conditional_element_access ; ``` -A *null_conditional_element_access* is a conditional version of *element_access* ([§11.7.10](expressions.md#11710-element-access)) and it is a binding time error if the result type is `void`. For a null conditional expression where the result type may be `void` see ([§11.7.9](expressions.md#1179-null-conditional-invocation-expression)). +A *null_conditional_element_access* is a conditional version of *element_access* ([§12.8.11](expressions.md#12811-element-access)) and it is a binding time error if the result type is `void`. For a null conditional expression where the result type may be `void` see ([§12.8.10](expressions.md#12810-null-conditional-invocation-expression)). -A *null_conditional_element_access* expression `E` is of the form `P?[A]B`; where `B` are the *dependent_access*es, if any. Let `T` be the type of the expression `P[A]B`. The meaning of `E` is determined as follows: +A *null_conditional_element_access* expression `E` is of the form `P?[A]B`; where `B` are the *dependent_access*es, if any. The meaning of `E` is determined as follows: -- If `T` is a type parameter that is not known to be a reference type or a non-nullable value type, a compile-time error occurs. -- If `T` is a non-nullable value type, then the type of `E` is `T?`, and the meaning of `E` is the same as the meaning of: +- If the type of `P` is a nullable value type: - ```csharp - ((object)P == null) ? (T?)null : P[A]B - ``` + Let `T` be the type of the expression `P.Value[A]B`. + + - If `T` is a type parameter that is not known to be either a reference type or a non-nullable value type, a compile-time error occurs. + - If `T` is a non-nullable value type, then the type of `E` is `T?`, and the meaning of `E` is the same as the meaning of: - Except that `P` is evaluated only once. -- Otherwise the type of `E` is `T`, and the meaning of `E` is the same as the meaning of: + ```csharp + ((object)P == null) ? (T?)null : P.Value[A]B + ``` - ```csharp - ((object)P == null) ? null : P[A]B - ``` + Except that `P` is evaluated only once. + - Otherwise the type of `E` is `T`, and the meaning of `E` is the same as the meaning of: + + ```csharp + ((object)P == null) ? null : P.Value[A]B + ``` + + Except that `P` is evaluated only once. + +- Otherwise: + + Let `T` be the type of the expression `P[A]B`. + + - If `T` is a type parameter that is not known to be either a reference type or a non-nullable value type, a compile-time error occurs. + - If `T` is a non-nullable value type, then the type of `E` is `T?`, and the meaning of `E` is the same as the meaning of: + + ```csharp + ((object)P == null) ? (T?)null : P[A]B + ``` - Except that `P` is evaluated only once. + Except that `P` is evaluated only once. + - Otherwise the type of `E` is `T`, and the meaning of `E` is the same as the meaning of: + + ```csharp + ((object)P == null) ? null : P[A]B + ``` + + Except that `P` is evaluated only once. > *Note*: In an expression of the form: > @@ -1961,11 +2173,11 @@ A *null_conditional_element_access* expression `E` is of the form `P?[A]B`; wher > P?[A₀]?[A₁] > ``` > -> if `P` evaluates to `null` neither `A₀` or `A₁` are evaluated. The same is true if an expression is a sequence of *null_conditional_element_access* or *null_conditional_member_access* [§11.7.7](expressions.md#1177-null-conditional-member-access) operations. +> if `P` evaluates to `null` neither `A₀` or `A₁` are evaluated. The same is true if an expression is a sequence of *null_conditional_element_access* or *null_conditional_member_access* [§12.8.8](expressions.md#1288-null-conditional-member-access) operations. > > *end note* -### 11.7.12 This access +### 12.8.13 This access A *this_access* consists of the keyword `this`. @@ -1975,20 +2187,22 @@ this_access ; ``` -A *this_access* is permitted only in the *block* of an instance constructor, an instance method, an instance accessor ([§11.2.1](expressions.md#1121-general)), or a finalizer. It has one of the following meanings: +A *this_access* is permitted only in the *block* of an instance constructor, an instance method, an instance accessor ([§12.2.1](expressions.md#1221-general)), or a finalizer. It has one of the following meanings: -- When `this` is used in a *primary_expression* within an instance constructor of a class, it is classified as a value. The type of the value is the instance type ([§14.3.2](classes.md#1432-the-instance-type)) of the class within which the usage occurs, and the value is a reference to the object being constructed. -- When `this` is used in a *primary_expression* within an instance method or instance accessor of a class, it is classified as a value. The type of the value is the instance type ([§14.3.2](classes.md#1432-the-instance-type)) of the class within which the usage occurs, and the value is a reference to the object for which the method or accessor was invoked. -- When `this` is used in a *primary_expression* within an instance constructor of a struct, it is classified as a variable. The type of the variable is the instance type ([§14.3.2](classes.md#1432-the-instance-type)) of the struct within which the usage occurs, and the variable represents the struct being constructed. +- When `this` is used in a *primary_expression* within an instance constructor of a class, it is classified as a value. The type of the value is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the class within which the usage occurs, and the value is a reference to the object being constructed. +- When `this` is used in a *primary_expression* within an instance method or instance accessor of a class, it is classified as a value. The type of the value is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the class within which the usage occurs, and the value is a reference to the object for which the method or accessor was invoked. +- When `this` is used in a *primary_expression* within an instance constructor of a struct, it is classified as a variable. The type of the variable is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the struct within which the usage occurs, and the variable represents the struct being constructed. - If the constructor declaration has no constructor initializer, the `this` variable behaves exactly the same as an `out` parameter of the struct type. In particular, this means that the variable shall be definitely assigned in every execution path of the instance constructor. - Otherwise, the `this` variable behaves exactly the same as a `ref` parameter of the struct type. In particular, this means that the variable is considered initially assigned. -- When `this` is used in a *primary_expression* within an instance method or instance accessor of a struct, it is classified as a variable. The type of the variable is the instance type ([§14.3.2](classes.md#1432-the-instance-type)) of the struct within which the usage occurs. - - If the method or accessor is not an iterator ([§14.14](classes.md#1414-iterators)) or async function ([§14.15](classes.md#1415-async-functions)), the `this` variable represents the struct for which the method or accessor was invoked, and behaves exactly the same as a `ref` parameter of the struct type. +- When `this` is used in a *primary_expression* within an instance method or instance accessor of a struct, it is classified as a variable. The type of the variable is the instance type ([§15.3.2](classes.md#1532-the-instance-type)) of the struct within which the usage occurs. + - If the method or accessor is not an iterator ([§15.14](classes.md#1514-iterators)) or async function ([§15.15](classes.md#1515-async-functions)), the `this` variable represents the struct for which the method or accessor was invoked. + - If the struct is a `readonly struct`, the `this` variable behaves exactly the same as an `in` parameter of the struct type + - Otherwise the `this` variable behaves exactly the same as a `ref` parameter of the struct type - If the method or accessor is an iterator or async function, the `this` variable represents a *copy* of the struct for which the method or accessor was invoked, and behaves exactly the same as a *value* parameter of the struct type. Use of `this` in a *primary_expression* in a context other than the ones listed above is a compile-time error. In particular, it is not possible to refer to `this` in a static method, a static property accessor, or in a *variable_initializer* of a field declaration. -### 11.7.13 Base access +### 12.8.14 Base access A *base_access* consists of the keyword base followed by either a “`.`” token and an identifier and optional *type_argument_list* or an *argument_list* enclosed in square brackets: @@ -1999,15 +2213,15 @@ base_access ; ``` -A *base_access* is used to access base class members that are hidden by similarly named members in the current class or struct. A *base_access* is permitted only in the *block* of an instance constructor, an instance method, an instance accessor ([§11.2.1](expressions.md#1121-general)), or a finalizer. When `base.I` occurs in a class or struct, I shall denote a member of the base class of that class or struct. Likewise, when `base[E]` occurs in a class, an applicable indexer shall exist in the base class. +A *base_access* is used to access base class members that are hidden by similarly named members in the current class or struct. A *base_access* is permitted only in the *block* of an instance constructor, an instance method, an instance accessor ([§12.2.1](expressions.md#1221-general)), or a finalizer. When `base.I` occurs in a class or struct, I shall denote a member of the base class of that class or struct. Likewise, when `base[E]` occurs in a class, an applicable indexer shall exist in the base class. At binding-time, *base_access* expressions of the form `base.I` and `base[E]` are evaluated exactly as if they were written `((B)this).I` and `((B)this)[E]`, where `B` is the base class of the class or struct in which the construct occurs. Thus, `base.I` and `base[E]` correspond to `this.I` and `this[E]`, except `this` is viewed as an instance of the base class. -When a *base_access* references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time ([§11.6.6](expressions.md#1166-function-member-invocation)) is changed. The function member that is invoked is determined by finding the most derived implementation ([§14.6.4](classes.md#1464-virtual-methods)) of the function member with respect to `B` (instead of with respect to the run-time type of `this`, as would be usual in a non-base access). Thus, within an override of a virtual function member, a *base_access* can be used to invoke the inherited implementation of the function member. If the function member referenced by a *base_access* is abstract, a binding-time error occurs. +When a *base_access* references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time ([§12.6.6](expressions.md#1266-function-member-invocation)) is changed. The function member that is invoked is determined by finding the most derived implementation ([§15.6.4](classes.md#1564-virtual-methods)) of the function member with respect to `B` (instead of with respect to the run-time type of `this`, as would be usual in a non-base access). Thus, within an override of a virtual function member, a *base_access* can be used to invoke the inherited implementation of the function member. If the function member referenced by a *base_access* is abstract, a binding-time error occurs. -> *Note*: Unlike `this`, `base` is not an expression in itself. It is a keyword only used in the context of a *base_access* or a *constructor_initializer* ([§14.11.2](classes.md#14112-constructor-initializers)). *end note* +> *Note*: Unlike `this`, `base` is not an expression in itself. It is a keyword only used in the context of a *base_access* or a *constructor_initializer* ([§15.11.2](classes.md#15112-constructor-initializers)). *end note* -### 11.7.14 Postfix increment and decrement operators +### 12.8.15 Postfix increment and decrement operators ```ANTLR post_increment_expression @@ -2021,11 +2235,11 @@ post_decrement_expression The operand of a postfix increment or decrement operation shall be an expression classified as a variable, a property access, or an indexer access. The result of the operation is a value of the same type as the operand. -If the *primary_expression* has the compile-time type `dynamic` then the operator is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)), the *post_increment_expression* or *post_decrement_expression* has the compile-time type `dynamic` and the following rules are applied at run-time using the run-time type of the *primary_expression*. +If the *primary_expression* has the compile-time type `dynamic` then the operator is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)), the *post_increment_expression* or *post_decrement_expression* has the compile-time type `dynamic` and the following rules are applied at run-time using the run-time type of the *primary_expression*. If the operand of a postfix increment or decrement operation is a property or indexer access, the property or indexer shall have both a get and a set accessor. If this is not the case, a binding-time error occurs. -Unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. +Unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. There shall be an implicit conversion from the return type of the selected unary operator to the type of the *primary_expression*, otherwise a compile-time error occurs. @@ -2044,13 +2258,13 @@ The run-time processing of a postfix increment or decrement operation of the for - The value returned by the operator is converted to the type of `x` and the set accessor of `x` is invoked with this value as its value argument. - The saved value of `x` becomes the result of the operation. -The `++` and `--` operators also support prefix notation ([§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)). Typically, the result of `x++` or `x--` is the value of `X` *before* the operation, whereas the result of `++x` or `--x` is the value of `X` *after* the operation. In either case, `x` itself has the same value after the operation. +The `++` and `--` operators also support prefix notation ([§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)). Typically, the result of `x++` or `x--` is the value of `X` *before* the operation, whereas the result of `++x` or `--x` is the value of `X` *after* the operation. In either case, `x` itself has the same value after the operation. An operator `++` or operator `--` implementation can be invoked using either postfix or prefix notation. It is not possible to have separate operator implementations for the two notations. -### 11.7.15 The new operator +### 12.8.16 The new operator -#### 11.7.15.1 General +#### 12.8.16.1 General The `new` operator is used to create new instances of types. @@ -2064,7 +2278,7 @@ The `new` operator implies creation of an instance of a type, but does not neces > *Note*: Delegate creation expressions do not always create new instances. When the expression is processed in the same way as a method group conversion ([§10.8](conversions.md#108-method-group-conversions)) or an anonymous function conversion ([§10.7](conversions.md#107-anonymous-function-conversions)) this may result in an existing delegate instance being reused. *end note* -#### 11.7.15.2 Object creation expressions +#### 12.8.16.2 Object creation expressions An *object_creation_expression* is used to create a new instance of a *class_type* or a *value_type*. @@ -2080,26 +2294,26 @@ object_or_collection_initializer ; ``` -The *type* of an *object_creation_expression* shall be a *class_type*, a *value_type*, or a *type_parameter*. The *type* cannot be an abstract or static *class_type*. +The *type* of an *object_creation_expression* shall be a *class_type*, a *value_type*, or a *type_parameter*. The *type* cannot be a *tuple_type* or an abstract or static *class_type*. -The optional *argument_list* ([§11.6.2](expressions.md#1162-argument-lists)) is permitted only if the *type* is a *class_type* or a *struct_type*. +The optional *argument_list* ([§12.6.2](expressions.md#1262-argument-lists)) is permitted only if the *type* is a *class_type* or a *struct_type*. An object creation expression can omit the constructor argument list and enclosing parentheses provided it includes an object initializer or collection initializer. Omitting the constructor argument list and enclosing parentheses is equivalent to specifying an empty argument list. -Processing of an object creation expression that includes an object initializer or collection initializer consists of first processing the instance constructor and then processing the member or element initializations specified by the object initializer ([§11.7.15.3](expressions.md#117153-object-initializers)) or collection initializer ([§11.7.15.4](expressions.md#117154-collection-initializers)). +Processing of an object creation expression that includes an object initializer or collection initializer consists of first processing the instance constructor and then processing the member or element initializations specified by the object initializer ([§12.8.16.3](expressions.md#128163-object-initializers)) or collection initializer ([§12.8.16.4](expressions.md#128164-collection-initializers)). -If any of the arguments in the optional *argument_list* has the compile-time type `dynamic` then the *object_creation_expression* is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)) and the following rules are applied at run-time using the run-time type of those arguments of the *argument_list* that have the compile-time type `dynamic`. However, the object creation undergoes a limited compile-time check as described in [§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation). +If any of the arguments in the optional *argument_list* has the compile-time type `dynamic` then the *object_creation_expression* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) and the following rules are applied at run-time using the run-time type of those arguments of the *argument_list* that have the compile-time type `dynamic`. However, the object creation undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation). The binding-time processing of an *object_creation_expression* of the form new `T(A)`, where `T` is a *class_type*, or a *value_type*, and `A` is an optional *argument_list*, consists of the following steps: - If `T` is a *value_type* and `A` is not present: - The *object_creation_expression* is a default constructor invocation. The result of the *object_creation_expression* is a value of type `T`, namely the default value for `T` as defined in [§8.3.3](types.md#833-default-constructors). - Otherwise, if `T` is a *type_parameter* and `A` is not present: - - If no value type constraint or constructor constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)) has been specified for `T`, a binding-time error occurs. + - If no value type constraint or constructor constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)) has been specified for `T`, a binding-time error occurs. - The result of the *object_creation_expression* is a value of the run-time type that the type parameter has been bound to, namely the result of invoking the default constructor of that type. The run-time type may be a reference type or a value type. - Otherwise, if `T` is a *class_type* or a *struct_type*: - If `T` is an abstract or static *class_type*, a compile-time error occurs. - - The instance constructor to invoke is determined using the overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution). The set of candidate instance constructors consists of all accessible instance constructors declared in `T`, which are applicable with respect to A ([§11.6.4.2](expressions.md#11642-applicable-function-member)). If the set of candidate instance constructors is empty, or if a single best instance constructor cannot be identified, a binding-time error occurs. + - The instance constructor to invoke is determined using the overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). The set of candidate instance constructors consists of all accessible instance constructors declared in `T`, which are applicable with respect to A ([§12.6.4.2](expressions.md#12642-applicable-function-member)). If the set of candidate instance constructors is empty, or if a single best instance constructor cannot be identified, a binding-time error occurs. - The result of the *object_creation_expression* is a value of type `T`, namely the value produced by invoking the instance constructor determined in the step above. - Otherwise, the *object_creation_expression* is invalid, and a binding-time error occurs. @@ -2110,12 +2324,12 @@ The run-time processing of an *object_creation_expression* of the form new `T(A) - If `T` is a *class_type*: - A new instance of class `T` is allocated. If there is not enough memory available to allocate the new instance, a `System.OutOfMemoryException` is thrown and no further steps are executed. - All fields of the new instance are initialized to their default values ([§9.3](variables.md#93-default-values)). - - The instance constructor is invoked according to the rules of function member invocation ([§11.6.6](expressions.md#1166-function-member-invocation)). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. + - The instance constructor is invoked according to the rules of function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. - If `T` is a *struct_type*: - An instance of type `T` is created by allocating a temporary local variable. Since an instance constructor of a *struct_type* is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary. - - The instance constructor is invoked according to the rules of function member invocation ([§11.6.6](expressions.md#1166-function-member-invocation)). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. + - The instance constructor is invoked according to the rules of function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this. -#### 11.7.15.3 Object initializers +#### 12.8.16.3 Object initializers An ***object initializer*** specifies values for zero or more fields, properties, or indexed elements of an object. @@ -2150,11 +2364,11 @@ An object initializer consists of a sequence of member initializers, enclosed by Each *initializer_target* is followed by an equals sign and either an expression, an object initializer or a collection initializer. It is not possible for expressions within the object initializer to refer to the newly created object it is initializing. -A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment ([§11.19.2](expressions.md#11192-simple-assignment)) to the target. +A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment ([§12.21.2](expressions.md#12212-simple-assignment)) to the target. A member initializer that specifies an object initializer after the equals sign is a ***nested object initializer***, i.e., an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type. -A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the target field, property, or indexer, the elements given in the initializer are added to the collection referenced by the target. The target shall be of a collection type that satisfies the requirements specified in [§11.7.15.4](expressions.md#117154-collection-initializers). +A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the target field, property, or indexer, the elements given in the initializer are added to the collection referenced by the target. The target shall be of a collection type that satisfies the requirements specified in [§12.8.16.4](expressions.md#128164-collection-initializers). When an initializer target refers to an indexer, the arguments to the indexer shall always be evaluated exactly once. Thus, even if the arguments end up never getting used (e.g., because of an empty nested initializer), they are evaluated for their side effects. @@ -2264,7 +2478,7 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > > *end example* -#### 11.7.15.4 Collection initializers +#### 12.8.16.4 Collection initializers A collection initializer specifies the elements of a collection. @@ -2289,7 +2503,7 @@ expression_list ; ``` -A collection initializer consists of a sequence of element initializers, enclosed by `{` and `}` tokens and separated by commas. Each element initializer specifies an element to be added to the collection object being initialized, and consists of a list of expressions enclosed by `{` and `}` tokens and separated by commas. A single-expression element initializer can be written without braces, but cannot then be an assignment expression, to avoid ambiguity with member initializers. The *non_assignment_expression* production is defined in [§11.20](expressions.md#1120-expression). +A collection initializer consists of a sequence of element initializers, enclosed by `{` and `}` tokens and separated by commas. Each element initializer specifies an element to be added to the collection object being initialized, and consists of a list of expressions enclosed by `{` and `}` tokens and separated by commas. A single-expression element initializer can be written without braces, but cannot then be an assignment expression, to avoid ambiguity with member initializers. The *non_assignment_expression* production is defined in [§12.22](expressions.md#1222-expression). > *Example*: > The following is an example of an object creation expression that includes a collection initializer: @@ -2301,7 +2515,7 @@ A collection initializer consists of a sequence of element initializers, enclose > > *end example* -The collection object to which a collection initializer is applied shall be of a type that implements `System.Collections.IEnumerable` or a compile-time error occurs. For each specified element in order, normal member lookup is applied to find a member named `Add`. If the result of the member lookup is not a method group, a compile-time error occurs. Otherwise, overload resolution is applied with the expression list of the element initializer as the argument list, and the collection initializer invokes the resulting method. Thus, the collection object shall contain an applicable instance or extension method with the name `Add` for each element initializer. +The collection object to which a collection initializer is applied shall be of a type that implements `System.Collections.IEnumerable` or a compile-time error occurs. For each specified element in order from left to right, normal member lookup is applied to find a member named `Add`. If the result of the member lookup is not a method group, a compile-time error occurs. Otherwise, overload resolution is applied with the expression list of the element initializer as the argument list, and the collection initializer invokes the resulting method. Thus, the collection object shall contain an applicable instance or extension method with the name `Add` for each element initializer. > *Example*:The following shows a class that represents a contact with a name and a list of phone numbers, and the creation and initialization of a `List`: > @@ -2354,7 +2568,7 @@ The collection object to which a collection initializer is applied shall be of a > > *end example* -#### 11.7.15.5 Array creation expressions +#### 12.8.16.5 Array creation expressions An *array_creation_expression* is used to create a new instance of an *array_type*. @@ -2373,7 +2587,7 @@ An array creation expression of the first form allocates an array instance of th Each expression in the expression list shall be of type `int`, `uint`, `long`, or `ulong`, or implicitly convertible to one or more of these types. The value of each expression determines the length of the corresponding dimension in the newly allocated array instance. Since the length of an array dimension shall be nonnegative, it is a compile-time error to have a constant expression with a negative value, in the expression list. -Except in an unsafe context ([§22.2](unsafe-code.md#222-unsafe-contexts)), the layout of arrays is unspecified. +Except in an unsafe context ([§23.2](unsafe-code.md#232-unsafe-contexts)), the layout of arrays is unspecified. If an array creation expression of the first form includes an array initializer, each expression in the expression list shall be a constant and the rank and dimension lengths specified by the expression list shall match those of the array initializer. @@ -2391,9 +2605,9 @@ exactly corresponds to var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}}; ``` -An array creation expression of the third form is referred to as an ***implicitly typed array-creation expression***. It is similar to the second form, except that the element type of the array is not explicitly given, but determined as the best common type ([§11.6.3.15](expressions.md#116315-finding-the-best-common-type-of-a-set-of-expressions)) of the set of expressions in the array initializer. For a multidimensional array, i.e., one where the *rank_specifier* contains at least one comma, this set comprises all *expression*s found in nested *array_initializer*s. +An array creation expression of the third form is referred to as an ***implicitly typed array-creation expression***. It is similar to the second form, except that the element type of the array is not explicitly given, but determined as the best common type ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)) of the set of expressions in the array initializer. For a multidimensional array, i.e., one where the *rank_specifier* contains at least one comma, this set comprises all *expression*s found in nested *array_initializer*s. -Array initializers are described further in [§16.7](arrays.md#167-array-initializers). +Array initializers are described further in [§17.7](arrays.md#177-array-initializers). The result of evaluating an array creation expression is classified as a value, namely a reference to the newly allocated array instance. The run-time processing of an array creation expression consists of the following steps: @@ -2461,7 +2675,7 @@ An array creation expression permits instantiation of an array with elements of > > *end example* -Implicitly typed array creation expressions can be combined with anonymous object initializers ([§11.7.15.7](expressions.md#117157-anonymous-object-creation-expressions)) to create anonymously typed data structures. +Implicitly typed array creation expressions can be combined with anonymous object initializers ([§12.8.16.7](expressions.md#128167-anonymous-object-creation-expressions)) to create anonymously typed data structures. > *Example*: > @@ -2484,7 +2698,7 @@ Implicitly typed array creation expressions can be combined with anonymous objec > > *end example* -#### 11.7.15.6 Delegate creation expressions +#### 12.8.16.6 Delegate creation expressions A *delegate_creation_expression* is used to obtain an instance of a *delegate_type*. @@ -2496,14 +2710,14 @@ delegate_creation_expression The argument of a delegate creation expression shall be a method group, an anonymous function, or a value of either the compile-time type `dynamic` or a *delegate_type*. If the argument is a method group, it identifies the method and, for an instance method, the object for which to create a delegate. If the argument is an anonymous function it directly defines the parameters and method body of the delegate target. If the argument is a value it identifies a delegate instance of which to create a copy. -If the *expression* has the compile-time type `dynamic`, the *delegate_creation_expression* is dynamically bound ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)), and the rules below are applied at run-time using the run-time type of the *expression*. Otherwise, the rules are applied at compile-time. +If the *expression* has the compile-time type `dynamic`, the *delegate_creation_expression* is dynamically bound ([§12.8.16.6](expressions.md#128166-delegate-creation-expressions)), and the rules below are applied at run-time using the run-time type of the *expression*. Otherwise, the rules are applied at compile-time. The binding-time processing of a *delegate_creation_expression* of the form new `D(E)`, where `D` is a *delegate_type* and `E` is an *expression*, consists of the following steps: - If `E` is a method group, the delegate creation expression is processed in the same way as a method group conversion ([§10.8](conversions.md#108-method-group-conversions)) from `E` to `D`. - If `E` is an anonymous function, the delegate creation expression is processed in the same way as an anonymous function conversion ([§10.7](conversions.md#107-anonymous-function-conversions)) from `E` to `D`. -- If `E` is a value, `E` shall be compatible ([§19.2](delegates.md#192-delegate-declarations)) with `D`, and the result is a reference to a newly created delegate with a single-entry invocation list that invokes  `E`. +- If `E` is a value, `E` shall be compatible ([§20.2](delegates.md#202-delegate-declarations)) with `D`, and the result is a reference to a newly created delegate with a single-entry invocation list that invokes  `E`. The run-time processing of a *delegate_creation_expression* of the form new `D(E)`, where `D` is a *delegate_type* and `E` is an *expression*, consists of the following steps: @@ -2540,7 +2754,7 @@ It is not possible to create a delegate that refers to a property, indexer, user > > *end example* -#### 11.7.15.7 Anonymous object creation expressions +#### 12.8.16.7 Anonymous object creation expressions An *anonymous_object_creation_expression* is used to create an object of an anonymous type. @@ -2598,7 +2812,7 @@ class __Anonymous1 } ``` -where each «Tx» is the type of the corresponding expression «ex». The expression used in a *member_declarator* shall have a type. Thus, it is a compile-time error for an expression in a *member_declarator* to be `null` or an anonymous function. It is also a compile-time error for the expression to have a pointer type ([§22.3](unsafe-code.md#223-pointer-types)). +where each «Tx» is the type of the corresponding expression «ex». The expression used in a *member_declarator* shall have a type. Thus, it is a compile-time error for an expression in a *member_declarator* to be `null` or an anonymous function. The names of an anonymous type and of the parameter to its `Equals` method are automatically generated by the compiler and cannot be referenced in program text. @@ -2619,7 +2833,7 @@ Within the same program, two anonymous object initializers that specify a sequen The `Equals` and `GetHashcode` methods on anonymous types override the methods inherited from `object`, and are defined in terms of the `Equals` and `GetHashcode` of the properties, so that two instances of the same anonymous type are equal if and only if all their properties are equal. -A member declarator can be abbreviated to a simple name ([§11.7.4](expressions.md#1174-simple-names)), a member access ([§11.7.6](expressions.md#1176-member-access)), a null conditional projection initializer [§11.7.7](expressions.md#1177-null-conditional-member-access) or a base access ([§11.7.13](expressions.md#11713-base-access)). This is called a ***projection initializer*** and is shorthand for a declaration of and assignment to a property with the same name. Specifically, member declarators of the forms +A member declarator can be abbreviated to a simple name ([§12.8.4](expressions.md#1284-simple-names)), a member access ([§12.8.7](expressions.md#1287-member-access)), a null conditional projection initializer [§12.8.8](expressions.md#1288-null-conditional-member-access) or a base access ([§12.8.14](expressions.md#12814-base-access)). This is called a ***projection initializer*** and is shorthand for a declaration of and assignment to a property with the same name. Specifically, member declarators of the forms `«identifier»`, `«expr» . «identifier»` and `«expr» ? . «identifier»` @@ -2629,7 +2843,7 @@ are precisely equivalent to the following, respectively: Thus, in a projection initializer the identifier selects both the value and the field or property to which the value is assigned. Intuitively, a projection initializer projects not just a value, but also the name of the value. -### 11.7.16 The typeof operator +### 12.8.17 The typeof operator The `typeof` operator is used to obtain the `System.Type` object for a type. @@ -2672,9 +2886,9 @@ The result of the *typeof_expression* is the `System.Type` object for the result The third form of *typeof_expression* consists of a `typeof` keyword followed by a parenthesized `void` keyword. The result of an expression of this form is the `System.Type` object that represents the absence of a type. The type object returned by `typeof(void)` is distinct from the type object returned for any type. -> *Note*: This special type `object` is useful in class libraries that allow reflection onto methods in the language, where those methods wish to have a way to represent the return type of any method, including `void` methods, with an instance of `System.Type`. *end note* +> *Note*: This special `System.Type` object is useful in class libraries that allow reflection onto methods in the language, where those methods wish to have a way to represent the return type of any method, including `void` methods, with an instance of `System.Type`. *end note* -The `typeof` operator can be used on a type parameter. The result is the `System.Type` object for the run-time type that was bound to the type parameter. The `typeof` operator can also be used on a constructed type or an unbound generic type ([§8.4.4](types.md#844-bound-and-unbound-types)). The `System.Type` object for an unbound generic type is not the same as the `System.Type` object of the instance type ([§14.3.2](classes.md#1432-the-instance-type)). The instance type is always a closed constructed type at run-time so its `System.Type` object depends on the run-time type arguments in use. The unbound generic type, on the other hand, has no type arguments, and yields the same `System.Type` object regardless of runtime type arguments. +The `typeof` operator can be used on a type parameter. The result is the `System.Type` object for the run-time type that was bound to the type parameter. The `typeof` operator can also be used on a constructed type or an unbound generic type ([§8.4.4](types.md#844-bound-and-unbound-types)). The `System.Type` object for an unbound generic type is not the same as the `System.Type` object of the instance type ([§15.3.2](classes.md#1532-the-instance-type)). The instance type is always a closed constructed type at run-time so its `System.Type` object depends on the run-time type arguments in use. The unbound generic type, on the other hand, has no type arguments, and yields the same `System.Type` object regardless of runtime type arguments. > *Example*: The example > @@ -2731,7 +2945,7 @@ The `typeof` operator can be used on a type parameter. The result is the `System > > *end example* -### 11.7.17 The sizeof operator +### 12.8.18 The sizeof operator The `sizeof` operator returns the number of 8-bit bytes occupied by a variable of a given type. The type specified as an operand to sizeof shall be an *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)). @@ -2759,9 +2973,9 @@ For certain predefined types the `sizeof` operator yields a constant `int` value `sizeof(bool)` | 1 `sizeof(decimal)` | 16 -For an enum type `T`, the result of the expression `sizeof(T)` is a constant value equal to the size of its underlying type, as given above. For all other operand types, the `sizeof` operator is specified in [§22.6.9](unsafe-code.md#2269-the-sizeof-operator). +For an enum type `T`, the result of the expression `sizeof(T)` is a constant value equal to the size of its underlying type, as given above. For all other operand types, the `sizeof` operator is specified in [§23.6.9](unsafe-code.md#2369-the-sizeof-operator). -### 11.7.18 The checked and unchecked operators +### 12.8.19 The checked and unchecked operators The `checked` and `unchecked` operators are used to control the overflow-checking context for integral-type arithmetic operations and conversions. @@ -2775,25 +2989,25 @@ unchecked_expression ; ``` -The `checked` operator evaluates the contained expression in a checked context, and the `unchecked` operator evaluates the contained expression in an unchecked context. A *checked_expression* or *unchecked_expression* corresponds exactly to a *parenthesized_expression* ([§11.7.5](expressions.md#1175-parenthesized-expressions)), except that the contained expression is evaluated in the given overflow checking context. +The `checked` operator evaluates the contained expression in a checked context, and the `unchecked` operator evaluates the contained expression in an unchecked context. A *checked_expression* or *unchecked_expression* corresponds exactly to a *parenthesized_expression* ([§12.8.5](expressions.md#1285-parenthesized-expressions)), except that the contained expression is evaluated in the given overflow checking context. -The overflow checking context can also be controlled through the `checked` and `unchecked` statements ([§12.12](statements.md#1212-the-checked-and-unchecked-statements)). +The overflow checking context can also be controlled through the `checked` and `unchecked` statements ([§13.12](statements.md#1312-the-checked-and-unchecked-statements)). The following operations are affected by the overflow checking context established by the checked and unchecked operators and statements: -- The predefined `++` and `--` operators ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators) and [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)), when the operand is of an integral or enum type. -- The predefined `-` unary operator ([§11.8.3](expressions.md#1183-unary-minus-operator)), when the operand is of an integral type. -- The predefined `+`, `-`, `*`, and `/` binary operators ([§11.9](expressions.md#119-arithmetic-operators)), when both operands are of integral or enum types. +- The predefined `++` and `--` operators ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators) and [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)), when the operand is of an integral or enum type. +- The predefined `-` unary operator ([§12.9.3](expressions.md#1293-unary-minus-operator)), when the operand is of an integral type. +- The predefined `+`, `-`, `*`, and `/` binary operators ([§12.10](expressions.md#1210-arithmetic-operators)), when both operands are of integral or enum types. - Explicit numeric conversions ([§10.3.2](conversions.md#1032-explicit-numeric-conversions)) from one integral or enum type to another integral or enum type, or from `float` or `double` to an integral or enum type. When one of the above operations produces a result that is too large to represent in the destination type, the context in which the operation is performed controls the resulting behavior: -- In a `checked` context, if the operation is a constant expression ([§11.21](expressions.md#1121-constant-expressions)), a compile-time error occurs. Otherwise, when the operation is performed at run-time, a `System.OverflowException` is thrown. +- In a `checked` context, if the operation is a constant expression ([§12.23](expressions.md#1223-constant-expressions)), a compile-time error occurs. Otherwise, when the operation is performed at run-time, a `System.OverflowException` is thrown. - In an `unchecked` context, the result is truncated by discarding any high-order bits that do not fit in the destination type. -For non-constant expressions ([§11.21](expressions.md#1121-constant-expressions)) (expressions that are evaluated at run-time) that are not enclosed by any `checked` or `unchecked` operators or statements, the default overflow checking context is unchecked, unless external factors (such as compiler switches and execution environment configuration) call for checked evaluation. +For non-constant expressions ([§12.23](expressions.md#1223-constant-expressions)) (expressions that are evaluated at run-time) that are not enclosed by any `checked` or `unchecked` operators or statements, the default overflow checking context is unchecked, unless external factors (such as compiler switches and execution environment configuration) call for checked evaluation. -For constant expressions ([§11.21](expressions.md#1121-constant-expressions)) (expressions that can be fully evaluated at compile-time), the default overflow checking context is always checked. Unless a constant expression is explicitly placed in an `unchecked` context, overflows that occur during the compile-time evaluation of the expression always cause compile-time errors. +For constant expressions ([§12.23](expressions.md#1223-constant-expressions)) (expressions that can be fully evaluated at compile-time), the default overflow checking context is always checked. Unless a constant expression is explicitly placed in an `unchecked` context, overflows that occur during the compile-time evaluation of the expression always cause compile-time errors. The body of an anonymous function is not affected by `checked` or `unchecked` contexts in which the anonymous function occurs. @@ -2876,7 +3090,7 @@ The `unchecked` operator is convenient when writing constants of the signed inte > *Note*: The `checked` and `unchecked` operators and statements allow programmers to control certain aspects of some numeric calculations. However, the behavior of some numeric operators depends on their operands’ data types. For example, multiplying two decimals always results in an exception on overflow even within an explicitly unchecked construct. Similarly, multiplying two floats never results in an exception on overflow even within an explicitly checked construct. In addition, other operators are never affected by the mode of checking, whether default or explicit. *end note* -### 11.7.19 Default value expressions +### 12.8.20 Default value expressions A default value expression is used to obtain the default value ([§9.3](variables.md#93-default-values)) of a type. @@ -2895,18 +3109,118 @@ default_literal ; ``` -A *default_literal* represents a default value ([§9.3](variables.md#93-default-values)). It does not have a type, but can be converted to any type through a default literal conversion ([§10.2.15](conversions.md#10215-default-literal-conversions)). +A *default_literal* represents a default value ([§9.3](variables.md#93-default-values)). It does not have a type, but can be converted to any type through a default literal conversion ([§10.2.16](conversions.md#10216-default-literal-conversions)). The result of a *default_value_expression* is the default ([§9.3](variables.md#93-default-values)) of the explicit type in an *explictly_typed_default*, or the target type of the conversion for a *default_value_expression*. -A *default_value_expression* is a constant expression ([§11.21](expressions.md#1121-constant-expressions)) if the type is one of: +A *default_value_expression* is a constant expression ([§12.23](expressions.md#1223-constant-expressions)) if the type is one of: - a reference type - a type parameter that is known to be a reference type ([§8.2](types.md#82-reference-types)); - one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,`; or - any enumeration type. -### 11.7.20 Nameof expressions +### 12.8.21 Stack allocation + +A stack allocation expression allocates a block of memory from the execution stack. The ***execution stack*** is an area of memory where local variables are stored. The execution stack is not part of the managed heap. The memory used for local variable storage is automatically recovered when the current function returns. + +The safe context rules for a stack allocation expression are described in [§16.4.12.7](structs.md#164127-stackalloc). + +```ANTLR +stackalloc_expression + : 'stackalloc' unmanaged_type '[' expression ']' + | 'stackalloc' unmanaged_type? '[' constant_expression? ']' + stackalloc_initializer + ; + +stackalloc_initializer + : '{' stackalloc_initializer_element_list '}' + ; + +stackalloc_initializer_element_list + : stackalloc_element_initializer (',' stackalloc_element_initializer)* ','? + ; + +stackalloc_element_initializer + : expression + ; +``` + + +A *stackalloc_expression* is only permitted in two contexts: + +1. The initializing *expression*, `E`, of a *local_variable_declaration* ([§13.6.2](statements.md#1362-local-variable-declarations)); and +2. The right operand *expression*, `E`, of a simple assignment ([§12.21.2](expressions.md#12212-simple-assignment)) which itself occurs as a *expression_statement* ([§13.7](statements.md#137-expression-statements)) + +In both contexts the *stackalloc_expression* is only permitted to occur as: + +- The whole of `E`; or +- The second and/or third operands of a *conditional_expression* ([§12.18](expressions.md#1218-conditional-operator)) which is itself the whole of `E`. + + +The *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)) indicates the type of the items that will be stored in the newly allocated location, and the *expression* indicates the number of these items. Taken together, these specify the required allocation size. The type of *expression* must be implicitly convertible to the type `int`. + +As the size of a stack allocation cannot be negative, it is a compile-time error to specify the number of items as a *constant_expression* that evaluates to a negative value. + +At runtime if the number of items to be allocated is a negative value then the behavior is undefined. If it is zero, then no allocation is made, and the value returned is implementation-defined. If there is not enough memory available to allocate the items a `System.StackOverflowException` is thrown. + +When a *stackalloc_initializer* is present: + +- If *unmanaged_type* is omitted, it is inferred following the rules for best common type ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)) for the set of *stackalloc_element_initializer*s. +- If *constant_expression* is omitted it is inferred to be the number of *stackalloc_element_initializer*s. +- If *constant_expression* is present it must equal the number of *stackalloc_element_initializer*s. + +Each *stackalloc_element_initializer* shall have an implicit conversion to *unmanaged_type* ([§10.2](conversions.md#102-implicit-conversions)). The *stackalloc_element_initializer*s initialize elements in the allocated memory in increasing order, starting with the element at index zero. In the absence of a *stackalloc_initializer*, the content of the newly allocated memory is undefined. + +The result of a *stackalloc_expression* is an instance of type `Span`, where `T` is the *unmanaged_type*: + +- `Span` ([§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271)) is a ref struct type ([§16.2.3](structs.md#1623-ref-modifier)), which presents a block of memory, here the block allocated by the *stackalloc_expression*, as an indexable collection of typed (`T`) items. +- The result’s `Length` property returns the number of items allocated. +- The result’s indexer ([§15.9](classes.md#159-indexers)) returns a *variable_reference* ([§9.5](variables.md#95-variable-references)) to an item of the allocated block and is range checked. + +> *Note*: When occurring in unsafe code the result of a *stackalloc_expression* may be of a different type, see ([§23.9](unsafe-code.md#239-stack-allocation)). *end note* + +Stack allocation initializers are not permitted in `catch` or `finally` blocks ([§13.11](statements.md#1311-the-try-statement)). + +> *Note*: There is no way to explicitly free memory allocated using `stackalloc`. *end note* + +All stack-allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. + +Except for the `stackalloc` operator, C# provides no predefined constructs for managing non-garbage collected memory. Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. + +> *Example*: +> +> +> ```csharp +> // Memory uninitialized +> Span span1 = stackalloc int[3]; +> // Memory initialized +> Span span2 = stackalloc int[3] { -10, -15, -30 }; +> // Type int is inferred +> Span span3 = stackalloc[] { 11, 12, 13 }; +> // Error; result is int*, not allowed in a safe context +> var span4 = stackalloc[] { 11, 12, 13 }; +> // Error; no conversion from Span to Span +> Span span5 = stackalloc[] { 11, 12, 13 }; +> // Converts 11 and 13, and returns Span +> Span span6 = stackalloc[] { 11, 12L, 13 }; +> // Converts all and returns Span +> Span span7 = stackalloc long[] { 11, 12, 13 }; +> // Implicit conversion of Span +> ReadOnlySpan span8 = stackalloc int[] { 10, 22, 30 }; +> // Implicit conversion of Span +> Widget span9 = stackalloc double[] { 1.2, 5.6 }; +> +> public class Widget +> { +> public static implicit operator Widget(Span sp) { return null; } +> } +> ``` +> +> In the case of `span8`, `stackalloc` results in a `Span`, which is converted by an implicit operator to `ReadOnlySpan`. Similarly, for `span9`, the resulting `Span` is converted to the user-defined type `Widget` using the conversion, as shown. +> *end example* + +### 12.8.22 Nameof expressions A *nameof_expression* is used to obtain the name of a program entity as a constant string. @@ -2928,9 +3242,9 @@ named_entity_target ; ``` -Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§11.7.4](expressions.md#1174-simple-names)) of the name `nameof` succeeds, the expression is treated as an *invocation_expression* — regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*. +Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§12.8.4](expressions.md#1284-simple-names)) of the name `nameof` succeeds, the expression is treated as an *invocation_expression* — regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*. -Simple name and member access lookups are performed on the *named_entity* at compile time, following the rules described in [§11.7.4](expressions.md#1174-simple-names) and [§11.7.6](expressions.md#1176-member-access). However, where the lookup described in [§11.7.4](expressions.md#1174-simple-names) and [§11.7.6](expressions.md#1176-member-access) results in an error because an instance member was found in a static context, a *nameof_expression* produces no such error. +Simple name and member access lookups are performed on the *named_entity* at compile time, following the rules described in [§12.8.4](expressions.md#1284-simple-names) and [§12.8.7](expressions.md#1287-member-access). However, where the lookup described in [§12.8.4](expressions.md#1284-simple-names) and [§12.8.7](expressions.md#1287-member-access) results in an error because an instance member was found in a static context, a *nameof_expression* produces no such error. It is a compile-time error for a *named_entity* designating a method group to have a *type_argument_list*. It is a compile time error for a *named_entity_target* to have the type `dynamic`. @@ -2991,13 +3305,13 @@ These are the same transformations applied in [§6.4.3](lexical-structure.md#643 > Potentially surprising parts of this example are the resolution of `nameof(System.Collections.Generic)` to just “Generic” instead of the full namespace, and of `nameof(TestAlias)` to “TestAlias” rather than “String”. > *end example* -### 11.7.21 Anonymous method expressions +### 12.8.23 Anonymous method expressions -An *anonymous_method_expression* is one of two ways of defining an anonymous function. These are further described in [§11.17](expressions.md#1117-anonymous-function-expressions). +An *anonymous_method_expression* is one of two ways of defining an anonymous function. These are further described in [§12.19](expressions.md#1219-anonymous-function-expressions). -## 11.8 Unary operators +## 12.9 Unary operators -### 11.8.1 General +### 12.9.1 General The `+`, `-`, `!`, `~`, `++`, `--`, cast, and `await` operators are called the unary operators. @@ -3017,13 +3331,13 @@ unary_expression ; ``` -*pointer_indirection_expression* ([§22.6.2](unsafe-code.md#2262-pointer-indirection)) and *addressof_expression* ([§22.6.5](unsafe-code.md#2265-the-address-of-operator)) are available only in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*pointer_indirection_expression* ([§23.6.2](unsafe-code.md#2362-pointer-indirection)) and *addressof_expression* ([§23.6.5](unsafe-code.md#2365-the-address-of-operator)) are available only in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). -If the operand of a *unary_expression* has the compile-time type `dynamic`, it is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case, the compile-time type of the *unary_expression* is `dynamic`, and the resolution described below will take place at run-time using the run-time type of the operand. +If the operand of a *unary_expression* has the compile-time type `dynamic`, it is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the *unary_expression* is `dynamic`, and the resolution described below will take place at run-time using the run-time type of the operand. -### 11.8.2 Unary plus operator +### 12.9.2 Unary plus operator -For an operation of the form `+x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary plus operators are: +For an operation of the form `+x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary plus operators are: ```csharp int operator +(int x); @@ -3037,11 +3351,11 @@ decimal operator +(decimal x); For each of these operators, the result is simply the value of the operand. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined unary plus operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined unary plus operators defined above are also predefined. -### 11.8.3 Unary minus operator +### 12.9.3 Unary minus operator -For an operation of the form `–x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary minus operators are: +For an operation of the form `–x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary minus operators are: - Integer negation: @@ -3071,11 +3385,11 @@ For an operation of the form `–x`, unary operator overload resolution ([§11. The result is computed by subtracting `X` from zero. Decimal negation is equivalent to using the unary minus operator of type `System.Decimal`. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined unary minus operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined unary minus operators defined above are also predefined. -### 11.8.4 Logical negation operator +### 12.9.4 Logical negation operator -For an operation of the form `!x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined logical negation operator exists: +For an operation of the form `!x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined logical negation operator exists: ```csharp bool operator !(bool x); @@ -3083,11 +3397,11 @@ bool operator !(bool x); This operator computes the logical negation of the operand: If the operand is `true`, the result is `false`. If the operand is `false`, the result is `true`. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined logical negation operator defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined logical negation operator defined above are also predefined. -### 11.8.5 Bitwise complement operator +### 12.9.5 Bitwise complement operator -For an operation of the form `~x`, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined bitwise complement operators are: +For an operation of the form `~x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined bitwise complement operators are: ```csharp int operator ~(int x); @@ -3104,11 +3418,11 @@ Every enumeration type `E` implicitly provides the following bitwise complement E operator ~(E x); ``` -The result of evaluating `~x`, where `X` is an expression of an enumeration type `E` with an underlying type `U`, is exactly the same as evaluating `(E)(~(U)x)`, except that the conversion to `E` is always performed as if in an `unchecked` context ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)). +The result of evaluating `~x`, where `X` is an expression of an enumeration type `E` with an underlying type `U`, is exactly the same as evaluating `(E)(~(U)x)`, except that the conversion to `E` is always performed as if in an `unchecked` context ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)). -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined bitwise complement operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined bitwise complement operators defined above are also predefined. -### 11.8.6 Prefix increment and decrement operators +### 12.9.6 Prefix increment and decrement operators ```ANTLR pre_increment_expression @@ -3124,7 +3438,7 @@ The operand of a prefix increment or decrement operation shall be an expression If the operand of a prefix increment or decrement operation is a property or indexer access, the property or indexer shall have both a get and a set accessor. If this is not the case, a binding-time error occurs. -Unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a `checked` context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. +Unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. Predefined `++` and `--` operators exist for the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, and any enum type. The predefined `++` operators return the value produced by adding `1` to the operand, and the predefined `--` operators return the value produced by subtracting `1` from the operand. In a `checked` context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a `System.OverflowException` is thrown. There shall be an implicit conversion from the return type of the selected unary operator to the type of the *unary_expression*, otherwise a compile-time error occurs. @@ -3142,13 +3456,13 @@ The run-time processing of a prefix increment or decrement operation of the form - The value returned by the operator is converted to the type of `x`. The set accessor of `X` is invoked with this value as its value argument. - This value also becomes the result of the operation. -The `++` and `--` operators also support postfix notation ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators)). Typically, the result of `x++` or `x--` is the value of `X` before the operation, whereas the result of `++x` or `--x` is the value of `X` after the operation. In either case, `x` itself has the same value after the operation. +The `++` and `--` operators also support postfix notation ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators)). Typically, the result of `x++` or `x--` is the value of `X` before the operation, whereas the result of `++x` or `--x` is the value of `X` after the operation. In either case, `x` itself has the same value after the operation. An operator `++` or operator `--` implementation can be invoked using either postfix or prefix notation. It is not possible to have separate operator implementations for the two notations. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined prefix increment and decrement operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined prefix increment and decrement operators defined above are also predefined. -### 11.8.7 Cast expressions +### 12.9.7 Cast expressions A *cast_expression* is used to convert explicitly an expression to a given type. @@ -3177,9 +3491,9 @@ The term “correct grammar” above means only that the sequence of tokens shal > *Note*: From the disambiguation rule, it follows that, if `x` and `y` are identifiers, `(x)y`, `(x)(y)`, and `(x)(-y)` are *cast_expression*s, but `(x)-y` is not, even if `x` identifies a type. However, if `x` is a keyword that identifies a predefined type (such as `int`), then all four forms are *cast_expression*s (because such a keyword could not possibly be an expression by itself). *end note* -### 11.8.8 Await expressions +### 12.9.8 Await expressions -#### 11.8.8.1 General +#### 12.9.8.1 General The `await` operator is used to suspend evaluation of the enclosing async function until the asynchronous operation represented by the operand has completed. @@ -3189,7 +3503,7 @@ await_expression ; ``` -An *await_expression* is only allowed in the body of an async function ([§14.15](classes.md#1415-async-functions)). Within the nearest enclosing async function, an *await_expression* shall not occur in these places: +An *await_expression* is only allowed in the body of an async function ([§15.15](classes.md#1515-async-functions)). Within the nearest enclosing async function, an *await_expression* shall not occur in these places: - Inside a nested (non-async) anonymous function - Inside the block of a *lock_statement* @@ -3202,7 +3516,7 @@ Inside an async function, `await` shall not be used as an *available_identifier* The operand of an *await_expression* is called the ***task***. It represents an asynchronous operation that may or may not be complete at the time the *await_expression* is evaluated. The purpose of the `await` operator is to suspend execution of the enclosing async function until the awaited task is complete, and then obtain its outcome. -#### 11.8.8.2 Awaitable expressions +#### 12.9.8.2 Awaitable expressions The task of an *await_expression* is required to be ***awaitable***. An expression `t` is awaitable if one of the following holds: @@ -3220,17 +3534,17 @@ The purpose of the `INotifyCompletion.OnCompleted` method is to sign up a “con The purpose of the `GetResult` method is to obtain the outcome of the task once it is complete. This outcome may be successful completion, possibly with a result value, or it may be an exception which is thrown by the `GetResult` method. -#### 11.8.8.3 Classification of await expressions +#### 12.9.8.3 Classification of await expressions The expression `await t` is classified the same way as the expression `(t).GetAwaiter().GetResult()`. Thus, if the return type of `GetResult` is `void`, the *await_expression* is classified as nothing. If it has a non-`void` return type `T`, the *await_expression* is classified as a value of type `T`. -#### 11.8.8.4 Run-time evaluation of await expressions +#### 12.9.8.4 Run-time evaluation of await expressions At run-time, the expression `await t` is evaluated as follows: - An awaiter `a` is obtained by evaluating the expression `(t).GetAwaiter()`. - A `bool` `b` is obtained by evaluating the expression `(a).IsCompleted`. -- If `b` is `false` then evaluation depends on whether a implements the interface `System.Runtime.CompilerServices.ICriticalNotifyCompletion` (hereafter known as `ICriticalNotifyCompletion` for brevity). This check is done at binding time; i.e., at run-time if `a` has the compile-time type `dynamic`, and at compile-time otherwise. Let `r` denote the resumption delegate ([§14.15](classes.md#1415-async-functions)): +- If `b` is `false` then evaluation depends on whether `a` implements the interface `System.Runtime.CompilerServices.ICriticalNotifyCompletion` (hereafter known as `ICriticalNotifyCompletion` for brevity). This check is done at binding time; i.e., at run-time if `a` has the compile-time type `dynamic`, and at compile-time otherwise. Let `r` denote the resumption delegate ([§15.15](classes.md#1515-async-functions)): - If `a` does not implement `ICriticalNotifyCompletion`, then the expression `((a) as INotifyCompletion).OnCompleted(r)` is evaluated. - If `a` does implement `ICriticalNotifyCompletion`, then the expression @@ -3240,9 +3554,9 @@ At run-time, the expression `await t` is evaluated as follows: An awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` should cause the delegate `r` to be invoked at most once. Otherwise, the behavior of the enclosing async function is undefined. -## 11.9 Arithmetic operators +## 12.10 Arithmetic operators -### 11.9.1 General +### 12.10.1 General The `*`, `/`, `%`, `+`, and `–` operators are called the arithmetic operators. @@ -3261,11 +3575,11 @@ additive_expression ; ``` -If an operand of an arithmetic operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case, the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. +If an operand of an arithmetic operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. -### 11.9.2 Multiplication operator +### 12.10.2 Multiplication operator -For an operation of the form `x * y`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x * y`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. The predefined multiplication operators are listed below. The operators all compute the product of `x` and `y`. @@ -3298,7 +3612,7 @@ The predefined multiplication operators are listed below. The operators all comp | **`-∞`** | `-∞` | `+∞` | `NaN` | `NaN` | `-∞` | `+∞` | `NaN` | | **`NaN`** | `NaN` | `NaN` | `NaN` | `NaN` | `NaN` | `NaN` | `NaN` | - (Except were otherwise noted, in the floating-point tables in [§11.9.2](expressions.md#1192-multiplication-operator)–[§11.9.6](expressions.md#1196-subtraction-operator) the use of “`+`” means the value is positive; the use of “`-`” means the value is negative; and the lack of a sign means the value may be positive or negative or has no sign (NaN).) + (Except were otherwise noted, in the floating-point tables in [§12.10.2](expressions.md#12102-multiplication-operator)–[§12.10.6](expressions.md#12106-subtraction-operator) the use of “`+`” means the value is positive; the use of “`-`” means the value is negative; and the lack of a sign means the value may be positive or negative or has no sign (NaN).) - Decimal multiplication: ```csharp @@ -3308,11 +3622,11 @@ The predefined multiplication operators are listed below. The operators all comp If the magnitude of the resulting value is too large to represent in the decimal format, a `System.OverflowException` is thrown. Because of rounding, the result may be zero even though neither operand is zero. The scale of the result, before any rounding, is the sum of the scales of the two operands. Decimal multiplication is equivalent to using the multiplication operator of type `System.Decimal`. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined multiplication operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined multiplication operators defined above are also predefined. -### 11.9.3 Division operator +### 12.10.3 Division operator -For an operation of the form `x / y`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x / y`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. The predefined division operators are listed below. The operators all compute the quotient of `x` and `y`. @@ -3359,11 +3673,11 @@ The predefined division operators are listed below. The operators all compute th Decimal division is equivalent to using the division operator of type `System.Decimal`. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined division operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined division operators defined above are also predefined. -### 11.9.4 Remainder operator +### 12.10.4 Remainder operator -For an operation of the form `x % y`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x % y`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. The predefined remainder operators are listed below. The operators all compute the remainder of the division between `x` and `y`. @@ -3409,11 +3723,11 @@ The predefined remainder operators are listed below. The operators all compute t Decimal remainder is equivalent to using the remainder operator of type `System.Decimal`. > *Note*: These rules ensure that for all types, the result never has the opposite sign of the left operand. *end note* -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined remainder operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined remainder operators defined above are also predefined. -### 11.9.5 Addition operator +### 12.10.5 Addition operator -For an operation of the form `x + y`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x + y`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. The predefined addition operators are listed below. For numeric and enumeration types, the predefined addition operators compute the sum of the two operands. When one or both operands are of type `string`, the predefined addition operators concatenate the string representation of the operands. @@ -3509,13 +3823,13 @@ The predefined addition operators are listed below. For numeric and enumeration If the first operand is `null`, the result of the operation is the value of the second operand (even if that is also `null`). Otherwise, if the second operand is `null`, then the result of the operation is the value of the first operand. Otherwise, the result of the operation is a new delegate instance whose invocation list consists of the elements in the invocation list of the first operand, followed by the elements in the invocation list of the second operand. That is, the invocation list of the resulting delegate is the concatenation of the invocation lists of the two operands. - > *Note*: For examples of delegate combination, see [§11.9.6](expressions.md#1196-subtraction-operator) and [§19.6](delegates.md#196-delegate-invocation). Since `System.Delegate` is not a delegate type, operator + is not defined for it. *end note* + > *Note*: For examples of delegate combination, see [§12.10.6](expressions.md#12106-subtraction-operator) and [§20.6](delegates.md#206-delegate-invocation). Since `System.Delegate` is not a delegate type, operator + is not defined for it. *end note* -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined addition operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined addition operators defined above are also predefined. -### 11.9.6 Subtraction operator +### 12.10.6 Subtraction operator -For an operation of the form `x – y`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x – y`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. The predefined subtraction operators are listed below. The operators all subtract `y` from `x`. @@ -3581,8 +3895,8 @@ The predefined subtraction operators are listed below. The operators all subtrac The semantics are as follows: - If the first operand is `null`, the result of the operation is `null`. - Otherwise, if the second operand is `null`, then the result of the operation is the value of the first operand. - - Otherwise, both operands represent non-empty invocation lists ([§19.2](delegates.md#192-delegate-declarations)). - - If the lists compare equal, as determined by the delegate equality operator ([§11.11.9](expressions.md#11119-delegate-equality-operators)), the result of the operation is `null`. + - Otherwise, both operands represent non-empty invocation lists ([§20.2](delegates.md#202-delegate-declarations)). + - If the lists compare equal, as determined by the delegate equality operator ([§12.12.9](expressions.md#12129-delegate-equality-operators)), the result of the operation is `null`. - Otherwise, the result of the operation is a new invocation list consisting of the first operand’s list with the second operand’s entries removed from it, provided the second operand’s list is a sublist of the first’s. (To determine sublist equality, corresponding entries are compared as for the delegate equality operator.) If the second operand’s list matches multiple sublists of contiguous entries in the first operand’s list, the last matching sublist of contiguous entries is removed. - Otherwise, the result of the operation is the value of the left operand. @@ -3622,9 +3936,9 @@ The predefined subtraction operators are listed below. The operators all subtrac > > *end example* -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined subtraction operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined subtraction operators defined above are also predefined. -## 11.10 Shift operators +## 12.11 Shift operators The `<<` and `>>` operators are used to perform bit-shifting operations. @@ -3636,9 +3950,9 @@ shift_expression ; ``` -If an operand of a *shift_expression* has the compile-time type `dynamic`, then the expression is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case, the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. +If an operand of a *shift_expression* has the compile-time type `dynamic`, then the expression is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. -For an operation of the form `x << count` or `x >> count`, binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x << count` or `x >> count`, binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. When declaring an overloaded shift operator, the type of the first operand shall always be the class or struct containing the operator declaration, and the type of the second operand shall always be `int`. @@ -3684,11 +3998,11 @@ When the left operand of the `>>` operator is of a signed integral type, the op > *Example*: If `x` is a variable of type `int`, the operation `unchecked ((int)((uint)x >> y))` performs a logical shift right of `x`. *end example* -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined shift operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined shift operators defined above are also predefined. -## 11.11 Relational and type-testing operators +## 12.12 Relational and type-testing operators -### 11.11.1 General +### 12.12.1 General The `==`, `!=`, `<`, `>`, `<=`, `>=`, `is`, and `as` operators are called the relational and type-testing operators. @@ -3700,6 +4014,7 @@ relational_expression | relational_expression '<=' shift_expression | relational_expression '>=' shift_expression | relational_expression 'is' type + | relational_expression 'is' pattern | relational_expression 'as' type ; @@ -3710,16 +4025,18 @@ equality_expression ; ``` -The `is` operator is described in [§11.11.11](expressions.md#111111-the-is-operator) and the `as` operator is described in [§11.11.12](expressions.md#111112-the-as-operator). +> *Note*: Lookup for the right operand of the `is` operator must first test as a *type*, then as an *expression* which may span multiple tokens. In the case where the operand is an *expreesion*, the pattern expression must have precedence at least as high as *shift_expression*. *end note* + +The `is` operator is described in [§12.12.12](expressions.md#121212-the-is-operator) and the `as` operator is described in [§12.12.13](expressions.md#121213-the-as-operator). The `==`, `!=`, `<`, `>`, `<=` and `>=` operators are ***comparison operators***. -If a *default_literal* ([§11.7.19](expressions.md#11719-default-value-expressions)) is used as an operand of a `<`, `>`, `<=`, or `>=` operator, a compile-time error occurs. +If a *default_literal* ([§12.8.20](expressions.md#12820-default-value-expressions)) is used as an operand of a `<`, `>`, `<=`, or `>=` operator, a compile-time error occurs. If a *default_literal* is used as both operands of a `==` or `!=` operator, a compile-time error occurs. If a *default_literal* is used as the left operand of the `is` or `as` operator, a compile-time error occurs. -If an operand of a comparison operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. +If an operand of a comparison operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. -For an operation of the form `x «op» y`, where «op» is a comparison operator, overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. If both operands of an *equality_expression* are the `null` literal, then overload resolution is not performed and the expression evaluates to a constant value of `true` or `false` according to whether the operator is `==` or `!=`. +For an operation of the form `x «op» y`, where «op» is a comparison operator, overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. If both operands of an *equality_expression* are the `null` literal, then overload resolution is not performed and the expression evaluates to a constant value of `true` or `false` according to whether the operator is `==` or `!=`. The predefined comparison operators are described in the following subclauses. All predefined comparison operators return a result of type bool, as described in the following table. @@ -3732,7 +4049,7 @@ The predefined comparison operators are described in the following subclauses. A `x <= y` | `true` if `x` is less than or equal to `y`, `false` otherwise `x >= y` | `true` if `x` is greater than or equal to `y`, `false` otherwise -### 11.11.2 Integer comparison operators +### 12.12.2 Integer comparison operators The predefined integer comparison operators are: @@ -3770,9 +4087,9 @@ bool operator >=(ulong x, ulong y); Each of these operators compares the numeric values of the two integer operands and returns a `bool` value that indicates whether the particular relation is `true` or `false`. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined integer comparison operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined integer comparison operators defined above are also predefined. -### 11.11.3 Floating-point comparison operators +### 12.12.3 Floating-point comparison operators The predefined floating-point comparison operators are: @@ -3814,9 +4131,9 @@ where `min` and `max` are the smallest and largest positive finite values that c - A negative infinity is considered less than all other values, but equal to another negative infinity. - A positive infinity is considered greater than all other values, but equal to another positive infinity. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined floating-point comparison operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined floating-point comparison operators defined above are also predefined. -### 11.11.4 Decimal comparison operators +### 12.12.4 Decimal comparison operators The predefined decimal comparison operators are: @@ -3831,9 +4148,9 @@ bool operator >=(decimal x, decimal y); Each of these operators compares the numeric values of the two decimal operands and returns a `bool` value that indicates whether the particular relation is `true` or `false`. Each decimal comparison is equivalent to using the corresponding relational or equality operator of type `System.Decimal`. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined decimal comparison operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined decimal comparison operators defined above are also predefined. -### 11.11.5 Boolean equality operators +### 12.12.5 Boolean equality operators The predefined Boolean equality operators are: @@ -3846,9 +4163,9 @@ The result of `==` is `true` if both `x` and `y` are `true` or if both `x` and ` The result of `!=` is `false` if both `x` and `y` are `true` or if both `x` and `y` are `false`. Otherwise, the result is `true`. When the operands are of type `bool`, the `!=` operator produces the same result as the `^` operator. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined Boolean equality operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined Boolean equality operators defined above are also predefined. -### 11.11.6 Enumeration comparison operators +### 12.12.6 Enumeration comparison operators Every enumeration type implicitly provides the following predefined comparison operators @@ -3864,9 +4181,9 @@ bool operator >=(E x, E y); The result of evaluating `x «op» y`, where x and y are expressions of an enumeration type `E` with an underlying type `U`, and «op» is one of the comparison operators, is exactly the same as evaluating `((U)x) «op» ((U)y)`. In other words, the enumeration type comparison operators simply compare the underlying integral values of the two operands. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined enumeration comparison operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined enumeration comparison operators defined above are also predefined. -### 11.11.7 Reference type equality operators +### 12.12.7 Reference type equality operators Every class type `C` implicitly provides the following predefined reference type equality operators: @@ -3879,12 +4196,12 @@ unless predefined equality operators otherwise exist for `C` (for example, when The operators return the result of comparing the two references for equality or non-equality. `operator ==` returns `true` if and only if `x` and `y` refer to the same instance or are both `null`, while `operator !=` returns `true` if and only if `operator ==` with the same operands would return `false`. -In addition to normal applicability rules ([§11.6.4.2](expressions.md#11642-applicable-function-member)), the predefined reference type equality operators require one of the following in order to be applicable: +In addition to normal applicability rules ([§12.6.4.2](expressions.md#12642-applicable-function-member)), the predefined reference type equality operators require one of the following in order to be applicable: - Both operands are a value of a type known to be a *reference_type* or the literal `null`. Furthermore, an identity or explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) exists from either operand to the type of the other operand. - One operand is the literal `null`, and the other operand is a value of type `T` where `T` is a *type_parameter* that is not known to be a value type, and does not have the value type constraint. - If at runtime `T` is a non-nullable value type, the result of `==` is `false` and the result of `!=` is `true`. - - If at runtime `T` is a nullable value type, the result is computed from the `HasValue` property of the operand, as described in ([§11.11.10](expressions.md#111110-equality-operators-between-nullable-value-types-and-the-null-literal)). + - If at runtime `T` is a nullable value type, the result is computed from the `HasValue` property of the operand, as described in ([§12.12.10](expressions.md#121210-equality-operators-between-nullable-value-types-and-the-null-literal)). - If at runtime `T` is a reference type, the result is `true` if the operand is `null`, and `false` otherwise. Unless one of these conditions is true, a binding-time error occurs. @@ -3895,7 +4212,7 @@ Unless one of these conditions is true, a binding-time error occurs. > - The predefined reference type equality operators do not permit value type operands to be compared (except when type parameters are compared to `null`, which is handled specially). > - Operands of predefined reference type equality operators are never boxed. It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references. > -> For an operation of the form `x == y` or `x != y`, if any applicable user-defined `operator ==` or `operator !=` exists, the operator overload resolution rules ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) will select that operator instead of the predefined reference type equality operator. It is always possible to select the predefined reference type equality operator by explicitly casting one or both of the operands to type `object`. +> For an operation of the form `x == y` or `x != y`, if any applicable user-defined `operator ==` or `operator !=` exists, the operator overload resolution rules ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) will select that operator instead of the predefined reference type equality operator. It is always possible to select the predefined reference type equality operator by explicitly casting one or both of the operands to type `object`. > > *end note* @@ -3922,7 +4239,7 @@ Unless one of these conditions is true, a binding-time error occurs. > > *end example* -For an operation of the form `x == y` or `x != y`, if any applicable `operator ==` or `operator !=` exists, the operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) rules will select that operator instead of the predefined reference type equality operator. +For an operation of the form `x == y` or `x != y`, if any applicable `operator ==` or `operator !=` exists, the operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) rules will select that operator instead of the predefined reference type equality operator. > *Note*: It is always possible to select the predefined reference type equality operator by explicitly casting both of the operands to type `object`. *end note* @@ -3955,7 +4272,7 @@ For an operation of the form `x == y` or `x != y`, if any applicable `operat > False > ``` > -> The `s` and `t` variables refer to two distinct string instances containing the same characters. The first comparison outputs `True` because the predefined string equality operator ([§11.11.8](expressions.md#11118-string-equality-operators)) is selected when both operands are of type `string`. The remaining comparisons all output `False` because the overload of `operator ==` in the `string` type is not applicable when either operand has a binding-time type of `object`. +> The `s` and `t` variables refer to two distinct string instances containing the same characters. The first comparison outputs `True` because the predefined string equality operator ([§12.12.8](expressions.md#12128-string-equality-operators)) is selected when both operands are of type `string`. The remaining comparisons all output `False` because the overload of `operator ==` in the `string` type is not applicable when either operand has a binding-time type of `object`. > > Note that the above technique is not meaningful for value types. The example > @@ -3976,7 +4293,7 @@ For an operation of the form `x == y` or `x != y`, if any applicable `operat > > *end example* -### 11.11.8 String equality operators +### 12.12.8 String equality operators The predefined string equality operators are: @@ -3992,9 +4309,9 @@ Two `string` values are considered equal when one of the following is true: The string equality operators compare string values rather than string references. When two separate string instances contain the exact same sequence of characters, the values of the strings are equal, but the references are different. -> *Note*: As described in [§11.11.7](expressions.md#11117-reference-type-equality-operators), the reference type equality operators can be used to compare string references instead of string values. *end note* +> *Note*: As described in [§12.12.7](expressions.md#12127-reference-type-equality-operators), the reference type equality operators can be used to compare string references instead of string values. *end note* -### 11.11.9 Delegate equality operators +### 12.12.9 Delegate equality operators The predefined delegate equality operators are: @@ -4007,19 +4324,19 @@ Two delegate instances are considered equal as follows: - If either of the delegate instances is `null`, they are equal if and only if both are `null`. - If the delegates have different run-time type, they are never equal. -- If both of the delegate instances have an invocation list ([§19.2](delegates.md#192-delegate-declarations)), those instances are equal if and only if their invocation lists are the same length, and each entry in one’s invocation list is equal (as defined below) to the corresponding entry, in order, in the other’s invocation list. +- If both of the delegate instances have an invocation list ([§20.2](delegates.md#202-delegate-declarations)), those instances are equal if and only if their invocation lists are the same length, and each entry in one’s invocation list is equal (as defined below) to the corresponding entry, in order, in the other’s invocation list. The following rules govern the equality of invocation list entries: - If two invocation list entries both refer to the same static method then the entries are equal. - If two invocation list entries both refer to the same non-static method on the same target object (as defined by the reference equality operators) then the entries are equal. -- Invocation list entries produced from evaluation of semantically identical anonymous functions ([§11.17](expressions.md#1117-anonymous-function-expressions)) with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal. +- Invocation list entries produced from evaluation of semantically identical anonymous functions ([§12.19](expressions.md#1219-anonymous-function-expressions)) with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal. -If operator overload resolution resolves to either delegate equality operator, and the binding-time types of both operands are delegate types as described in [§19](delegates.md#19-delegates) rather than `System.Delegate`, and there is no identity conversion between the binding-type operand types, a binding-time error occurs. +If operator overload resolution resolves to either delegate equality operator, and the binding-time types of both operands are delegate types as described in [§20](delegates.md#20-delegates) rather than `System.Delegate`, and there is no identity conversion between the binding-type operand types, a binding-time error occurs. > *Note*: This rule prevents comparisons which can never consider non-`null` values as equal due to being references to instances of different types of delegates. *end note* -### 11.11.10 Equality operators between nullable value types and the null literal +### 12.12.10 Equality operators between nullable value types and the null literal The `==` and `!=` operators permit one operand to be a value of a nullable value type and the other to be the `null` literal, even if no predefined or user-defined operator (in unlifted or lifted form) exists for the operation. @@ -4029,16 +4346,56 @@ For an operation of one of the forms x == null null == x x != null null != x ``` -where `x` is an expression of a nullable value type, if operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) fails to find an applicable operator, the result is instead computed from the `HasValue` property of `x`. Specifically, the first two forms are translated into `!x.HasValue`, and the last two forms are translated into `x.HasValue`. +where `x` is an expression of a nullable value type, if operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) fails to find an applicable operator, the result is instead computed from the `HasValue` property of `x`. Specifically, the first two forms are translated into `!x.HasValue`, and the last two forms are translated into `x.HasValue`. + +### 12.12.11 Tuple equality operators + +The tuple equality operators are applied pairwise to the elements of the tuple operands in lexical order. -### 11.11.11 The is operator +If each operand `x` and `y` of a `==` or `!=` operator is classified either as a tuple or as a value with a tuple type ([§8.3.11](types.md#8311-tuple-types)), the operator is a *tuple equality operator*. -The `is` operator is used to check if the run-time type of an object is compatible with a given type. The check is performed at runtime. The result of the operation `E is T`, where `E` is an expression and `T` is a type other than `dynamic`, is a Boolean value indicating whether `E` is non-null and can successfully be converted to type `T` by a reference conversion, a boxing conversion, an unboxing conversion, a wrapping conversion, or an unwrapping conversion. +If an operand `e` is classified as a tuple, the elements `e1...en` shall be the results of evaluating the element expressions of the tuple expression. Otherwise if `e` is a value of a tuple type, the elements shall be `t.Item1...t.Itemn` where `t` is the result of evaluating `e`. + +The operands `x` and `y` of a tuple equality operator shall have the same arity, or a compile time error occurs. For each pair of elements `xi` and `yi`, the same equality operator must apply, and must yield a result of type `bool`, `dynamic`, a type that has an implicit conversion to `bool`, or a type that defines the `true` and `false` operators. + +The tuple equality operator `x == y` is evaluated as follows: + +- The left side operand `x` is evaluated. +- The right side operand `y` is evaluated. +- For each pair of elements `xi` and `yi` in lexical order: + - The operator `xi == yi` is evaluated, and a result of type `bool` is obtained in the following way: + - If the comparison yielded a `bool` then that is the result. + - Otherwise if the comparison yielded a `dynamic` then the operator `false` is dynamically invoked on it, and the resulting `bool` value is negated with the `!` operator. + - Otherwise, if the type of the comparison has an implicit conversion to `bool`, that conversion is applied. + - Otherwise, if the type of the comparison has an operator `false`, that operator is invoked and the resulting `bool` value is negated with the `!` operator. + - If the resulting `bool` is `false`, then no further evaluation occurs, and the result of the tuple equality operator is `false`. +- If all element comparisons yielded `true`, the result of the tuple equality operator is `true`. + +The tuple equality operator `x != y` is evaluated as follows: + +- The left side operand `x` is evaluated. +- The right side operand `y` is evaluated. +- For each pair of elements `xi` and `yi` in lexical order: + - The operator `xi != yi` is evaluated, and a result of type `bool` is obtained in the following way: + - If the comparison yielded a `bool` then that is the result. + - Otherwise if the comparison yielded a `dynamic` then the operator `true` is dynamically invoked on it, and the resulting `bool` value is the result. + - Otherwise, if the type of the comparison has an implicit conversion to `bool`, that conversion is applied. + - Otherwise, if the type of the comparison has an operator `true`, that operator is invoked and the resulting `bool` value is the result. + - If the resulting `bool` is `true`, then no further evaluation occurs, and the result of the tuple equality operator is `true`. +- If all element comparisons yielded `false`, the result of the tuple equality operator is `false`. + +### 12.12.12 The is operator + +There are two forms of the `is` operator. One is the *is-type operator*, which has a type on the right-hand-side. The other is the *is-pattern operator*, which has a pattern on the right-hand-side. + +#### 12.12.12.1 The is-type operator + +The *is-type operator* is used to check if the run-time type of an object is compatible with a given type. The check is performed at runtime. The result of the operation `E is T`, where `E` is an expression and `T` is a type other than `dynamic`, is a Boolean value indicating whether `E` is non-null and can successfully be converted to type `T` by a reference conversion, a boxing conversion, an unboxing conversion, a wrapping conversion, or an unwrapping conversion. The operation is evaluated as follows: -1. If `E` is an anonymous function, a compile-time error occurs -1. If `E` is a method group or the `null` literal, of if the value of `E` is `null`, the result is `false`. +1. If `E` is an anonymous function or method group, a compile-time error occurs +1. If `E` is the `null` literal, or if the value of `E` is `null`, the result is `false`. 1. Otherwise: 1. Let `R` be the runtime type of `E`. 1. Let `D` be derived from `R` as follows: @@ -4065,19 +4422,28 @@ User defined conversions are not considered by the `is` operator. > - If the compile-time type of `e` is the same as `T`, or if an implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)), boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)), wrapping conversion ([§10.6](conversions.md#106-conversions-involving-nullable-types)), or an explicit unwrapping conversion ([§10.6](conversions.md#106-conversions-involving-nullable-types)) exists from the compile-time type of `E` to `T`: > - If `C` is of a non-nullable value type, the result of the operation is `true`. > - Otherwise, the result of the operation is equivalent to evaluating `E != null`. -> - Otherwise, if an explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) or unboxing conversion ([§10.3.6](conversions.md#1036-unboxing-conversions)) exists from `C` to `T`, or if `C` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)), then runtime checks as above must be peformed. +> - Otherwise, if an explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) or unboxing conversion ([§10.3.7](conversions.md#1037-unboxing-conversions)) exists from `C` to `T`, or if `C` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)), then runtime checks as above must be peformed. > - Otherwise, no reference, boxing, wrapping, or unwrapping conversion of `E` to type `T` is possible, and the result of the operation is `false`. > A compiler may implement optimisations based on the compile-time type. > > *end note* -### 11.11.12 The as operator +#### 12.12.12.2 The is-pattern operator + +The *is-pattern operator* is used to check if the value computed by an expression *matches* a given pattern ([§11](patterns.md#11-patterns-and-pattern-matching)). The check is performed at runtime. The result of the is-pattern operator is true if the value matches the pattern; otherwise it is false. + +For an expression of the form `E is P`, where `E` is a relational expression of type `T` and `P` is a pattern, it is a compile-time error if any of the following hold: + +- `E` does not designate a value or does not have a type. +- The pattern `P` is not applicable ([§11.2](patterns.md#112-pattern-forms)) to the type `T`. -The `as` operator is used to explicitly convert a value to a given reference type or nullable value type. Unlike a cast expression ([§11.8.7](expressions.md#1187-cast-expressions)), the `as` operator never throws an exception. Instead, if the indicated conversion is not possible, the resulting value is `null`. +### 12.12.13 The as operator + +The `as` operator is used to explicitly convert a value to a given reference type or nullable value type. Unlike a cast expression ([§12.9.7](expressions.md#1297-cast-expressions)), the `as` operator never throws an exception. Instead, if the indicated conversion is not possible, the resulting value is `null`. In an operation of the form `E as T`, `E` shall be an expression and `T` shall be a reference type, a type parameter known to be a reference type, or a nullable value type. Furthermore, at least one of the following shall be true, or otherwise a compile-time error occurs: -- An identity ([§10.2.2](conversions.md#1022-identity-conversion)), implicit nullable ([§10.2.6](conversions.md#1026-implicit-nullable-conversions)), implicit reference ([§10.2.8](conversions.md#1028-implicit-reference-conversions)), boxing ([§10.2.9](conversions.md#1029-boxing-conversions)), explicit nullable ([§10.3.4](conversions.md#1034-explicit-nullable-conversions)), explicit reference ([§10.3.5](conversions.md#1035-explicit-reference-conversions)), or wrapping ([§8.3.11](types.md#8311-nullable-value-types)) conversion exists from `E` to `T`. +- An identity ([§10.2.2](conversions.md#1022-identity-conversion)), implicit nullable ([§10.2.6](conversions.md#1026-implicit-nullable-conversions)), implicit reference ([§10.2.8](conversions.md#1028-implicit-reference-conversions)), boxing ([§10.2.9](conversions.md#1029-boxing-conversions)), explicit nullable ([§10.3.4](conversions.md#1034-explicit-nullable-conversions)), explicit reference ([§10.3.5](conversions.md#1035-explicit-reference-conversions)), or wrapping ([§8.3.12](types.md#8312-nullable-value-types)) conversion exists from `E` to `T`. - The type of `E` or `T` is an open type. - `E` is the `null` literal. @@ -4089,7 +4455,7 @@ E is T ? (T)(E) : (T)null except that `E` is only evaluated once. The compiler can be expected to optimize `E` as `T` to perform at most one runtime type check as opposed to the two runtime type checks implied by the expansion above. -If the compile-time type of `E` is `dynamic`, unlike the cast operator the `a` operator is not dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). Therefore the expansion in this case is: +If the compile-time type of `E` is `dynamic`, unlike the cast operator the `as` operator is not dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). Therefore the expansion in this case is: ```csharp E is T ? (T)(object)(E) : (T)null @@ -4125,9 +4491,9 @@ Note that some conversions, such as user defined conversions, are not possible w > > *end example* -## 11.12 Logical operators +## 12.13 Logical operators -### 11.12.1 General +### 12.13.1 General The `&,` `^`, and `|` operators are called the logical operators. @@ -4148,13 +4514,13 @@ inclusive_or_expression ; ``` -If an operand of a logical operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. +If an operand of a logical operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. -For an operation of the form `x «op» y`, where «op» is one of the logical operators, overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. +For an operation of the form `x «op» y`, where «op» is one of the logical operators, overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator. The predefined logical operators are described in the following subclauses. -### 11.12.2 Integer logical operators +### 12.13.2 Integer logical operators The predefined integer logical operators are: @@ -4177,9 +4543,9 @@ ulong operator ^(ulong x, ulong y); The `&` operator computes the bitwise logical AND of the two operands, the `|` operator computes the bitwise logical OR of the two operands, and the `^` operator computes the bitwise logical exclusive OR of the two operands. No overflows are possible from these operations. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined integer logical operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined integer logical operators defined above are also predefined. -### 11.12.3 Enumeration logical operators +### 12.13.3 Enumeration logical operators Every enumeration type `E` implicitly provides the following predefined logical operators: @@ -4191,9 +4557,9 @@ E operator ^(E x, E y); The result of evaluating `x «op» y`, where `x` and `y` are expressions of an enumeration type `E` with an underlying type `U`, and «op» is one of the logical operators, is exactly the same as evaluating `(E)((U)x «op» (U)y)`. In other words, the enumeration type logical operators simply perform the logical operation on the underlying type of the two operands. -Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted predefined enumeration logical operators defined above are also predefined. +Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined enumeration logical operators defined above are also predefined. -### 11.12.4 Boolean logical operators +### 12.13.4 Boolean logical operators The predefined Boolean logical operators are: @@ -4209,11 +4575,11 @@ The result of `x | y` is `true` if either `x` or `y` is `true`. Otherwise, the The result of `x ^ y` is `true` if `x` is `true` and `y` is `false`, or `x` is `false` and `y` is `true`. Otherwise, the result is `false`. When the operands are of type `bool`, the `^` operator computes the same result as the `!=` operator. -### 11.12.5 Nullable Boolean & and | operators +### 12.13.5 Nullable Boolean & and | operators The nullable Boolean type `bool?` can represent three values, `true`, `false`, and `null`. -As with the other binary operators, lifted forms of the logical operators `&` and `|` ([§11.12.4](expressions.md#11124-boolean-logical-operators)) are also pre-defined: +As with the other binary operators, lifted forms of the logical operators `&` and `|` ([§12.13.4](expressions.md#12134-boolean-logical-operators)) are also pre-defined: ```csharp bool? operator &(bool? x, bool? y); @@ -4234,11 +4600,11 @@ The semantics of the lifted `&` and `|` operators are defined by the following `null` | `false` | `false` | `null` `null` | `null` | `null` | `null` -> *Note*: The `bool?` type is conceptually similar to the three-valued type used for Boolean expressions in SQL. The table above follows the same semantics as SQL, whereas applying the rules of [§11.4.8](expressions.md#1148-lifted-operators) to the `&` and `|` operators would not. The rules of [§11.4.8](expressions.md#1148-lifted-operators) already provide SQL-like semantics for the lifted `^` operator. *end note* +> *Note*: The `bool?` type is conceptually similar to the three-valued type used for Boolean expressions in SQL. The table above follows the same semantics as SQL, whereas applying the rules of [§12.4.8](expressions.md#1248-lifted-operators) to the `&` and `|` operators would not. The rules of [§12.4.8](expressions.md#1248-lifted-operators) already provide SQL-like semantics for the lifted `^` operator. *end note* -## 11.13 Conditional logical operators +## 12.14 Conditional logical operators -### 11.13.1 General +### 12.14.1 General The `&&` and `||` operators are called the conditional logical operators. They are also called the “short-circuiting” logical operators. @@ -4261,24 +4627,24 @@ The `&&` and `||` operators are conditional versions of the `&` and `|` operato > *Note*: The reason that short circuiting uses the ‘not true’ and ‘not false’ conditions is to enable user-defined conditional operators to define when short circuiting applies. User-defined types could be in a state where `operator true` returns `false` and `operator false` returns `false`. In those cases, neither `&&` nor `||` would short circuit. *end note* -If an operand of a conditional logical operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. +If an operand of a conditional logical operator has the compile-time type `dynamic`, then the expression is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case the compile-time type of the expression is `dynamic`, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type `dynamic`. -An operation of the form `x && y` or `x || y` is processed by applying overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) as if the operation was written `x & y` or `x | y`. Then, +An operation of the form `x && y` or `x || y` is processed by applying overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) as if the operation was written `x & y` or `x | y`. Then, -- If overload resolution fails to find a single best operator, or if overload resolution selects one of the predefined integer logical operators or nullable Boolean logical operators ([§11.12.5](expressions.md#11125-nullable-boolean--and--operators)), a binding-time error occurs. -- Otherwise, if the selected operator is one of the predefined Boolean logical operators ([§11.12.4](expressions.md#11124-boolean-logical-operators)), the operation is processed as described in [§11.13.2](expressions.md#11132-boolean-conditional-logical-operators). -- Otherwise, the selected operator is a user-defined operator, and the operation is processed as described in [§11.13.3](expressions.md#11133-user-defined-conditional-logical-operators). +- If overload resolution fails to find a single best operator, or if overload resolution selects one of the predefined integer logical operators or nullable Boolean logical operators ([§12.13.5](expressions.md#12135-nullable-boolean--and--operators)), a binding-time error occurs. +- Otherwise, if the selected operator is one of the predefined Boolean logical operators ([§12.13.4](expressions.md#12134-boolean-logical-operators)), the operation is processed as described in [§12.14.2](expressions.md#12142-boolean-conditional-logical-operators). +- Otherwise, the selected operator is a user-defined operator, and the operation is processed as described in [§12.14.3](expressions.md#12143-user-defined-conditional-logical-operators). -It is not possible to directly overload the conditional logical operators. However, because the conditional logical operators are evaluated in terms of the regular logical operators, overloads of the regular logical operators are, with certain restrictions, also considered overloads of the conditional logical operators. This is described further in [§11.13.3](expressions.md#11133-user-defined-conditional-logical-operators). +It is not possible to directly overload the conditional logical operators. However, because the conditional logical operators are evaluated in terms of the regular logical operators, overloads of the regular logical operators are, with certain restrictions, also considered overloads of the conditional logical operators. This is described further in [§12.14.3](expressions.md#12143-user-defined-conditional-logical-operators). -### 11.13.2 Boolean conditional logical operators +### 12.14.2 Boolean conditional logical operators When the operands of `&&` or `||` are of type `bool`, or when the operands are of types that do not define an applicable `operator &` or `operator |`, but do define implicit conversions to `bool`, the operation is processed as follows: - The operation `x && y` is evaluated as `x ? y : false`. In other words, `x` is first evaluated and converted to type `bool`. Then, if `x` is `true`, `y` is evaluated and converted to type `bool`, and this becomes the result of the operation. Otherwise, the result of the operation is `false`. - The operation `x || y` is evaluated as `x ? true : y`. In other words, `x` is first evaluated and converted to type `bool`. Then, if `x` is `true`, the result of the operation is `true`. Otherwise, `y` is evaluated and converted to type `bool`, and this becomes the result of the operation. -### 11.13.3 User-defined conditional logical operators +### 12.14.3 User-defined conditional logical operators When the operands of `&&` or `||` are of types that declare an applicable user-defined `operator &` or `operator |`, both of the following shall be true, where `T` is the type in which the selected operator is declared: @@ -4292,7 +4658,7 @@ A binding-time error occurs if either of these requirements is not satisfied. Ot In either of these operations, the expression given by `x` is only evaluated once, and the expression given by `y` is either not evaluated or evaluated exactly once. -## 11.14 The null coalescing operator +## 12.15 The null coalescing operator The `??` operator is called the null coalescing operator. @@ -4321,7 +4687,7 @@ The type of the expression `a ?? b` depends on which implicit conversions are Otherwise, `a` and `b` are incompatible, and `a` compile-time error occurs. -## 11.15 The throw expression operator +## 12.16 The throw expression operator ```ANTLR throw_expression @@ -4329,7 +4695,7 @@ throw_expression ; ``` -A *throw_expression* throws the value produced by evaluating the *null_coalescing_expression*. The expression shall be implicitly convertible to `System.Exception`, and the result of evaluating the expression is converted to `System.Exception` before being thrown. The behavior at runtime of the evaluation of a *throw expression* is the same as specified for a *throw statement* ([§12.10.6](statements.md#12106-the-throw-statement)). +A *throw_expression* throws the value produced by evaluating the *null_coalescing_expression*. The expression shall be implicitly convertible to `System.Exception`, and the result of evaluating the expression is converted to `System.Exception` before being thrown. The behavior at runtime of the evaluation of a *throw expression* is the same as specified for a *throw statement* ([§13.10.6](statements.md#13106-the-throw-statement)). A *throw_expression* has no type. A *throw_expression* is convertible to every type by an *implicit throw conversion*. @@ -4339,7 +4705,87 @@ A *throw expression* shall only occur in the following syntactic contexts: - As the second operand of a null coalescing operator (`??`). - As the body of an expression-bodied lambda or member. -## 11.16 Conditional operator +## 12.17 Declaration expressions + +A declaration expression declares a local variable. + +```ANTLR +declaration_expression + : local_variable_type identifier + ; + +local_variable_type + : type + | 'var' + ; +``` + +The *simple_name* `_` is also considered a declaration expression if simple name lookup did not find an associated declaration ([§12.8.4](expressions.md#1284-simple-names)). When used as a declaration expression, `_` is called a *simple discard*. It is semantically equivalent to `var _`, but is permitted in more places. + +A declaration expression shall only occur in the following syntactic contexts: + +- As an `out` *argument_value* in an *argument_list*. +- As a simple discard `_` comprising the left side of a simple assignment ([§12.21.2](expressions.md#12212-simple-assignment)). +- As a *tuple_element* in one or more recursively nested *tuple_expression*s, the outermost of which comprises the left side of a deconstructing assignment. A *deconstruction_expression* gives rise to declaration expressions in this position, even though the declaration expressions are not syntactically present. + +> *Note*: This means that a declaration expression cannot be parenthesized. *end note* + +It is an error for an implicitly typed variable declared with a *declaration_expression* to be referenced within the *argument_list* where it is declared. + +It is an error for a variable declared with a *declaration_expression* to be referenced within the deconstructing assignment where it occurs. + +A declaration expression that is a simple discard or where the *local_variable_type* is the identifier `var` is classified as an *implicitly typed* variable. The expression has no type, and the type of the local variable is inferred based on the syntactic context as follows: + +- In an *argument_list* the inferred type of the variable is the declared type of the corresponding parameter. +- As the left side of a simple assignment, the inferred type of the variable is the type of the right side of the assignment. +- In a *tuple_expression* on the left side of a simple assignment, the inferred type of the variable is the type of the corresponding tuple element on the right side (after deconstruction) of the assignment. + +Otherwise, the declaration expression is classified as an *explicitly typed* variable, and the type of the expression as well as the declared variable shall be that given by the *local_variable_type*. + +A declaration expression with the identifier `_` is a discard ([§9.2.9.1](variables.md#9291-discards)), and does not introduce a name for the variable. A declaration expression with an identifier other than `_` introduces that name into the nearest enclosing local variable declaration space ([§7.3](basic-concepts.md#73-declarations)). + +> *Example*: +> +> +> ```csharp +> string M(out int i, string s, out bool b) { ... } +> +> var s1 = M(out int i1, "One", out var b1); +> Console.WriteLine($"{i1}, {b1}, {s1}"); +> // Error: i2 referenced within declaring argument list +> var s2 = M(out var i2, M(out i2, "Two", out bool b2), out b2); +> var s3 = M(out int _, "Three", out var _); +> ``` +> +> The declaration of `s1` shows both explicitly and implicitly typed declaration expressions. The inferred type of `b1` is `bool` because that is the type of the corresponding out parameter in `M1`. The subsequent `WriteLine` is able to access `i1` and `b1`, which have been introduced to the enclosing scope. +> +> The declaration of `s2` shows an attempt to use `i2` in the nested call to `M`, which is disallowed, because the reference occurs within the argument list where `i2` was declared. On the other hand the reference to `b2` in the final argument is allowed, because it occurs after the end of the nested argument list where `b2` was declared. +> +> The declaration of `s3` shows the use of both implicitly and explicitly typed declaration expressions that are discards. Because discards do not declare a named variable, the multiple occurrences of the identifier `_` are allowed. +> +> +> ```csharp +> (int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5); +> ``` +> +> This example shows the use of implicitly and explicitly typed declaration expressions for both variables and discards in a deconstructing assignment. The *simple_name* `_` is equivalent to `var _` when no declaration of `_` is found. +> +> +> ```csharp +> void M1(out int i) { ... } +> +> void M2(string _) +> { +> M1(out _); // Error: `_` is a string +> M1(out var _); +> } +> ``` +> +> This examples shows the use of `var _` to provide an implicitly typed discard when `_` is not available, because it designates a variable in the enclosing scope. +> +> *end example* + +## 12.18 Conditional operator The `?:` operator is called the conditional operator. It is at times also called the ternary operator. @@ -4347,9 +4793,13 @@ The `?:` operator is called the conditional operator. It is at times also calle conditional_expression : null_coalescing_expression | null_coalescing_expression '?' expression ':' expression + | null_coalescing_expression '?' 'ref' variable_reference ':' + 'ref' variable_reference ; ``` +A throw expression ([§12.16](expressions.md#1216-the-throw-expression-operator)) is not allowed in a conditional operator if `ref` is present. + A conditional expression of the form `b ? x : y` first evaluates the condition `b`. Then, if `b` is `true`, `x` is evaluated and becomes the result of the operation. Otherwise, `y` is evaluated and becomes the result of the operation. A conditional expression never evaluates both `x` and `y`. The conditional operator is right-associative, meaning that operations are grouped from right to left. @@ -4358,10 +4808,17 @@ The conditional operator is right-associative, meaning that operations are group The first operand of the `?:` operator shall be an expression that can be implicitly converted to `bool`, or an expression of a type that implements `operator true`. If neither of these requirements is satisfied, a compile-time error occurs. -The second and third operands, `x` and `y`, of the `?:` operator control the type of the conditional expression. If both `x` and `y` are *default_literal*s ([§11.7.19](expressions.md#11719-default-value-expressions)), a compile-time error occurs. +If `ref` is present: + +- An identity conversion must exist between the types of the two *variable_reference*s, and type of the result can be either type. If either type is `dynamic`, type inference prefers `dynamic` ([§8.7](types.md#87-the-dynamic-type)). If either type is a tuple type ([§8.3.11](types.md#8311-tuple-types)), type inference includes the element names when the element names in the same ordinal position match in both tuples. +- The result is a variable reference, which is writeable if both *variable_reference*s are writeable. + +> *Note*: When `ref` is present, the *conditional_expression* returns a variable reference, which can be assigned to a reference variable using the `= ref` operator or passed as a reference/input/output parameter. *end note* + +If `ref` is not present, the second and third operands, `x` and `y`, of the `?:` operator control the type of the conditional expression: - If `x` has type `X` and `y` has type `Y` then, - - If `X` and `Y` are the same type, then this is the type of the conditional expression. + - If an identity conversion exists between `X` and `Y`, then the result is the best common type of a set of expressions ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)). If either type is `dynamic`, type inference prefers `dynamic` ([§8.7](types.md#87-the-dynamic-type)). If either type is a tuple type ([§8.3.11](types.md#8311-tuple-types)), type inference includes the element names when the element names in the same ordinal position match in both tuples. - Otherwise, if an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from `X` to `Y`, but not from `Y` to `X`, then `Y` is the type of the conditional expression. - Otherwise, if an implicit enumeration conversion ([§10.2.4](conversions.md#1024-implicit-enumeration-conversions)) exists from `X` to `Y`, then `Y` is the type of the conditional expression. - Otherwise, if an implicit enumeration conversion ([§10.2.4](conversions.md#1024-implicit-enumeration-conversions)) exists from `Y` to `X`, then `X` is the type of the conditional expression. @@ -4370,6 +4827,14 @@ The second and third operands, `x` and `y`, of the `?:` operator control the t - If only one of `x` and `y` has a type, and both `x` and `y` are implicitly convertible to that type, then that is the type of the conditional expression. - Otherwise, no expression type can be determined, and a compile-time error occurs. +The run-time processing of a ref conditional expression of the form `b ? ref x : ref y` consists of the following steps: + +- First, `b` is evaluated, and the `bool` value of `b` is determined: + - If an implicit conversion from the type of `b` to `bool` exists, then this implicit conversion is performed to produce a `bool` value. + - Otherwise, the `operator true` defined by the type of `b` is invoked to produce a `bool` value. +- If the `bool` value produced by the step above is `true`, then `x` is evaluated and the resulting variable reference becomes the result of the conditional expression. +- Otherwise, `y` is evaluated and the resulting variable reference becomes the result of the conditional expression. + The run-time processing of a conditional expression of the form `b ? x : y` consists of the following steps: - First, `b` is evaluated, and the `bool` value of `b` is determined: @@ -4378,13 +4843,13 @@ The run-time processing of a conditional expression of the form `b ? x : y` - If the `bool` value produced by the step above is `true`, then `x` is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression. - Otherwise, `y` is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression. -## 11.17 Anonymous function expressions +## 12.19 Anonymous function expressions -### 11.17.1 General +### 12.19.1 General An ***anonymous function*** is an expression that represents an “in-line” method definition. An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression-tree type. The evaluation of an anonymous-function conversion depends on the target type of the conversion: If it is a delegate type, the conversion evaluates to a delegate value referencing the method that the anonymous function defines. If it is an expression-tree type, the conversion evaluates to an expression tree that represents the structure of the method as an object structure. -> *Note*: For historical reasons, there are two syntactic flavors of anonymous functions, namely *lambda_expression*s and *anonymous_method_expression*s. For almost all purposes, *lambda_expression*s are more concise and expressive than *anonymous_method_expression*s, which remain in the language for backwards compatibility. +> *Note*: For historical reasons, there are two syntactic flavors of anonymous functions, namely *lambda_expression*s and *anonymous_method_expression*s. For almost all purposes, *lambda_expression*s are more concise and expressive than *anonymous_method_expression*s, which remain in the language for backwards compatibility. *end note* ```ANTLR lambda_expression @@ -4416,6 +4881,7 @@ explicit_anonymous_function_parameter anonymous_function_parameter_modifier : 'ref' | 'out' + | 'in' ; implicit_anonymous_function_signature @@ -4435,17 +4901,35 @@ implicit_anonymous_function_parameter anonymous_function_body : null_conditional_invocation_expression | expression + | 'ref' variable_reference | block ; ``` When recognising an *anonymous_function_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen. -> *Note*: The overlapping of, and priority between, alternatives here is solely for descriptive convenience; the grammar rules could be elaborated to remove the overlap. ANTLR, and other grammar systems, adopt the same convenience and so *anonymous_function_body* has the specified semantics automatically. + +> *Note*: The overlapping of, and priority between, alternatives here is solely for descriptive convenience; the grammar rules could be elaborated to remove the overlap. ANTLR, and other grammar systems, adopt the same convenience and so *anonymous_function_body* has the specified semantics automatically. *end note* + + + +> *Note*: When treated as an *expression*, a syntactic form such as `x?.M()` would be an error if the result type of `M` is `void` ([§12.8.12](expressions.md#12812-null-conditional-element-access)). But when treated as a *null_conditional_invocation_expression*, the result type is permitted to be `void`. *end note* + + + +> *Example*: The result type of `List.Reverse` is `void`. In the following code, the body of the anonymous expression is a *null_conditional_invocation_expression*, so it is not an error. +> +> +> ```csharp +> Action> a = x => x?.Reverse(); +> ``` +> +> *end example* + The `=>` operator has the same precedence as assignment (`=`) and is right-associative. -An anonymous function with the `async` modifier is an async function and follows the rules described in [§14.15](classes.md#1415-async-functions). +An anonymous function with the `async` modifier is an async function and follows the rules described in [§15.15](classes.md#1515-async-functions). The parameters of an anonymous function in the form of a *lambda_expression* can be explicitly or implicitly typed. In an explicitly typed parameter list, the type of each parameter is explicitly stated. In an implicitly typed parameter list, the types of the parameters are inferred from the context in which the anonymous function occurs—specifically, when the anonymous function is converted to a compatible delegate type or expression tree type, that type provides the parameter types ([§10.7](conversions.md#107-anonymous-function-conversions)). @@ -4463,7 +4947,7 @@ can be abbreviated to The parameter list of an anonymous function in the form of an *anonymous_method_expression* is optional. If given, the parameters shall be explicitly typed. If not, the anonymous function is convertible to a delegate with any parameter list not containing out parameters. -A *block* body of an anonymous function is always reachable ([§12.2](statements.md#122-end-points-and-reachability)). +A *block* body of an anonymous function is always reachable ([§13.2](statements.md#132-end-points-and-reachability)). > *Example*: Some examples of anonymous functions follow below: > @@ -4488,7 +4972,7 @@ The behavior of *lambda_expression*s and *anonymous_method_expression*s is the s - The body of a *lambda_expression* can be an expression or a block whereas the body of an *anonymous_method_expression* shall be a block. - Only *lambda_expression*s have conversions to compatible expression tree types ([§8.6](types.md#86-expression-tree-types)). -### 11.17.2 Anonymous function signatures +### 12.19.2 Anonymous function signatures The *anonymous_function_signature* of an anonymous function defines the names and optionally the types of the formal parameters for the anonymous function. The scope of the parameters of the anonymous function is the *anonymous_function_body* ([§7.7](basic-concepts.md#77-scopes)). Together with the parameter list (if given) the anonymous-method-body constitutes a declaration space ([§7.3](basic-concepts.md#73-declarations)). It is thus a compile-time error for the name of a parameter of the anonymous function to match the name of a local variable, local constant or parameter whose scope includes the *anonymous_method_expression* or *lambda_expression*. @@ -4498,22 +4982,23 @@ Note that an *anonymous_function_signature* cannot include attributes or a param Note also that conversion to an expression tree type, even if compatible, may still fail at compile-time ([§8.6](types.md#86-expression-tree-types)). -### 11.17.3 Anonymous function bodies +### 12.19.3 Anonymous function bodies The body (*expression* or *block*) of an anonymous function is subject to the following rules: - If the anonymous function includes a signature, the parameters specified in the signature are available in the body. If the anonymous function has no signature it can be converted to a delegate type or expression type having parameters ([§10.7](conversions.md#107-anonymous-function-conversions)), but the parameters cannot be accessed in the body. -- Except for `ref` or `out` parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a `ref` or `out` parameter. +- Except for `in`, `out`, or `ref` parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access an `in`, `out`, or `ref` parameter. +- Except for parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a parameter of a `ref struct` type. - When the type of `this` is a struct type, it is a compile-time error for the body to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule simply prohibits such access and does not affect whether member lookup results in a member of the struct. -- The body has access to the outer variables ([§11.17.6](expressions.md#11176-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§11.17.7](expressions.md#11177-evaluation-of-anonymous-function-expressions)). +- The body has access to the outer variables ([§12.19.6](expressions.md#12196-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§12.19.7](expressions.md#12197-evaluation-of-anonymous-function-expressions)). - It is a compile-time error for the body to contain a `goto` statement, a `break` statement, or a `continue` statement whose target is outside the body or within the body of a contained anonymous function. - A `return` statement in the body returns control from an invocation of the nearest enclosing anonymous function, not from the enclosing function member. It is explicitly unspecified whether there is any way to execute the block of an anonymous function other than through evaluation and invocation of the *lambda_expression* or *anonymous_method_expression*. In particular, the compiler may choose to implement an anonymous function by synthesizing one or more named methods or types. The names of any such synthesized elements shall be of a form reserved for compiler use ([§6.4.3](lexical-structure.md#643-identifiers)). -### 11.17.4 Overload resolution +### 12.19.4 Overload resolution -Anonymous functions in an argument list participate in type inference and overload resolution. Refer to [§11.6.3](expressions.md#1163-type-inference) and [§11.6.4](expressions.md#1164-overload-resolution) for the exact rules. +Anonymous functions in an argument list participate in type inference and overload resolution. Refer to [§12.6.3](expressions.md#1263-type-inference) and [§12.6.4](expressions.md#1264-overload-resolution) for the exact rules. > *Example*: The following example illustrates the effect of anonymous functions on overload resolution. > @@ -4580,19 +5065,19 @@ Anonymous functions in an argument list participate in type inference and overlo > > *end example* -### 11.17.5 Anonymous functions and dynamic binding +### 12.19.5 Anonymous functions and dynamic binding An anonymous function cannot be a receiver, argument, or operand of a dynamically bound operation. -### 11.17.6 Outer variables +### 12.19.6 Outer variables -#### 11.17.6.1 General +#### 12.19.6.1 General Any local variable, value parameter, or parameter array whose scope includes the *lambda_expression* or *anonymous_method_expression* is called an ***outer variable*** of the anonymous function. In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member. -#### 11.17.6.2 Captured outer variables +#### 12.19.6.2 Captured outer variables -When an outer variable is referenced by an anonymous function, the outer variable is said to have been ***captured*** by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated ([§9.2.8](variables.md#928-local-variables)). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection. +When an outer variable is referenced by an anonymous function, the outer variable is said to have been ***captured*** by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated ([§9.2.9](variables.md#929-local-variables)). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection. > *Example*: In the example > @@ -4629,11 +5114,11 @@ When an outer variable is referenced by an anonymous function, the outer variabl > > *end example* -When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable ([§22.4](unsafe-code.md#224-fixed-and-moveable-variables)), but is instead considered to be a moveable variable. However, captured outer variables cannot be used in a `fixed` statement ([§22.7](unsafe-code.md#227-the-fixed-statement)), so the address of a captured outer variable cannot be taken. +When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable ([§23.4](unsafe-code.md#234-fixed-and-moveable-variables)), but is instead considered to be a moveable variable. However, captured outer variables cannot be used in a `fixed` statement ([§23.7](unsafe-code.md#237-the-fixed-statement)), so the address of a captured outer variable cannot be taken. > *Note*: Unlike an uncaptured variable, a captured local variable can be simultaneously exposed to multiple threads of execution. *end note* -#### 11.17.6.3 Instantiation of local variables +#### 12.19.6.3 Instantiation of local variables A local variable is considered to be ***instantiated*** when execution enters the scope of the variable. @@ -4850,11 +5335,11 @@ Separate anonymous functions can capture the same instance of an outer variable. > > *end example* -### 11.17.7 Evaluation of anonymous function expressions +### 12.19.7 Evaluation of anonymous function expressions An anonymous function `F` shall always be converted to a delegate type `D` or an expression-tree type `E`, either directly or through the execution of a delegate creation expression `new D(F)`. This conversion determines the result of the anonymous function, as described in [§10.7](conversions.md#107-anonymous-function-conversions). -### 11.17.8 Implementation Example +### 12.19.8 Implementation Example **This subclause is informative.** @@ -4956,7 +5441,7 @@ class Test } ``` -The lifetime of the local variable must now be extended to at least the lifetime of the anonymous function delegate. This can be achieved by “hoisting” the local variable into a field of a compiler-generated class. Instantiation of the local variable ([§11.17.6.3](expressions.md#111763-instantiation-of-local-variables)) then corresponds to creating an instance of the compiler generated class, and accessing the local variable corresponds to accessing a field in the instance of the compiler generated class. Furthermore, the anonymous function becomes an instance method of the compiler-generated class: +The lifetime of the local variable must now be extended to at least the lifetime of the anonymous function delegate. This can be achieved by “hoisting” the local variable into a field of a compiler-generated class. Instantiation of the local variable ([§12.19.6.3](expressions.md#121963-instantiation-of-local-variables)) then corresponds to creating an instance of the compiler generated class, and accessing the local variable corresponds to accessing a field in the instance of the compiler generated class. Furthermore, the anonymous function becomes an instance method of the compiler-generated class: ```csharp @@ -5052,9 +5537,9 @@ The same technique applied here to capture local variables can also be used when **End of informative text.** -## 11.18 Query expressions +## 12.20 Query expressions -### 11.18.1 General +### 12.20.1 General ***Query expressions*** provide a language-integrated syntax for queries that is similar to relational and hierarchical query languages such as SQL and XQuery. @@ -5140,17 +5625,17 @@ query_continuation A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a ***range variable*** that ranges over the elements of a ***sequence***. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. Each `where` clause is a filter that excludes items from the result. Each `join` clause compares specified keys of the source sequence with keys of another sequence, yielding matching pairs. Each `orderby` clause reorders items according to specified criteria.The final `select` or `group` clause specifies the shape of the result in terms of the range variables. Finally, an `into` clause can be used to “splice” queries by treating the results of one query as a generator in a subsequent query. -### 11.18.2 Ambiguities in query expressions +### 12.20.2 Ambiguities in query expressions Query expressions use a number of contextual keywords ([§6.4.4](lexical-structure.md#644-keywords)): `ascending`, `by`, `descending`, `equals`, `from`, `group`, `into`, `join`, `let`, `on`, `orderby`, `select` and `where`. To avoid ambiguities that could arise from the use of these identifiers both as keywords and simple names these identifiers are considered keywords anywhere within a query expression, unless they are prefixed with “`@`” ([§6.4.4](lexical-structure.md#644-keywords)) in which case they are considered identifiers. For this purpose, a query expression is any expression that starts with “`from` *identifier*” followed by any token except “`;`”, “`=`” or “`,`”. -### 11.18.3 Query expression translation +### 12.20.3 Query expression translation -#### 11.18.3.1 General +#### 12.20.3.1 General -The C# language does not specify the execution semantics of query expressions. Rather, query expressions are translated into invocations of methods that adhere to the query-expression pattern ([§11.18.4](expressions.md#11184-the-query-expression-pattern)). Specifically, query expressions are translated into invocations of methods named `Where`, `Select`, `SelectMany`, `Join`, `GroupJoin`, `OrderBy`, `OrderByDescending`, `ThenBy`, `ThenByDescending`, `GroupBy`, and `Cast`. These methods are expected to have particular signatures and return types, as described in [§11.18.4](expressions.md#11184-the-query-expression-pattern). These methods may be instance methods of the object being queried or extension methods that are external to the object. These methods implement the actual execution of the query. +The C# language does not specify the execution semantics of query expressions. Rather, query expressions are translated into invocations of methods that adhere to the query-expression pattern ([§12.20.4](expressions.md#12204-the-query-expression-pattern)). Specifically, query expressions are translated into invocations of methods named `Where`, `Select`, `SelectMany`, `Join`, `GroupJoin`, `OrderBy`, `OrderByDescending`, `ThenBy`, `ThenByDescending`, `GroupBy`, and `Cast`. These methods are expected to have particular signatures and return types, as described in [§12.20.4](expressions.md#12204-the-query-expression-pattern). These methods may be instance methods of the object being queried or extension methods that are external to the object. These methods implement the actual execution of the query. The translation from query expressions to method invocations is a syntactic mapping that occurs before any type binding or overload resolution has been performed. Following translation of query expressions, the resulting method invocations are processed as regular method invocations, and this may in turn uncover compile time errors. These error conditions include, but are not limited to, methods that do not exist, arguments of the wrong types, and generic methods where type inference fails. @@ -5158,23 +5643,23 @@ A query expression is processed by repeatedly applying the following translation It is a compile time error for a query expression to include an assignment to a range variable, or the use of a range variable as an argument for a `ref` or `out` parameter. -Certain translations inject range variables with *transparent identifiers* denoted by \*. These are described further in [§11.18.3.8](expressions.md#111838-transparent-identifiers). +Certain translations inject range variables with *transparent identifiers* denoted by \*. These are described further in [§12.20.3.8](expressions.md#122038-transparent-identifiers). -#### 11.18.3.2 select and group … by clauses with continuations +#### 12.20.3.2 Query expressions with continuations -A query expression with a group clause using a property `Prop` of `y` and a query body `Q` containing a continuation in the form: +A query expression with a continuation following its query body ```csharp -from «y» in S group «y» by «y».Prop into «x» Q +from «x1» in «e1» «b1» into «x2» «b2» ``` -is translated into: +is translated into ```csharp -from «x» in ( from «y» in S group «y» by «y».Prop ) Q +from «x2» in ( from «x1» in «e1» «b1» ) «b2» ``` -The translations in the following sections assume that queries have no into continuations. +The translations in the following sections assume that queries have no continuations. > *Example*: The example: > @@ -5203,7 +5688,7 @@ The translations in the following sections assume that queries have no into cont > > *end example* -#### 11.18.3.3 Explicit range variable types +#### 12.20.3.3 Explicit range variable types A `from` clause that explicitly specifies a range variable type @@ -5261,7 +5746,7 @@ The translations in the following sections assume that queries have no explicit > *Note*: Explicit range variable types are useful for querying collections that implement the non-generic `IEnumerable` interface, but not the generic `IEnumerable` interface. In the example above, this would be the case if customers were of type `ArrayList`. *end note* -#### 11.18.3.4 Degenerate query expressions +#### 12.20.3.4 Degenerate query expressions A query expression of the form @@ -5292,9 +5777,9 @@ is translated into A degenerate query expression is one that trivially selects the elements of the source. -> *Note*: Later phases of the translation ([§11.18.3.6](expressions.md#111836-select-clauses) and [§11.18.3.7](expressions.md#111837-group-clauses)) remove degenerate queries introduced by other translation steps by replacing them with their source. It is important, however, to ensure that the result of a query expression is never the source object itself. Otherwise, returning the result of such a query might inadvertently expose private data (e.g., an element array) to a caller. Therefore this step protects degenerate queries written directly in source code by explicitly calling `Select` on the source. It is then up to the implementers of `Select` and other query operators to ensure that these methods never return the source object itself. *end note* +> *Note*: Later phases of the translation ([§12.20.3.6](expressions.md#122036-select-clauses) and [§12.20.3.7](expressions.md#122037-group-clauses)) remove degenerate queries introduced by other translation steps by replacing them with their source. It is important, however, to ensure that the result of a query expression is never the source object itself. Otherwise, returning the result of such a query might inadvertently expose private data (e.g., an element array) to a caller. Therefore this step protects degenerate queries written directly in source code by explicitly calling `Select` on the source. It is then up to the implementers of `Select` and other query operators to ensure that these methods never return the source object itself. *end note* -#### 11.18.3.5 From, let, where, join and orderby clauses +#### 12.20.3.5 From, let, where, join and orderby clauses A query expression with a second `from` clause followed by a `select` clause @@ -5570,11 +6055,11 @@ orderby «k1» , «k2» , ... , «kn» is translated into ```csharp -from «x» in ( «e» ) . -OrderBy ( «x» => «k1» ) . -ThenBy ( «x» => «k2» ) . -... . -ThenBy ( «x» => «kn» ) +from «x» in ( «e» ) . +OrderBy ( «x» => «k1» ) . +ThenBy ( «x» => «k2» ) . +... . +ThenBy ( «x» => «kn» ) ... ``` @@ -5600,7 +6085,7 @@ If an `ordering` clause specifies a descending direction indicator, an invocatio The following translations assume that there are no `let`, `where`, `join` or `orderby` clauses, and no more than the one initial `from` clause in each query expression. -#### 11.18.3.6 Select clauses +#### 12.20.3.6 Select clauses A query expression of the form @@ -5635,7 +6120,7 @@ except when `«v»` is the identifier `«x»`, the translation is simply > > *end example* -#### 11.18.3.7 Group clauses +#### 12.20.3.7 Group clauses A `group` clause @@ -5670,7 +6155,7 @@ except when `«v»` is the identifier `«x»`, the translation is > > *end example* -#### 11.18.3.8 Transparent identifiers +#### 12.20.3.8 Transparent identifiers Certain translations inject range variables with ***transparent identifiers*** denoted by `*`. Transparent identifiers exist only as an intermediate step in the query-expression translation process. @@ -5767,7 +6252,7 @@ In the translation steps described above, transparent identifiers are always int > where `x` and `y` are compiler-generated identifiers that are otherwise invisible and inaccessible. > *end example* -### 11.18.4 The query-expression pattern +### 12.20.4 The query-expression pattern The ***Query-expression pattern*** establishes a pattern of methods that types can implement to support query expressions. @@ -5828,11 +6313,11 @@ The methods above use the generic delegate types `Func` and `Func > *Note*: The `System.Linq` namespace provides an implementation of the query-expression pattern for any type that implements the `System.Collections.Generic.IEnumerable` interface. *end note* -## 11.19 Assignment operators +## 12.21 Assignment operators -### 11.19.1 General +### 12.21.1 General -The assignment operators assign a new value to a variable, a property, an event, or an indexer element. +All but one of the assignment operators assigns a new value to a variable, a property, an event, or an indexer element. The exception, `= ref`, assigns a variable reference ([§9.5](variables.md#95-variable-references)) to a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)). ```ANTLR assignment @@ -5840,52 +6325,62 @@ assignment ; assignment_operator - : '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' + : '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | right_shift_assignment ; ``` -The left operand of an assignment shall be an expression classified as a variable, a property access, an indexer access, or an event access. +The left operand of an assignment shall be an expression classified as a variable, or, except for `= ref`, a property access, an indexer access, an event access or a tuple. A declaration expression is not directly permitted as a left operand, but may occur as a step in the evaluation of a deconstructing assignment. -The `=` operator is called the ***simple assignment operator***. It assigns the value of the right operand to the variable, property, or indexer element given by the left operand. The left operand of the simple assignment operator shall not be an event access (except as described in [§14.8.2](classes.md#1482-field-like-events)). The simple assignment operator is described in [§11.19.2](expressions.md#11192-simple-assignment). +The `=` operator is called the ***simple assignment operator***. It assigns the value or values of the right operand to the variable, property, indexer element or tuple elements given by the left operand. The left operand of the simple assignment operator shall not be an event access (except as described in [§15.8.2](classes.md#1582-field-like-events)). The simple assignment operator is described in [§12.21.2](expressions.md#12212-simple-assignment). -The assignment operators other than the `=` operator are called the ***compound assignment operators***. These operators perform the indicated operation on the two operands, and then assign the resulting value to the variable, property, or indexer element given by the left operand. The compound assignment operators are described in [§11.19.3](expressions.md#11193-compound-assignment). +The operator `= ref` is called the ***ref assignment operator***. It makes the right operand, which must be a *variable_reference* ([§9.5](variables.md#95-variable-references)), the referent of the reference variable designated by the left operand. The ref assignment operator is described in [§12.21.3](expressions.md#12213-ref-assignment). -The `+=` and `-=` operators with an event access expression as the left operand are called the ***event assignment operators***. No other assignment operator is valid with an event access as the left operand. The event assignment operators are described in [§11.19.4](expressions.md#11194-event-assignment). +The assignment operators other than the `=` and `= ref` operators are called the ***compound assignment operators***. These operators perform the indicated operation on the two operands, and then assign the resulting value to the variable, property, or indexer element given by the left operand. The compound assignment operators are described in [§12.21.4](expressions.md#12214-compound-assignment). + +The `+=` and `-=` operators with an event access expression as the left operand are called the ***event assignment operators***. No other assignment operator is valid with an event access as the left operand. The event assignment operators are described in [§12.21.5](expressions.md#12215-event-assignment). The assignment operators are right-associative, meaning that operations are grouped from right to left. > *Example*: An expression of the form `a = b = c` is evaluated as `a = (b = c)`. *end example* -### 11.19.2 Simple assignment +### 12.21.2 Simple assignment The `=` operator is called the simple assignment operator. -If the left operand of a simple assignment is of the form `E.P` or `E[Ei]` where `E` has the compile-time type `dynamic`, then the assignment is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case, the compile-time type of the assignment expression is `dynamic`, and the resolution described below will take place at run-time based on the run-time type of `E`. If the left operand is of the form `E[Ei]` where at least one element of `Ei` has the compile-time type `dynamic`, and the compile-time type of `E` is not an array, the resulting indexer access is dynamically bound, but with limited compile-time checking ([§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation)). +If the left operand of a simple assignment is of the form `E.P` or `E[Ei]` where `E` has the compile-time type `dynamic`, then the assignment is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the assignment expression is `dynamic`, and the resolution described below will take place at run-time based on the run-time type of `E`. If the left operand is of the form `E[Ei]` where at least one element of `Ei` has the compile-time type `dynamic`, and the compile-time type of `E` is not an array, the resulting indexer access is dynamically bound, but with limited compile-time checking ([§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation)). -In a simple assignment, the right operand shall be an expression that is implicitly convertible to the type of the left operand. The operation assigns the value of the right operand to the variable, property, or indexer element given by the left operand. +A simple assignment where the left operand is classified as a tuple is also called a ***deconstructing assignment***. If any of the tuple elements of the left operand has an element name, a compile-time error occurs. If any of the tuple elements of the left operand is a *declaration_expression* and any other element is not a *declaration_expression* or a simple discard, a compile-time error occurs. -The result of a simple assignment expression is the value assigned to the left operand. The result has the same type as the left operand, and is always classified as a value. +The type of a simple assignment `x = y` is the type of an assignment to `x` of `y`, which is recursively determined as follows: -If the left operand is a property or indexer access, the property or indexer shall have an accessible set accessor. If this is not the case, a binding-time error occurs. +- If `x` is a tuple expression `(x1, ..., xn)`, and `y` can be deconstructed to a tuple expression `(y1, ..., yn)` with `n` elements ([§12.7](expressions.md#127-deconstruction)), and each assignment to `xi` of `yi` has the type `Ti`, then the assignment has the type `(T1, ..., Tn)`. +- Otherwise, if `x` is classified as a variable, the variable is not `readonly`, `x` has a type `T`, and `y` has an implicit conversion to `T`, then the assignment has the type `T`. +- Otherwise, if `x` is classified as an implicitly typed variable (i.e. an implicitly typed declaration expression) and `y` has a type `T`, then the inferred type of the variable is `T`, and the assignment has the type `T`. +- Otherwise, if `x` is classified as a property or indexer access, the property or indexer has an accessible set accessor, `x` has a type `T`, and `y` has an implicit conversion to `T`, then the assignment has the type `T`. +- Otherwise the assignment is not valid and a binding-time error occurs. -The run-time processing of a simple assignment of the form `x` = `y` consists of the following steps: +The run-time processing of a simple assignment of the form `x = y` with type `T` is performed as an assignment to `x` of `y` with type `T`, which consists of the following recursive steps: -- If `x` is classified as a variable: - - `x` is evaluated to produce the variable. - - `y` is evaluated and, if required, converted to the type of `x` through an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). +- `x` is evaluated if it wasn’t already. +- If `x` is classified as a variable, `y` is evaluated and, if required, converted to `T` through an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). - If the variable given by `x` is an array element of a *reference_type*, a run-time check is performed to ensure that the value computed for `y` is compatible with the array instance of which `x` is an element. The check succeeds if `y` is `null`, or if an implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) exists from the type of the instance referenced by `y` to the actual element type of the array instance containing `x`. Otherwise, a `System.ArrayTypeMismatchException` is thrown. - - The value resulting from the evaluation and conversion of `y` is stored into the location given by the evaluation of `x`. + - The value resulting from the evaluation and conversion of `y` is stored into the location given by the evaluation of `x`, and is yielded as a result of the assignment. - If `x` is classified as a property or indexer access: - - The instance expression (if `x` is not `static`) and the argument list (if `x` is an indexer access) associated with `x` are evaluated, and the results are used in the subsequent set accessor invocation. - - `y` is evaluated and, if required, converted to the type of `x` through an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). - - The set accessor of `x` is invoked with the value computed for `y` as its value argument. + - `y` is evaluated and, if required, converted to `T` through an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). + - The set accessor of `x` is invoked with the value resulting from the evaluation and conversion of `y` as its value argument. + - The value resulting from the evaluation and conversion of `y` is yielded as the result of the assignment. +- If `x` is classified as a tuple `(x1, ..., xn)` with arity `n`: + - `y` is deconstructed with `n` elements to a tuple expression `e`. + - a result tuple `t` is created by converting `e` to `T` using an implicit tuple conversion. + - for each `xi` in order from left to right, an assignment to `xi` of `t.Itemi` is performed, except that the `xi` are not evaluated again. + - `t` is yielded as the result of the assignment. > *Note*: if the compile time type of `x` is `dynamic` and there is an implicit conversion from the compile time type of `y` to `dynamic`, no runtime resolution is required. *end note* -> *Note*: The array co-variance rules ([§16.6](arrays.md#166-array-covariance)) permit a value of an array type `A[]` to be a reference to an instance of an array type `B[]`, provided an implicit reference conversion exists from `B` to `A`. Because of these rules, assignment to an array element of a *reference_type* requires a run-time check to ensure that the value being assigned is compatible with the array instance. In the example +> *Note*: The array co-variance rules ([§17.6](arrays.md#176-array-covariance)) permit a value of an array type `A[]` to be a reference to an instance of an array type `B[]`, provided an implicit reference conversion exists from `B` to `A`. Because of these rules, assignment to an array element of a *reference_type* requires a run-time check to ensure that the value being assigned is compatible with the array instance. In the example > > > ```csharp @@ -5900,9 +6395,8 @@ The run-time processing of a simple assignment of the form `x` = `y` consists > > *end note* -When a property or indexer declared in a *struct_type* is the target of an assignment, the instance expression associated with the property or indexer access shall be classified as a variable. If the instance expression is classified as a value, a binding-time error occurs. - -> *Note*: Because of [§11.7.6](expressions.md#1176-member-access), the same rule also applies to fields. *end note* +When a property or indexer declared in a *struct_type* is the target of an assignment, the instance expression associated with the property or indexer access shall be classified as a variable. If the instance expression is classified as a value, a binding-time error occurs. +> *Note*: Because of [§12.8.7](expressions.md#1287-member-access), the same rule also applies to fields. *end note* @@ -5984,11 +6478,61 @@ When a property or indexer declared in a *struct_type* is the target of an assig > > *end example* -### 11.19.3 Compound assignment +### 12.21.3 Ref assignment + +The `= ref` operator is known as the *ref assignment* operator. + +The left operand shall be an expression that binds to a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)), a reference parameter (other than `this`), an output parameter, or an input parameter. The right operand shall be an expression that yields a *variable_reference* designating a value of the same type as the left operand. + +It is a compile time error if the ref-safe-context ([§9.7.2](variables.md#972-ref-safe-contexts)) of the left operand is wider than the ref-safe-context of the right operand. + +The right operand shall be definitely assigned at the point of the ref assignment. + +When the left operand binds to an `out` parameter, it is an error if that `out` parameter has not been definitely assigned at the beginning of the ref assignment operator. + +If the left operand is a writeable ref (i.e., it designates anything other than a `ref readonly` local or `in` parameter), then the right operand shall be a writeable *variable_reference*. If the right operand variable is writeable, the left operand may be a writeable or read-only ref. + +The operation makes the left operand an alias of the right operand variable. The alias may be made read-only even if the right operand variable is writeable. + +The ref assignment operator yields a *variable_reference* of the assigned type. It is writeable if the left operand is writeable. + +The ref assignment operator must not read the storage location referenced by the right operand. + +> *Example*: Here are some examples of using `= ref`: +> +> +> ```csharp +> public static int M1() { ... } +> public static ref int M2() { ... } +> public static ref uint M2u() { ... } +> public static ref readonly int M3() { ... } +> public static void Test() +> { +> int v = 42; +> ref int r1 = ref v; // OK, r1 refers to v, which has value 42 +> r1 = ref M1(); // Error; M1 returns a value, not a reference +> r1 = ref M2(); // OK; makes an alias +> r1 = ref M2u(); // Error; lhs and rhs have different types +> r1 = ref M3(); // error; M3 returns a ref readonly, which r1 cannot honor +> ref readonly int r2 = ref v; // OK; make readonly alias to ref +> r2 = ref M2(); // OK; makes an alias, adding read-only protection +> r2 = ref M3(); // OK; makes an alias and honors the read-only +> r2 = ref (r1 = ref M2()); // OK; r1 is an alias to a writable variable, +> // r2 is an alias (with read-only access) to the same variable +> } +> ``` +> +> *end example* + + + +> *Note*: When reading code using an `= ref` operator, it can be tempting to read the `ref` part as being part of the operand. This is particularly confusing when the operand is a conditional `?:` expression. For example, when reading `ref int a = ref b ? ref x : ref y;` it’s important to read this as `= ref` being the operator, and `b ? ref x : ref y` being the right operand: `ref int a = ref (b ? ref x : ref y);`. Importantly, the expression `ref b` is *not* part of that statement, even though it might appear so at first glance. *end note* + +### 12.21.4 Compound assignment -If the left operand of a compound assignment is of the form `E.P` or `E[Ei]` where `E` has the compile-time type `dynamic`, then the assignment is dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). In this case, the compile-time type of the assignment expression is `dynamic`, and the resolution described below will take place at run-time based on the run-time type of `E`. If the left operand is of the form `E[Ei]` where at least one element of `Ei` has the compile-time type `dynamic`, and the compile-time type of `E` is not an array, the resulting indexer access is dynamically bound, but with limited compile-time checking ([§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation)). +If the left operand of a compound assignment is of the form `E.P` or `E[Ei]` where `E` has the compile-time type `dynamic`, then the assignment is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the assignment expression is `dynamic`, and the resolution described below will take place at run-time based on the run-time type of `E`. If the left operand is of the form `E[Ei]` where at least one element of `Ei` has the compile-time type `dynamic`, and the compile-time type of `E` is not an array, the resulting indexer access is dynamically bound, but with limited compile-time checking ([§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation)). -An operation of the form `x «op»= y` is processed by applying binary operator overload resolution ([§11.4.5](expressions.md#1145-binary-operator-overload-resolution)) as if the operation was written `x «op» y`. Then, +An operation of the form `x «op»= y` is processed by applying binary operator overload resolution ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) as if the operation was written `x «op» y`. Then, - If the return type of the selected operator is implicitly convertible to the type of `x`, the operation is evaluated as `x = x «op» y`, except that `x` is evaluated only once. - Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of `x` , and if `y` is implicitly convertible to the type of `x`  or the operator is a shift operator, then the operation is evaluated as `x = (T)(x «op» y)`, where `T` is the type of `x`, except that `x` is evaluated only once. @@ -6000,7 +6544,7 @@ The term “evaluated only once” means that in the evaluation of `x «op» y When the left operand of a compound assignment is a property access or indexer access, the property or indexer shall have both a get accessor and a set accessor. If this is not the case, a binding-time error occurs. -The second rule above permits `x «op»= y` to be evaluated as `x = (T)(x «op» y)` in certain contexts. The rule exists such that the predefined operators can be used as compound operators when the left operand is of type `sbyte`, `byte`, `short`, `ushort`, or `char`. Even when both arguments are of one of those types, the predefined operators produce a result of type `int`, as described in [§11.4.7.3](expressions.md#11473-binary-numeric-promotions). Thus, without a cast it would not be possible to assign the result to the left operand. +The second rule above permits `x «op»= y` to be evaluated as `x = (T)(x «op» y)` in certain contexts. The rule exists such that the predefined operators can be used as compound operators when the left operand is of type `sbyte`, `byte`, `short`, `ushort`, or `char`. Even when both arguments are of one of those types, the predefined operators produce a result of type `int`, as described in [§12.4.7.3](expressions.md#12473-binary-numeric-promotions). Thus, without a cast it would not be possible to assign the result to the left operand. The intuitive effect of the rule for predefined operators is simply that `x «op»= y` is permitted if both of `x «op» y` and `x = y` are permitted. @@ -6027,7 +6571,7 @@ The intuitive effect of the rule for predefined operators is simply that `x «op > *Note*: This also means that compound assignment operations support lifted operators. Since a compound assignment `x «op»= y` is evaluated as either `x = x «op» y` or `x = (T)(x «op» y)`, the rules of evaluation implicitly cover lifted operators. *end note* -### 11.19.4 Event assignment +### 12.21.5 Event assignment If the left operand of `a += or -=` operator is classified as an event access, then the expression is evaluated as follows: @@ -6035,9 +6579,9 @@ If the left operand of `a += or -=` operator is classified as an event access, - The right operand of the `+=` or `-=` operator is evaluated, and, if required, converted to the type of the left operand through an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)). - An event accessor of the event is invoked, with an argument list consisting of the value computed in the previous step. If the operator was `+=`, the add accessor is invoked; if the operator was `-=`, the remove accessor is invoked. -An event assignment expression does not yield a value. Thus, an event assignment expression is valid only in the context of a *statement_expression* ([§12.7](statements.md#127-expression-statements)). +An event assignment expression does not yield a value. Thus, an event assignment expression is valid only in the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)). -## 11.20 Expression +## 12.22 Expression An *expression* is either a *non_assignment_expression* or an *assignment*. @@ -6048,13 +6592,14 @@ expression ; non_assignment_expression - : conditional_expression + : declaration_expression + | conditional_expression | lambda_expression | query_expression ; ``` -## 11.21 Constant expressions +## 12.23 Constant expressions A constant expression is an expression that shall be fully evaluated at compile-time. @@ -6064,22 +6609,22 @@ constant_expression ; ``` -A constant expression may be either a value type or a reference type. If a constant expression is a value type, it must be one of the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,` or any enumeration type. If a constant expression is a reference type, it must be the `string` type, a default value expression ([§11.7.19](expressions.md#11719-default-value-expressions)) for some reference type, or the value of the expression must be `null`. +A constant expression may be either a value type or a reference type. If a constant expression is a value type, it must be one of the following types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,` or any enumeration type. If a constant expression is a reference type, it must be the `string` type, a default value expression ([§12.8.20](expressions.md#12820-default-value-expressions)) for some reference type, or the value of the expression must be `null`. Only the following constructs are permitted in constant expressions: - Literals (including the `null` literal). - References to `const` members of class and struct types. - References to members of enumeration types. -- References to `const` parameters or local variables +- References to local constants. - Parenthesized subexpressions, which are themselves constant expressions. - Cast expressions. - `checked` and `unchecked` expressions. -- `nameof` expressions +- `nameof` expressions. - The predefined `+`, `–`, `!`, and `~` unary operators. - The predefined `+`, `–`, `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`, `&&`, `||`, `==`, `!=`, `<`, `>`, `<=`, and `>=` binary operators. - The `?:` conditional operator. -- `sizeof` expressions, provided the unmanaged-type is one of the types specified in [§22.6.9](unsafe-code.md#2269-the-sizeof-operator) for which `sizeof` returns a constant value. +- `sizeof` expressions, provided the unmanaged-type is one of the types specified in [§23.6.9](unsafe-code.md#2369-the-sizeof-operator) for which `sizeof` returns a constant value. - Default value expressions, provided the type is one of the types listed above. The following conversions are permitted in constant expressions: @@ -6113,21 +6658,22 @@ Whenever an expression fulfills the requirements listed above, the expression is The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions, except that where run-time evaluation would have thrown an exception, compile-time evaluation causes a compile-time error to occur. -Unless a constant expression is explicitly placed in an `unchecked` context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)). +Unless a constant expression is explicitly placed in an `unchecked` context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)). Constant expressions are required in the contexts listed below and this is indicated in the grammar by using *constant_expression*. In these contexts, a compile-time error occurs if an expression cannot be fully evaluated at compile-time. -- Constant declarations ([§14.4](classes.md#144-constants)) -- Enumeration member declarations ([§18.4](enums.md#184-enum-members)) -- Default arguments of formal parameter lists ([§14.6.2](classes.md#1462-method-parameters)) -- `case` labels of a `switch` statement ([§12.8.3](statements.md#1283-the-switch-statement)). -- `goto case` statements ([§12.10.4](statements.md#12104-the-goto-statement)) -- Dimension lengths in an array creation expression ([§11.7.15.5](expressions.md#117155-array-creation-expressions)) that includes an initializer. -- Attributes ([§21](attributes.md#21-attributes)) +- Constant declarations ([§15.4](classes.md#154-constants)) +- Enumeration member declarations ([§19.4](enums.md#194-enum-members)) +- Default arguments of formal parameter lists ([§15.6.2](classes.md#1562-method-parameters)) +- `case` labels of a `switch` statement ([§13.8.3](statements.md#1383-the-switch-statement)). +- `goto case` statements ([§13.10.4](statements.md#13104-the-goto-statement)) +- Dimension lengths in an array creation expression ([§12.8.16.5](expressions.md#128165-array-creation-expressions)) that includes an initializer. +- Attributes ([§22](attributes.md#22-attributes)) +- In a *constant_pattern* ([§11.2.3](patterns.md#1123-constant-pattern)) An implicit constant expression conversion ([§10.2.11](conversions.md#10211-implicit-constant-expression-conversions)) permits a constant expression of type `int` to be converted to `sbyte`, `byte`, `short`, `ushort`, `uint`, or `ulong`, provided the value of the constant expression is within the range of the destination type. -## 11.22 Boolean expressions +## 12.24 Boolean expressions A *boolean_expression* is an expression that yields a result of type `bool`; either directly or through application of `operator true` in certain contexts as specified in the following: @@ -6137,10 +6683,10 @@ boolean_expression ; ``` -The controlling conditional expression of an *if_statement* ([§12.8.2](statements.md#1282-the-if-statement)), *while_statement* ([§12.9.2](statements.md#1292-the-while-statement)), *do_statement* ([§12.9.3](statements.md#1293-the-do-statement)), or *for_statement* ([§12.9.4](statements.md#1294-the-for-statement)) is a *boolean_expression*. The controlling conditional expression of the `?:` operator ([§11.16](expressions.md#1116-conditional-operator)) follows the same rules as a *boolean_expression*, but for reasons of operator precedence is classified as a *null_coalescing_expression*. +The controlling conditional expression of an *if_statement* ([§13.8.2](statements.md#1382-the-if-statement)), *while_statement* ([§13.9.2](statements.md#1392-the-while-statement)), *do_statement* ([§13.9.3](statements.md#1393-the-do-statement)), or *for_statement* ([§13.9.4](statements.md#1394-the-for-statement)) is a *boolean_expression*. The controlling conditional expression of the `?:` operator ([§12.18](expressions.md#1218-conditional-operator)) follows the same rules as a *boolean_expression*, but for reasons of operator precedence is classified as a *null_coalescing_expression*. A *boolean_expression* `E` is required to be able to produce a value of type `bool`, as follows: - If E is implicitly convertible to `bool` then at run-time that implicit conversion is applied. -- Otherwise, unary operator overload resolution ([§11.4.4](expressions.md#1144-unary-operator-overload-resolution)) is used to find a unique best implementation of `operator true` on `E`, and that implementation is applied at run-time. +- Otherwise, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is used to find a unique best implementation of `operator true` on `E`, and that implementation is applied at run-time. - If no such operator is found, a binding-time error occurs. diff --git a/standard/foreword.md b/standard/foreword.md index 0563c8b0d..4e0f9616c 100644 --- a/standard/foreword.md +++ b/standard/foreword.md @@ -1,20 +1,31 @@ # Foreword -This specification replaces ECMA-334:2017. Changes from the previous edition include the addition of the following: +This specification replaces ECMA-334:2022. Changes from the previous edition include the addition of the following: -- Automatically implemented property initializers -- `await` in catch and finally blocks -- Exception filters -- Expression-bodied function members -- Extension Add methods in collection initializers -- Improved overload resolution -- Initialization of an accessible indexer -- Initialization of associative collections using indexers -- Interpolated strings -- `nameof` operator -- Null-conditional access operators `?.` and `?[]` -- Read-only auto-properties -- Relaxed rules for auto-properties -- `using static` - -All grammar is now expressed using ANTLR notation. +- Binary integer literals +- Embedded digit separators in numeric literals +- Leading-digit separators in binary and hexadecimal integer literals +- `out` variables +- Discards +- Tuple types +- Pattern Matching +- `ref` locals and returns, conditional `ref` expressions, `ref` with `this` in extension methods, and reassignment of `ref` local variables +- Local Functions +- More expression-bodied members +- `throw` Expressions +- Generalized `async` return types +- `async Main` method +- `default` literal expressions +- Non-trailing named arguments +- `private protected` access modifier +- `in` parameter modifier +- `readonly` structs +- `ref` structs +- Indexing movable fixed buffer without pinning +- Initializers on `stackalloc` arrays +- Pattern-based `fixed` statements +- `System.Delegate` and `System.Enum` as *class_type* constraints. +- Additional generic constraints +- Allow expression variables in more locations +- Attach attributes to the backing field of auto-implemented properties +- Reduce ambiguity of overload resolution diff --git a/standard/general-description.md b/standard/general-description.md index 760e3f349..a8ae96d8d 100644 --- a/standard/general-description.md +++ b/standard/general-description.md @@ -4,7 +4,7 @@ This specification is intended to be used by implementers, academics, and application programmers. As such, it contains a considerable amount of explanatory material that, strictly speaking, is not necessary in a formal language specification. -This standard is divided into the following subdivisions: front matter; language syntax, constraints, and semantics; and annexes. +This specification is divided into the following subdivisions: front matter; language syntax, constraints, and semantics; and annexes. Examples are provided to illustrate possible forms of the constructions described. References are used to refer to related clauses. Notes are provided to give advice or guidance to implementers or programmers. Annexes provide additional information and summarize the information contained in this specification. diff --git a/standard/grammar.md b/standard/grammar.md index 99ee7328a..2d7b1d7b7 100644 --- a/standard/grammar.md +++ b/standard/grammar.md @@ -207,12 +207,12 @@ keyword // Source: §6.4.4 Keywords contextual_keyword - : 'add' | 'alias' | 'ascending' | 'async' | 'await' - | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' - | 'get' | 'global' | 'group' | 'into' | 'join' - | 'let' | 'nameof' | 'on' | 'orderby' | 'partial' - | 'remove' | 'select' | 'set' | 'value' | 'var' - | 'when' | 'where' | 'yield' + : 'add' | 'alias' | 'ascending' | 'async' | 'await' + | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' + | 'get' | 'global' | 'group' | 'into' | 'join' + | 'let' | 'nameof' | 'on' | 'orderby' | 'partial' + | 'remove' | 'select' | 'set' | 'unmanaged' | 'value' + | 'var' | 'when' | 'where' | 'yield' ; // Source: §6.4.5.1 General @@ -623,6 +623,7 @@ non_nullable_value_type struct_type : type_name | simple_type + | tuple_type ; simple_type @@ -653,6 +654,14 @@ floating_point_type | 'double' ; +tuple_type + : '(' tuple_type_element (',' tuple_type_element)+ ')' + ; + +tuple_type_element + : type identifier? + ; + enum_type : type_name ; @@ -690,7 +699,38 @@ variable_reference : expression ; -// Source: §11.6.2.1 General +// Source: §11.2.1 General +pattern + : declaration_pattern + | constant_pattern + | var_pattern + ; + +// Source: §11.2.2 Declaration pattern +declaration_pattern + : type simple_designation + ; +simple_designation + : single_variable_designation + ; +single_variable_designation + : identifier + ; + +// Source: §11.2.3 Constant pattern +constant_pattern + : constant_expression + ; + +// Source: §11.2.4 Var pattern +var_pattern + : 'var' designation + ; +designation + : simple_designation + ; + +// Source: §12.6.2.1 General argument_list : argument (',' argument)* ; @@ -705,11 +745,12 @@ argument_name argument_value : expression + | 'in' variable_reference | 'ref' variable_reference | 'out' variable_reference ; -// Source: §11.7.1 General +// Source: §12.8.1 General primary_expression : primary_no_array_creation_expression | array_creation_expression @@ -720,6 +761,7 @@ primary_no_array_creation_expression | interpolated_string_expression | simple_name | parenthesized_expression + | tuple_expression | member_access | null_conditional_member_access | invocation_expression @@ -741,9 +783,10 @@ primary_no_array_creation_expression | anonymous_method_expression | pointer_member_access // unsafe code support | pointer_element_access // unsafe code support + | stackalloc_expression ; -// Source: §11.7.3 Interpolated string expressions +// Source: §12.8.3 Interpolated string expressions interpolated_string_expression : interpolated_regular_string_expression | interpolated_verbatim_string_expression @@ -851,17 +894,40 @@ fragment Close_Brace_Escape_Sequence : '}}' ; -// Source: §11.7.4 Simple names +// Source: §12.8.4 Simple names simple_name : identifier type_argument_list? ; -// Source: §11.7.5 Parenthesized expressions +// Source: §12.8.5 Parenthesized expressions parenthesized_expression : '(' expression ')' ; -// Source: §11.7.6.1 General +// Source: §12.8.6 Tuple expressions +tuple_expression + : '(' tuple_element (',' tuple_element)+ ')' + | deconstruction_expression + ; + +tuple_element + : (identifier ':')? expression + ; + +deconstruction_expression + : 'var' deconstruction_tuple + ; + +deconstruction_tuple + : '(' deconstruction_element (',' deconstruction_element)+ ')' + ; + +deconstruction_element + : deconstruction_tuple + | identifier + ; + +// Source: §12.8.7.1 General member_access : primary_expression '.' identifier type_argument_list? | predefined_type '.' identifier type_argument_list? @@ -874,7 +940,7 @@ predefined_type | 'ushort' ; -// Source: §11.7.7 Null Conditional Member Access +// Source: §12.8.8 Null Conditional Member Access null_conditional_member_access : primary_expression '?' '.' identifier type_argument_list? dependent_access* @@ -890,40 +956,40 @@ null_conditional_projection_initializer : primary_expression '?' '.' identifier type_argument_list? ; -// Source: §11.7.8.1 General +// Source: §12.8.9.1 General invocation_expression : primary_expression '(' argument_list? ')' ; -// Source: §11.7.9 Null Conditional Invocation Expression +// Source: §12.8.10 Null Conditional Invocation Expression null_conditional_invocation_expression : null_conditional_member_access '(' argument_list? ')' | null_conditional_element_access '(' argument_list? ')' ; -// Source: §11.7.10.1 General +// Source: §12.8.11.1 General element_access : primary_no_array_creation_expression '[' argument_list ']' ; -// Source: §11.7.11 Null Conditional Element Access +// Source: §12.8.12 Null Conditional Element Access null_conditional_element_access : primary_no_array_creation_expression '?' '[' argument_list ']' dependent_access* ; -// Source: §11.7.12 This access +// Source: §12.8.13 This access this_access : 'this' ; -// Source: §11.7.13 Base access +// Source: §12.8.14 Base access base_access : 'base' '.' identifier type_argument_list? | 'base' '[' argument_list ']' ; -// Source: §11.7.14 Postfix increment and decrement operators +// Source: §12.8.15 Postfix increment and decrement operators post_increment_expression : primary_expression '++' ; @@ -932,7 +998,7 @@ post_decrement_expression : primary_expression '--' ; -// Source: §11.7.15.2 Object creation expressions +// Source: §12.8.16.2 Object creation expressions object_creation_expression : 'new' type '(' argument_list? ')' object_or_collection_initializer? | 'new' type object_or_collection_initializer @@ -943,7 +1009,7 @@ object_or_collection_initializer | collection_initializer ; -// Source: §11.7.15.3 Object initializers +// Source: §12.8.16.3 Object initializers object_initializer : '{' member_initializer_list? '}' | '{' member_initializer_list ',' '}' @@ -967,7 +1033,7 @@ initializer_value | object_or_collection_initializer ; -// Source: §11.7.15.4 Collection initializers +// Source: §12.8.16.4 Collection initializers collection_initializer : '{' element_initializer_list '}' | '{' element_initializer_list ',' '}' @@ -987,7 +1053,7 @@ expression_list | expression_list ',' expression ; -// Source: §11.7.15.5 Array creation expressions +// Source: §12.8.16.5 Array creation expressions array_creation_expression : 'new' non_array_type '[' expression_list ']' rank_specifier* array_initializer? @@ -995,12 +1061,12 @@ array_creation_expression | 'new' rank_specifier array_initializer ; -// Source: §11.7.15.6 Delegate creation expressions +// Source: §12.8.16.6 Delegate creation expressions delegate_creation_expression : 'new' delegate_type '(' expression ')' ; -// Source: §11.7.15.7 Anonymous object creation expressions +// Source: §12.8.16.7 Anonymous object creation expressions anonymous_object_creation_expression : 'new' anonymous_object_initializer ; @@ -1022,7 +1088,7 @@ member_declarator | identifier '=' expression ; -// Source: §11.7.16 The typeof operator +// Source: §12.8.17 The typeof operator typeof_expression : 'typeof' '(' type ')' | 'typeof' '(' unbound_type_name ')' @@ -1044,12 +1110,12 @@ comma ; -// Source: §11.7.17 The sizeof operator +// Source: §12.8.18 The sizeof operator sizeof_expression : 'sizeof' '(' unmanaged_type ')' ; -// Source: §11.7.18 The checked and unchecked operators +// Source: §12.8.19 The checked and unchecked operators checked_expression : 'checked' '(' expression ')' ; @@ -1058,7 +1124,7 @@ unchecked_expression : 'unchecked' '(' expression ')' ; -// Source: §11.7.19 Default value expressions +// Source: §12.8.20 Default value expressions default_value_expression : explictly_typed_default | default_literal @@ -1072,7 +1138,26 @@ default_literal : 'default' ; -// Source: §11.7.20 Nameof expressions +// Source: §12.8.21 Stack allocation +stackalloc_expression + : 'stackalloc' unmanaged_type '[' expression ']' + | 'stackalloc' unmanaged_type? '[' constant_expression? ']' + stackalloc_initializer + ; + +stackalloc_initializer + : '{' stackalloc_initializer_element_list '}' + ; + +stackalloc_initializer_element_list + : stackalloc_element_initializer (',' stackalloc_element_initializer)* ','? + ; + +stackalloc_element_initializer + : expression + ; + +// Source: §12.8.22 Nameof expressions nameof_expression : 'nameof' '(' named_entity ')' ; @@ -1089,7 +1174,7 @@ named_entity_target | qualified_alias_member ; -// Source: §11.8.1 General +// Source: §12.9.1 General unary_expression : primary_expression | '+' unary_expression @@ -1104,7 +1189,7 @@ unary_expression | addressof_expression // unsafe code support ; -// Source: §11.8.6 Prefix increment and decrement operators +// Source: §12.9.6 Prefix increment and decrement operators pre_increment_expression : '++' unary_expression ; @@ -1113,17 +1198,17 @@ pre_decrement_expression : '--' unary_expression ; -// Source: §11.8.7 Cast expressions +// Source: §12.9.7 Cast expressions cast_expression : '(' type ')' unary_expression ; -// Source: §11.8.8.1 General +// Source: §12.9.8.1 General await_expression : 'await' unary_expression ; -// Source: §11.9.1 General +// Source: §12.10.1 General multiplicative_expression : unary_expression | multiplicative_expression '*' unary_expression @@ -1137,14 +1222,14 @@ additive_expression | additive_expression '-' multiplicative_expression ; -// Source: §11.10 Shift operators +// Source: §12.11 Shift operators shift_expression : additive_expression | shift_expression '<<' additive_expression | shift_expression right_shift additive_expression ; -// Source: §11.11.1 General +// Source: §12.12.1 General relational_expression : shift_expression | relational_expression '<' shift_expression @@ -1152,6 +1237,7 @@ relational_expression | relational_expression '<=' shift_expression | relational_expression '>=' shift_expression | relational_expression 'is' type + | relational_expression 'is' pattern | relational_expression 'as' type ; @@ -1161,7 +1247,7 @@ equality_expression | equality_expression '!=' relational_expression ; -// Source: §11.12.1 General +// Source: §12.13.1 General and_expression : equality_expression | and_expression '&' equality_expression @@ -1177,7 +1263,7 @@ inclusive_or_expression | inclusive_or_expression '|' exclusive_or_expression ; -// Source: §11.13.1 General +// Source: §12.14.1 General conditional_and_expression : inclusive_or_expression | conditional_and_expression '&&' inclusive_or_expression @@ -1188,25 +1274,37 @@ conditional_or_expression | conditional_or_expression '||' conditional_and_expression ; -// Source: §11.14 The null coalescing operator +// Source: §12.15 The null coalescing operator null_coalescing_expression : conditional_or_expression | conditional_or_expression '??' null_coalescing_expression | throw_expression ; -// Source: §11.15 The throw expression operator +// Source: §12.16 The throw expression operator throw_expression : 'throw' null_coalescing_expression ; -// Source: §11.16 Conditional operator +// Source: §12.17 Declaration expressions +declaration_expression + : local_variable_type identifier + ; + +local_variable_type + : type + | 'var' + ; + +// Source: §12.18 Conditional operator conditional_expression : null_coalescing_expression | null_coalescing_expression '?' expression ':' expression + | null_coalescing_expression '?' 'ref' variable_reference ':' + 'ref' variable_reference ; -// Source: §11.17.1 General +// Source: §12.19.1 General lambda_expression : 'async'? anonymous_function_signature '=>' anonymous_function_body ; @@ -1236,6 +1334,7 @@ explicit_anonymous_function_parameter anonymous_function_parameter_modifier : 'ref' | 'out' + | 'in' ; implicit_anonymous_function_signature @@ -1255,10 +1354,11 @@ implicit_anonymous_function_parameter anonymous_function_body : null_conditional_invocation_expression | expression + | 'ref' variable_reference | block ; -// Source: §11.18.1 General +// Source: §12.20.1 General query_expression : from_clause query_body ; @@ -1337,39 +1437,40 @@ query_continuation : 'into' identifier query_body ; -// Source: §11.19.1 General +// Source: §12.21.1 General assignment : unary_expression assignment_operator expression ; assignment_operator - : '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' + : '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | right_shift_assignment ; -// Source: §11.20 Expression +// Source: §12.22 Expression expression : non_assignment_expression | assignment ; non_assignment_expression - : conditional_expression + : declaration_expression + | conditional_expression | lambda_expression | query_expression ; -// Source: §11.21 Constant expressions +// Source: §12.23 Constant expressions constant_expression : expression ; -// Source: §11.22 Boolean expressions +// Source: §12.24 Boolean expressions boolean_expression : expression ; -// Source: §12.1 General +// Source: §13.1 General statement : labeled_statement | declaration_statement @@ -1393,60 +1494,83 @@ embedded_statement | fixed_statement // unsafe code support ; -// Source: §12.3.1 General +// Source: §13.3.1 General block : '{' statement_list? '}' ; -// Source: §12.3.2 Statement lists +// Source: §13.3.2 Statement lists statement_list : statement+ ; -// Source: §12.4 The empty statement +// Source: §13.4 The empty statement empty_statement : ';' ; -// Source: §12.5 Labeled statements +// Source: §13.5 Labeled statements labeled_statement : identifier ':' statement ; -// Source: §12.6.1 General +// Source: §13.6.1 General declaration_statement : local_variable_declaration ';' | local_constant_declaration ';' - | local_function_declaration + | local_function_declaration ; -// Source: §12.6.2 Local variable declarations +// Source: §13.6.2 Local variable declarations local_variable_declaration - : local_variable_type local_variable_declarators + : implicitly_typed_local_variable_declaration + | explicitly_typed_local_variable_declaration + | ref_local_variable_declaration ; -local_variable_type - : type - | 'var' +// Source: §13.6.2.1 Implicitly typed local variable declarations +implicitly_typed_local_variable_declaration + : 'var' implicitly_typed_local_variable_declarator + | ref_kind 'var' ref_local_variable_declarator ; -local_variable_declarators - : local_variable_declarator - | local_variable_declarators ',' local_variable_declarator +implicitly_typed_local_variable_declarator + : identifier '=' expression ; -local_variable_declarator - : identifier - | identifier '=' local_variable_initializer +// Source: §13.6.2.2 Explicitly typed local variable declarations +explicitly_typed_local_variable_declaration + : type explicitly_typed_local_variable_declarators + ; + +explicitly_typed_local_variable_declarators + : explicitly_typed_local_variable_declarator + (',' explicitly_typed_local_variable_declarator)* + ; + +explicitly_typed_local_variable_declarator + : identifier ('=' local_variable_initializer)? ; local_variable_initializer : expression | array_initializer - | stackalloc_initializer // unsafe code support ; -// Source: §12.6.3 Local constant declarations +// Source: §13.6.2.3 Ref local variable declarations +ref_local_variable_declaration + : ref_kind type ref_local_variable_declarators + ; + +ref_local_variable_declarators + : ref_local_variable_declarator (',' ref_local_variable_declarator)* + ; + +ref_local_variable_declarator + : identifier '=' 'ref' variable_reference + ; + +// Source: §13.6.3 Local constant declarations local_constant_declaration : 'const' type constant_declarators ; @@ -1459,18 +1583,27 @@ constant_declarator : identifier '=' constant_expression ; -// Source: §12.6.4 Local function declarations +// Source: §13.6.4 Local function declarations local_function_declaration - : local_function_header local_function_body + : local_function_modifier* return_type local_function_header + local_function_body + | ref_local_function_modifier* ref_kind ref_return_type + local_function_header ref_local_function_body ; local_function_header - : local_function_modifier* return_type identifier type_parameter_list? - ( formal_parameter_list? ) type_parameter_constraints_clause* + : identifier '(' formal_parameter_list? ')' + | identifier type_parameter_list '(' formal_parameter_list? ')' + type_parameter_constraints_clause* ; + local_function_modifier - : 'async' - | 'unsafe' + : ref_local_function_modifier + | 'async' + ; + +ref_local_function_modifier + : unsafe_modifier // unsafe code support ; local_function_body @@ -1479,7 +1612,12 @@ local_function_body | '=>' expression ';' ; -// Source: §12.7 Expression statements +ref_local_function_body + : block + | '=>' 'ref' variable_reference ';' + ; + +// Source: §13.7 Expression statements expression_statement : statement_expression ';' ; @@ -1496,20 +1634,20 @@ statement_expression | await_expression ; -// Source: §12.8.1 General +// Source: §13.8.1 General selection_statement : if_statement | switch_statement ; -// Source: §12.8.2 The if statement +// Source: §13.8.2 The if statement if_statement : 'if' '(' boolean_expression ')' embedded_statement | 'if' '(' boolean_expression ')' embedded_statement 'else' embedded_statement ; -// Source: §12.8.3 The switch statement +// Source: §13.8.3 The switch statement switch_statement : 'switch' '(' expression ')' switch_block ; @@ -1523,11 +1661,15 @@ switch_section ; switch_label - : 'case' constant_expression ':' + : 'case' pattern case_guard? ':' | 'default' ':' ; -// Source: §12.9.1 General +case_guard + : 'when' expression + ; + +// Source: §13.9.1 General iteration_statement : while_statement | do_statement @@ -1535,17 +1677,17 @@ iteration_statement | foreach_statement ; -// Source: §12.9.2 The while statement +// Source: §13.9.2 The while statement while_statement : 'while' '(' boolean_expression ')' embedded_statement ; -// Source: §12.9.3 The do statement +// Source: §13.9.3 The do statement do_statement : 'do' embedded_statement 'while' '(' boolean_expression ')' ';' ; -// Source: §12.9.4 The for statement +// Source: §13.9.4 The for statement for_statement : 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')' embedded_statement @@ -1568,13 +1710,13 @@ statement_expression_list : statement_expression (',' statement_expression)* ; -// Source: §12.9.5 The foreach statement +// Source: §13.9.5 The foreach statement foreach_statement - : 'foreach' '(' local_variable_type identifier 'in' expression ')' - embedded_statement + : 'foreach' '(' ref_kind? local_variable_type identifier 'in' + expression ')' embedded_statement ; -// Source: §12.10.1 General +// Source: §13.10.1 General jump_statement : break_statement | continue_statement @@ -1583,34 +1725,36 @@ jump_statement | throw_statement ; -// Source: §12.10.2 The break statement +// Source: §13.10.2 The break statement break_statement : 'break' ';' ; -// Source: §12.10.3 The continue statement +// Source: §13.10.3 The continue statement continue_statement : 'continue' ';' ; -// Source: §12.10.4 The goto statement +// Source: §13.10.4 The goto statement goto_statement : 'goto' identifier ';' | 'goto' 'case' constant_expression ';' | 'goto' 'default' ';' ; -// Source: §12.10.5 The return statement +// Source: §13.10.5 The return statement return_statement - : 'return' expression? ';' + : 'return' ';' + | 'return' expression ';' + | 'return' 'ref' variable_reference ';' ; -// Source: §12.10.6 The throw statement +// Source: §13.10.6 The throw statement throw_statement : 'throw' expression? ';' ; -// Source: §12.11 The try statement +// Source: §13.11 The try statement try_statement : 'try' block catch_clauses | 'try' block catch_clauses? finally_clause @@ -1642,7 +1786,7 @@ finally_clause : 'finally' block ; -// Source: §12.12 The checked and unchecked statements +// Source: §13.12 The checked and unchecked statements checked_statement : 'checked' block ; @@ -1651,12 +1795,12 @@ unchecked_statement : 'unchecked' block ; -// Source: §12.13 The lock statement +// Source: §13.13 The lock statement lock_statement : 'lock' '(' expression ')' embedded_statement ; -// Source: §12.14 The using statement +// Source: §13.14 The using statement using_statement : 'using' '(' resource_acquisition ')' embedded_statement ; @@ -1666,19 +1810,19 @@ resource_acquisition | expression ; -// Source: §12.15 The yield statement +// Source: §13.15 The yield statement yield_statement : 'yield' 'return' expression ';' | 'yield' 'break' ';' ; -// Source: §13.2 Compilation units +// Source: §14.2 Compilation units compilation_unit : extern_alias_directive* using_directive* global_attributes? namespace_member_declaration* ; -// Source: §13.3 Namespace declarations +// Source: §14.3 Namespace declarations namespace_declaration : 'namespace' qualified_identifier namespace_body ';'? ; @@ -1692,40 +1836,40 @@ namespace_body namespace_member_declaration* '}' ; -// Source: §13.4 Extern alias directives +// Source: §14.4 Extern alias directives extern_alias_directive : 'extern' 'alias' identifier ';' ; -// Source: §13.5.1 General +// Source: §14.5.1 General using_directive : using_alias_directive | using_namespace_directive | using_static_directive ; -// Source: §13.5.2 Using alias directives +// Source: §14.5.2 Using alias directives using_alias_directive : 'using' identifier '=' namespace_or_type_name ';' ; -// Source: §13.5.3 Using namespace directives +// Source: §14.5.3 Using namespace directives using_namespace_directive : 'using' namespace_name ';' ; -// Source: §13.5.4 Using static directives +// Source: §14.5.4 Using static directives using_static_directive : 'using' 'static' type_name ';' ; -// Source: §13.6 Namespace member declarations +// Source: §14.6 Namespace member declarations namespace_member_declaration : namespace_declaration | type_declaration ; -// Source: §13.7 Type declarations +// Source: §14.7 Type declarations type_declaration : class_declaration | struct_declaration @@ -1734,19 +1878,19 @@ type_declaration | delegate_declaration ; -// Source: §13.8.1 General +// Source: §14.8.1 General qualified_alias_member : identifier '::' identifier type_argument_list? ; -// Source: §14.2.1 General +// Source: §15.2.1 General class_declaration : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list? class_base? type_parameter_constraints_clause* class_body ';'? ; -// Source: §14.2.2.1 General +// Source: §15.2.2.1 General class_modifier : 'new' | 'public' @@ -1759,7 +1903,7 @@ class_modifier | unsafe_modifier // unsafe code support ; -// Source: §14.2.3 Type parameters +// Source: §15.2.3 Type parameters type_parameter_list : '<' type_parameters '>' ; @@ -1769,7 +1913,7 @@ type_parameters | type_parameters ',' attributes? type_parameter ; -// Source: §14.2.4.1 General +// Source: §15.2.4.1 General class_base : ':' class_type | ':' interface_type_list @@ -1780,7 +1924,7 @@ interface_type_list : interface_type (',' interface_type)* ; -// Source: §14.2.5 Type parameter constraints +// Source: §15.2.5 Type parameter constraints type_parameter_constraints_clauses : type_parameter_constraints_clause | type_parameter_constraints_clauses type_parameter_constraints_clause @@ -1804,6 +1948,7 @@ primary_constraint : class_type | 'class' | 'struct' + | 'unmanaged' ; secondary_constraints @@ -1817,12 +1962,12 @@ constructor_constraint : 'new' '(' ')' ; -// Source: §14.2.6 Class body +// Source: §15.2.6 Class body class_body : '{' class_member_declaration* '}' ; -// Source: §14.3.1 General +// Source: §15.3.1 General class_member_declaration : constant_declaration | field_declaration @@ -1837,7 +1982,7 @@ class_member_declaration | type_declaration ; -// Source: §14.4 Constants +// Source: §15.4 Constants constant_declaration : attributes? constant_modifier* 'const' type constant_declarators ';' ; @@ -1850,7 +1995,7 @@ constant_modifier | 'private' ; -// Source: §14.5.1 General +// Source: §15.5.1 General field_declaration : attributes? field_modifier* type variable_declarators ';' ; @@ -1875,18 +2020,38 @@ variable_declarator : identifier ('=' variable_initializer)? ; -// Source: §14.6.1 General +// Source: §15.6.1 General method_declaration - : method_header method_body + : attributes? method_modifiers return_type method_header method_body + | attributes? ref_method_modifiers ref_kind ref_return_type method_header + ref_method_body + ; + +method_modifiers + : method_modifier* 'partial'? + ; + +ref_kind + : 'ref' + | 'ref' 'readonly' + ; + +ref_method_modifiers + : ref_method_modifier* ; method_header - : attributes? method_modifier* 'partial'? return_type member_name - type_parameter_list? '(' formal_parameter_list? ')' + : member_name '(' formal_parameter_list? ')' + | member_name type_parameter_list '(' formal_parameter_list? ')' type_parameter_constraints_clause* ; method_modifier + : ref_method_modifier + | 'async' + ; + +ref_method_modifier : 'new' | 'public' | 'protected' @@ -1898,15 +2063,18 @@ method_modifier | 'override' | 'abstract' | 'extern' - | 'async' | unsafe_modifier // unsafe code support ; return_type - : type + : ref_return_type | 'void' ; +ref_return_type + : type + ; + member_name : identifier | interface_type '.' identifier @@ -1919,7 +2087,13 @@ method_body | ';' ; -// Source: §14.6.2.1 General +ref_method_body + : block + | '=>' 'ref' variable_reference ';' + | ';' + ; + +// Source: §15.6.2.1 General formal_parameter_list : fixed_parameters | fixed_parameters ',' parameter_array @@ -1946,15 +2120,17 @@ parameter_modifier parameter_mode_modifier : 'ref' | 'out' + | 'in' ; parameter_array : attributes? 'params' array_type identifier ; -// Source: §14.7.1 General +// Source: §15.7.1 General property_declaration : attributes? property_modifier* type member_name property_body + | attributes? property_modifier* ref_kind type member_name ref_property_body ; property_modifier @@ -1981,7 +2157,12 @@ property_initializer : '=' variable_initializer ';' ; -// Source: §14.7.3 Accessors +ref_property_body + : '{' ref_get_accessor_declaration '}' + | '=>' 'ref' variable_reference ';' + ; + +// Source: §15.7.3 Accessors accessor_declarations : get_accessor_declaration set_accessor_declaration? | set_accessor_declaration get_accessor_declaration? @@ -2011,7 +2192,17 @@ accessor_body | ';' ; -// Source: §14.8.1 General +ref_get_accessor_declaration + : attributes? accessor_modifier? 'get' ref_accessor_body + ; + +ref_accessor_body + : block + | '=>' 'ref' variable_reference ';' + | ';' + ; + +// Source: §15.8.1 General event_declaration : attributes? event_modifier* 'event' type variable_declarators ';' | attributes? event_modifier* 'event' type member_name @@ -2046,9 +2237,10 @@ remove_accessor_declaration : attributes? 'remove' block ; -// Source: §14.9 Indexers +// Source: §15.9.1 General indexer_declaration : attributes? indexer_modifier* indexer_declarator indexer_body + | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body ; indexer_modifier @@ -2075,7 +2267,12 @@ indexer_body | '=>' expression ';' ; -// Source: §14.10.1 General +ref_indexer_body + : '{' ref_get_accessor_declaration '}' + | '=>' 'ref' variable_reference ';' + ; + +// Source: §15.10.1 General operator_declaration : attributes? operator_modifier+ operator_declarator operator_body ; @@ -2122,7 +2319,7 @@ operator_body | ';' ; -// Source: §14.11.1 General +// Source: §15.11.1 General constructor_declaration : attributes? constructor_modifier* constructor_declarator constructor_body ; @@ -2151,7 +2348,7 @@ constructor_body | ';' ; -// Source: §14.12 Static constructors +// Source: §15.12 Static constructors static_constructor_declaration : attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body @@ -2173,7 +2370,7 @@ static_constructor_body | ';' ; -// Source: §14.13 Finalizers +// Source: §15.13 Finalizers finalizer_declaration : attributes? '~' identifier '(' ')' finalizer_body | attributes? 'extern' unsafe_modifier? '~' identifier '(' ')' @@ -2188,14 +2385,14 @@ finalizer_body | ';' ; -// Source: §15.2.1 General +// Source: §16.2.1 General struct_declaration - : attributes? struct_modifier* 'partial'? 'struct' + : attributes? struct_modifier* 'ref'? 'partial'? 'struct' identifier type_parameter_list? struct_interfaces? type_parameter_constraints_clause* struct_body ';'? ; -// Source: §15.2.2 Struct modifiers +// Source: §16.2.2 Struct modifiers struct_modifier : 'new' | 'public' @@ -2206,17 +2403,17 @@ struct_modifier | unsafe_modifier // unsafe code support ; -// Source: §15.2.4 Struct interfaces +// Source: §16.2.5 Struct interfaces struct_interfaces : ':' interface_type_list ; -// Source: §15.2.5 Struct body +// Source: §16.2.6 Struct body struct_body : '{' struct_member_declaration* '}' ; -// Source: §15.3 Struct members +// Source: §16.3 Struct members struct_member_declaration : constant_declaration | field_declaration @@ -2231,7 +2428,7 @@ struct_member_declaration | fixed_size_buffer_declaration // unsafe code support ; -// Source: §16.7 Array initializers +// Source: §17.7 Array initializers array_initializer : '{' variable_initializer_list? '}' | '{' variable_initializer_list ',' '}' @@ -2246,14 +2443,14 @@ variable_initializer | array_initializer ; -// Source: §17.2.1 General +// Source: §18.2.1 General interface_declaration : attributes? interface_modifier* 'partial'? 'interface' identifier variant_type_parameter_list? interface_base? type_parameter_constraints_clause* interface_body ';'? ; -// Source: §17.2.2 Interface modifiers +// Source: §18.2.2 Interface modifiers interface_modifier : 'new' | 'public' @@ -2263,35 +2460,35 @@ interface_modifier | unsafe_modifier // unsafe code support ; -// Source: §17.2.3.1 General +// Source: §18.2.3.1 General variant_type_parameter_list : '<' variant_type_parameters '>' ; -// Source: §17.2.3.1 General +// Source: §18.2.3.1 General variant_type_parameters : attributes? variance_annotation? type_parameter | variant_type_parameters ',' attributes? variance_annotation? type_parameter ; -// Source: §17.2.3.1 General +// Source: §18.2.3.1 General variance_annotation : 'in' | 'out' ; -// Source: §17.2.4 Base interfaces +// Source: §18.2.4 Base interfaces interface_base : ':' interface_type_list ; -// Source: §17.3 Interface body +// Source: §18.3 Interface body interface_body : '{' interface_member_declaration* '}' ; -// Source: §17.4.1 General +// Source: §18.4.1 General interface_member_declaration : interface_method_declaration | interface_property_declaration @@ -2299,18 +2496,24 @@ interface_member_declaration | interface_indexer_declaration ; -// Source: §17.4.2 Interface methods +// Source: §18.4.2 Interface methods interface_method_declaration - : attributes? 'new'? return_type identifier type_parameter_list? - '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';' + : attributes? 'new'? return_type interface_method_header + | attributes? 'new'? ref_kind ref_return_type interface_method_header + ; + +interface_method_header + : identifier '(' formal_parameter_list? ')' ';' + | identifier type_parameter_list '(' formal_parameter_list? ')' + type_parameter_constraints_clause* ';' ; -// Source: §17.4.3 Interface properties +// Source: §18.4.3 Interface properties interface_property_declaration : attributes? 'new'? type identifier '{' interface_accessors '}' + | attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}' ; -// Source: §17.4.3 Interface properties interface_accessors : attributes? 'get' ';' | attributes? 'set' ';' @@ -2318,18 +2521,24 @@ interface_accessors | attributes? 'set' ';' attributes? 'get' ';' ; -// Source: §17.4.4 Interface events +ref_interface_accessor + : attributes? 'get' ';' + ; + +// Source: §18.4.4 Interface events interface_event_declaration : attributes? 'new'? 'event' type identifier ';' ; -// Source: §17.4.5 Interface indexers -interface_indexer_declaration: - attributes? 'new'? type 'this' '[' formal_parameter_list ']' - '{' interface_accessors '}' +// Source: §18.4.5 Interface indexers +interface_indexer_declaration + : attributes? 'new'? type 'this' '[' formal_parameter_list ']' + '{' interface_accessors '}' + | attributes? 'new'? ref_kind type 'this' '[' formal_parameter_list ']' + '{' ref_interface_accessor '}' ; -// Source: §18.2 Enum declarations +// Source: §19.2 Enum declarations enum_declaration : attributes? enum_modifier* 'enum' identifier enum_base? enum_body ';'? ; @@ -2348,7 +2557,7 @@ enum_body | '{' enum_member_declarations ',' '}' ; -// Source: §18.3 Enum modifiers +// Source: §19.3 Enum modifiers enum_modifier : 'new' | 'public' @@ -2357,20 +2566,26 @@ enum_modifier | 'private' ; -// Source: §18.4 Enum members +// Source: §19.4 Enum members enum_member_declarations : enum_member_declaration (',' enum_member_declaration)* ; -// Source: §18.4 Enum members +// Source: §19.4 Enum members enum_member_declaration : attributes? identifier ('=' constant_expression)? ; -// Source: §19.2 Delegate declarations +// Source: §20.2 Delegate declarations delegate_declaration - : attributes? delegate_modifier* 'delegate' return_type identifier - variant_type_parameter_list? '(' formal_parameter_list? ')' + : attributes? delegate_modifier* 'delegate' return_type delegate_header + | attributes? delegate_modifier* 'delegate' ref_kind ref_return_type + delegate_header + ; + +delegate_header + : identifier '(' formal_parameter_list? ')' ';' + | identifier variant_type_parameter_list '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';' ; @@ -2383,7 +2598,7 @@ delegate_modifier | unsafe_modifier // unsafe code support ; -// Source: §21.3 Attribute specification +// Source: §22.3 Attribute specification global_attributes : global_attribute_section+ ; @@ -2462,7 +2677,7 @@ attribute_argument_expression ```ANTLR -// Source: §22.2 Unsafe contexts +// Source: §23.2 Unsafe contexts unsafe_modifier : 'unsafe' ; @@ -2471,33 +2686,33 @@ unsafe_statement : 'unsafe' block ; -// Source: §22.3 Pointer types +// Source: §23.3 Pointer types pointer_type : value_type ('*')+ | 'void' ('*')+ ; -// Source: §22.6.2 Pointer indirection +// Source: §23.6.2 Pointer indirection pointer_indirection_expression : '*' unary_expression ; -// Source: §22.6.3 Pointer member access +// Source: §23.6.3 Pointer member access pointer_member_access : primary_expression '->' identifier type_argument_list? ; -// Source: §22.6.4 Pointer element access +// Source: §23.6.4 Pointer element access pointer_element_access : primary_no_array_creation_expression '[' expression ']' ; -// Source: §22.6.5 The address-of operator +// Source: §23.6.5 The address-of operator addressof_expression : '&' unary_expression ; -// Source: §22.7 The fixed statement +// Source: §23.7 The fixed statement fixed_statement : 'fixed' '(' pointer_type fixed_pointer_declarators ')' embedded_statement ; @@ -2515,7 +2730,7 @@ fixed_pointer_initializer | expression ; -// Source: §22.8.2 Fixed-size buffer declarations +// Source: §23.8.2 Fixed-size buffer declarations fixed_size_buffer_declaration : attributes? fixed_size_buffer_modifier* 'fixed' buffer_element_type fixed_size_buffer_declarators ';' @@ -2540,11 +2755,6 @@ fixed_size_buffer_declarators fixed_size_buffer_declarator : identifier '[' constant_expression ']' ; - -// Source: §22.9 Stack allocation -stackalloc_initializer - : 'stackalloc' unmanaged_type '[' expression ']' - ; ``` **End of informative text.** diff --git a/standard/interfaces.md b/standard/interfaces.md index fd40b7961..9a9bf1c70 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -1,16 +1,16 @@ -# 17 Interfaces +# 18 Interfaces -## 17.1 General +## 18.1 General An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. Interfaces can contain methods, properties, events, and indexers. The interface itself does not provide implementations for the members that it declares. The interface merely specifies the members that shall be supplied by classes or structs that implement the interface. -## 17.2 Interface declarations +## 18.2 Interface declarations -### 17.2.1 General +### 18.2.1 General -An *interface_declaration* is a *type_declaration* ([§13.7](namespaces.md#137-type-declarations)) that declares a new interface type. +An *interface_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-type-declarations)) that declares a new interface type. ```ANTLR interface_declaration @@ -20,13 +20,13 @@ interface_declaration ; ``` -An *interface_declaration* consists of an optional set of *attributes* ([§21](attributes.md#21-attributes)), followed by an optional set of *interface_modifier*s ([§17.2.2](interfaces.md#1722-interface-modifiers)), followed by an optional partial modifier ([§14.2.7](classes.md#1427-partial-declarations)), followed by the keyword `interface` and an *identifier* that names the interface, followed by an optional *variant_type_parameter_list* specification ([§17.2.3](interfaces.md#1723-variant-type-parameter-lists)), followed by an optional *interface_base* specification ([§17.2.4](interfaces.md#1724-base-interfaces)), followed by an optional *type_parameter_constraints_clause*s specification ([§14.2.5](classes.md#1425-type-parameter-constraints)), followed by an *interface_body* ([§17.3](interfaces.md#173-interface-body)), optionally followed by a semicolon. +An *interface_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *interface_modifier*s ([§18.2.2](interfaces.md#1822-interface-modifiers)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword `interface` and an *identifier* that names the interface, followed by an optional *variant_type_parameter_list* specification ([§18.2.3](interfaces.md#1823-variant-type-parameter-lists)), followed by an optional *interface_base* specification ([§18.2.4](interfaces.md#1824-base-interfaces)), followed by an optional *type_parameter_constraints_clause*s specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by an *interface_body* ([§18.3](interfaces.md#183-interface-body)), optionally followed by a semicolon. An interface declaration shall not supply a *type_parameter_constraints_clause*s unless it also supplies a *type_parameter_list*. An interface declaration that supplies a *type_parameter_list* is a generic interface declaration. Additionally, any interface nested inside a generic class declaration or a generic struct declaration is itself a generic interface declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)). -### 17.2.2 Interface modifiers +### 18.2.2 Interface modifiers An *interface_declaration* may optionally include a sequence of interface modifiers: @@ -41,17 +41,17 @@ interface_modifier ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). It is a compile-time error for the same modifier to appear multiple times in an interface declaration. -The `new` modifier is only permitted on interfaces defined within a class. It specifies that the interface hides an inherited member by the same name, as described in [§14.3.5](classes.md#1435-the-new-modifier). +The `new` modifier is only permitted on interfaces defined within a class. It specifies that the interface hides an inherited member by the same name, as described in [§15.3.5](classes.md#1535-the-new-modifier). -The `public`, `protected`, `internal`, and `private` modifiers control the accessibility of the interface. Depending on the context in which the interface declaration occurs, only some of these modifiers might be permitted ([§7.5.2](basic-concepts.md#752-declared-accessibility)). When a partial type declaration ([§14.2.7](classes.md#1427-partial-declarations)) includes an accessibility specification (via the `public`, `protected`, `internal`, and `private` modifiers), the rules in [§14.2.2](classes.md#1422-class-modifiers) apply. +The `public`, `protected`, `internal`, and `private` modifiers control the accessibility of the interface. Depending on the context in which the interface declaration occurs, only some of these modifiers might be permitted ([§7.5.2](basic-concepts.md#752-declared-accessibility)). When a partial type declaration ([§15.2.7](classes.md#1527-partial-declarations)) includes an accessibility specification (via the `public`, `protected`, `internal`, and `private` modifiers), the rules in [§15.2.2](classes.md#1522-class-modifiers) apply. -### 17.2.3 Variant type parameter lists +### 18.2.3 Variant type parameter lists -#### 17.2.3.1 General +#### 18.2.3.1 General Variant type parameter lists can only occur on interface and delegate types. The difference from ordinary *type_parameter_list*s is the optional *variance_annotation* on each type parameter. @@ -93,9 +93,9 @@ If the variance annotation is `out`, the type parameter is said to be ***covaria > > *end example* -If a generic interface is declared in multiple parts ([§14.2.3](classes.md#1423-type-parameters)), each partial declaration shall specify the same variance for each type parameter. +If a generic interface is declared in multiple parts ([§15.2.3](classes.md#1523-type-parameters)), each partial declaration shall specify the same variance for each type parameter. -#### 17.2.3.2 Variance safety +#### 18.2.3.2 Variance safety The occurrence of variance annotations in the type parameter list of a type restricts the places where types can occur within the type declaration. @@ -119,7 +119,7 @@ Intuitively, an output-unsafe type is prohibited in an output position, and an i A type is ***output-safe*** if it is not output-unsafe, and ***input-safe*** if it is not input-unsafe. -#### 17.2.3.3 Variance conversion +#### 18.2.3.3 Variance conversion The purpose of variance annotations is to provide for more lenient (but still type safe) conversions to interface and delegate types. To this end the definitions of implicit ([§10.2](conversions.md#102-implicit-conversions)) and explicit conversions ([§10.3](conversions.md#103-explicit-conversions)) make use of the notion of variance-convertibility, which is defined as follows: @@ -129,7 +129,7 @@ A type `T` is variance-convertible to a type `T *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§17.4](interfaces.md#174-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§11.5](expressions.md#115-member-lookup)). *end note* +> *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§18.4](interfaces.md#184-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* -The set of members of an interface declared in multiple parts ([§14.2.7](classes.md#1427-partial-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. +The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. -### 17.4.2 Interface methods +### 18.4.2 Interface methods Interface methods are declared using *interface_method_declaration*s: ```ANTLR interface_method_declaration - : attributes? 'new'? return_type identifier type_parameter_list? - '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';' + : attributes? 'new'? return_type interface_method_header + | attributes? 'new'? ref_kind ref_return_type interface_method_header + ; + +interface_method_header + : identifier '(' formal_parameter_list? ')' ';' + | identifier type_parameter_list '(' formal_parameter_list? ')' + type_parameter_constraints_clause* ';' ; ``` -The *attributes*, *return_type*, *identifier*, and *formal_parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§14.6](classes.md#146-methods)). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon. An *interface_method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. +The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *formal_parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§15.6](classes.md#156-methods)). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon. -All formal parameter types of an interface method shall be input-safe ([§17.2.3.2](interfaces.md#17232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference formal parameter types shall also be output-safe. +All formal parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference formal parameter types shall also be output-safe. -> *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* +> *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* Furthermore, each class type constraint, interface type constraint and type parameter constraint on any type parameters of the method shall be input-safe. @@ -306,32 +312,35 @@ These rules ensure that any covariant or contravariant usage of the interface re > > *end example* -### 17.4.3 Interface properties +### 18.4.3 Interface properties Interface properties are declared using *interface_property_declaration*s: ```ANTLR interface_property_declaration : attributes? 'new'? type identifier '{' interface_accessors '}' + | attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}' ; -``` -```ANTLR interface_accessors : attributes? 'get' ';' | attributes? 'set' ';' | attributes? 'get' ';' attributes? 'set' ';' | attributes? 'set' ';' attributes? 'get' ';' ; + +ref_interface_accessor + : attributes? 'get' ';' + ; ``` -The *attributes*, *type*, and *identifier* of an interface property declaration have the same meaning as those of a property declaration in a class ([§14.7](classes.md#147-properties)). +The *attributes*, *type*, and *identifier* of an interface property declaration have the same meaning as those of a property declaration in a class ([§15.7](classes.md#157-properties)). -The accessors of an interface property declaration correspond to the accessors of a class property declaration ([§14.7.3](classes.md#1473-accessors)), except that the accessor body shall always be a semicolon. Thus, the accessors simply indicate whether the property is read-write, read-only, or write-only. +The accessors of an interface property declaration correspond to the accessors of a class property declaration ([§15.7.3](classes.md#1573-accessors)), except that the *accessor_body* shall always be a semicolon. Thus, the accessors simply indicate whether the property is read-write, read-only, or write-only. The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. -### 17.4.4 Interface events +### 18.4.4 Interface events Interface events are declared using *interface_event_declaration*s: @@ -341,36 +350,38 @@ interface_event_declaration ; ``` -The *attributes*, *type*, and *identifier* of an interface event declaration have the same meaning as those of an event declaration in a class ([§14.8](classes.md#148-events)). +The *attributes*, *type*, and *identifier* of an interface event declaration have the same meaning as those of an event declaration in a class ([§15.8](classes.md#158-events)). The type of an interface event shall be input-safe. -### 17.4.5 Interface indexers +### 18.4.5 Interface indexers Interface indexers are declared using *interface_indexer_declaration*s: ```ANTLR -interface_indexer_declaration: - attributes? 'new'? type 'this' '[' formal_parameter_list ']' - '{' interface_accessors '}' +interface_indexer_declaration + : attributes? 'new'? type 'this' '[' formal_parameter_list ']' + '{' interface_accessors '}' + | attributes? 'new'? ref_kind type 'this' '[' formal_parameter_list ']' + '{' ref_interface_accessor '}' ; ``` -The *attributes*, *type*, and *formal_parameter_list* of an interface indexer declaration have the same meaning as those of an indexer declaration in a class ([§14.9](classes.md#149-indexers)). +The *attributes*, *type*, and *formal_parameter_list* of an interface indexer declaration have the same meaning as those of an indexer declaration in a class ([§15.9](classes.md#159-indexers)). -The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration ([§14.9](classes.md#149-indexers)), except that the accessor body shall always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only. +The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration ([§15.9](classes.md#159-indexers)), except that the *accessor_body* shall always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only. -All the formal parameter types of an interface indexer shall be input-safe ([§17.2.3.2](interfaces.md#17232-variance-safety)). In addition, any output or reference formal parameter types shall also be output-safe. +All the formal parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). In addition, any output or reference formal parameter types shall also be output-safe. > *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* The type of an interface indexer shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. -### 17.4.6 Interface member access +### 18.4.6 Interface member access -Interface members are accessed through member access ([§11.7.6](expressions.md#1176-member-access)) and indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a method, property, or event of that interface type, and `A` is an indexer argument list. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.11.3](expressions.md#128113-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a method, property, or event of that interface type, and `A` is an indexer argument list. -For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§11.5](expressions.md#115-member-lookup)), method invocation ([§11.7.8.2](expressions.md#11782-method-invocations)), and indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. +For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.9.2](expressions.md#12892-method-invocations)), and indexer access ([§12.8.11.3](expressions.md#128113-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. > *Example*: In the following code > @@ -400,7 +411,7 @@ For interfaces that are strictly single-inheritance (each interface in the inher > } > ``` > -> the first two statements cause compile-time errors because the member lookup ([§11.5](expressions.md#115-member-lookup)) of `Count` in `IListCounter` is ambiguous. As illustrated by the example, the ambiguity is resolved by casting `x` to the appropriate base interface type. Such casts have no run-time costs—they merely consist of viewing the instance as a less derived type at compile-time. +> the first two statements cause compile-time errors because the member lookup ([§12.5](expressions.md#125-member-lookup)) of `Count` in `IListCounter` is ambiguous. As illustrated by the example, the ambiguity is resolved by casting `x` to the appropriate base interface type. Such casts have no run-time costs—they merely consist of viewing the instance as a less derived type at compile-time. > > *end example* @@ -434,7 +445,7 @@ For interfaces that are strictly single-inheritance (each interface in the inher > } > ``` > -> the invocation `n.Add(1)` selects `IInteger.Add` by applying overload resolution rules of [§11.6.4](expressions.md#1164-overload-resolution). Similarly, the invocation `n.Add(1.0)` selects `IDouble.Add`. When explicit casts are inserted, there is only one candidate method, and thus no ambiguity. +> the invocation `n.Add(1)` selects `IInteger.Add` by applying overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution). Similarly, the invocation `n.Add(1.0)` selects `IDouble.Add`. When explicit casts are inserted, there is only one candidate method, and thus no ambiguity. > > *end example* @@ -479,7 +490,7 @@ For interfaces that are strictly single-inheritance (each interface in the inher > > *end example* -## 17.5 Qualified interface member names +## 18.5 Qualified interface member names An interface member is sometimes referred to by its ***qualified interface member name***. The qualified name of an interface member consists of the name of the interface in which the member is declared, followed by a dot, followed by the name of the member. The qualified name of a member references the interface in which the member is declared. @@ -521,9 +532,9 @@ When an interface is part of a namespace, a qualified interface member name can > > *end example* -## 17.6 Interface implementations +## 18.6 Interface implementations -### 17.6.1 General +### 18.6.1 General Interfaces may be implemented by classes and structs. To indicate that a class or struct directly implements an interface, the interface is included in the base class list of the class or struct. @@ -579,7 +590,7 @@ A class or struct that directly implements an interface also implicitly implemen When a class `C` directly implements an interface, all classes derived from `C` also implement the interface implicitly. -The base interfaces specified in a class declaration can be constructed interface types ([§8.4](types.md#84-constructed-types), [§17.2](interfaces.md#172-interface-declarations)). +The base interfaces specified in a class declaration can be constructed interface types ([§8.4](types.md#84-constructed-types), [§18.2](interfaces.md#182-interface-declarations)). > *Example*: The following code illustrates how a class can implement constructed interface types: > @@ -593,9 +604,9 @@ The base interfaces specified in a class declaration can be constructed interfac > > *end example* -The base interfaces of a generic class declaration shall satisfy the uniqueness rule described in [§17.6.3](interfaces.md#1763-uniqueness-of-implemented-interfaces). +The base interfaces of a generic class declaration shall satisfy the uniqueness rule described in [§18.6.3](interfaces.md#1863-uniqueness-of-implemented-interfaces). -### 17.6.2 Explicit interface member implementations +### 18.6.2 Explicit interface member implementations For purposes of implementing interfaces, a class or struct may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. @@ -653,7 +664,7 @@ For purposes of implementing interfaces, a class or struct may declare ***explic It is not possible to access an explicit interface member implementation through its qualified interface member name in a method invocation, property access, event access, or indexer access. An explicit interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name. -It is a compile-time error for an explicit interface member implementation to include any modifiers ([§14.6](classes.md#146-methods)) other than `extern` or `async`. +It is a compile-time error for an explicit interface member implementation to include any modifiers ([§15.6](classes.md#156-methods)) other than `extern` or `async`. It is a compile-time error for an explicit interface method implementation to include *type_parameter_constraints_clause*s. The constraints for a generic explicit interface method implementation are inherited from the interface method. @@ -724,7 +735,7 @@ The qualified interface member name of an explicit interface member implementati > > *end example* -### 17.6.3 Uniqueness of implemented interfaces +### 18.6.3 Uniqueness of implemented interfaces The interfaces implemented by a generic type declaration shall remain unique for all possible constructed types. Without this rule, it would be impossible to determine the correct method to call for certain constructed types. @@ -789,9 +800,9 @@ I x = new Derived(); x.F(); ``` -invokes the method in `Derived`, since `Derived'` effectively re-implements `I` ([§17.6.7](interfaces.md#1767-interface-re-implementation)). +invokes the method in `Derived`, since `Derived'` effectively re-implements `I` ([§18.6.7](interfaces.md#1867-interface-re-implementation)). -### 17.6.4 Implementation of generic methods +### 18.6.4 Implementation of generic methods When a generic method implicitly implements an interface method, the constraints given for each method type parameter shall be equivalent in both declarations (after any interface type parameters are replaced with the appropriate type arguments), where method type parameters are identified by ordinal positions, left to right. @@ -837,9 +848,9 @@ When a generic method implicitly implements an interface method, the constraints -> *Note*: When a generic method explicitly implements an interface method no constraints are allowed on the implementing method ([§14.7.1](classes.md#1471-general), [§17.6.2](interfaces.md#1762-explicit-interface-member-implementations)). *end note* +> *Note*: When a generic method explicitly implements an interface method no constraints are allowed on the implementing method ([§15.7.1](classes.md#1571-general), [§18.6.2](interfaces.md#1862-explicit-interface-member-implementations)). *end note* -### 17.6.5 Interface mapping +### 18.6.5 Interface mapping A class or struct shall provide implementations of all members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as ***interface mapping***. @@ -850,7 +861,7 @@ Interface mapping for a class or struct `C` locates an implementation for each m A compile-time error occurs if implementations cannot be located for all members of all interfaces specified in the base class list of `C`. The members of an interface include those members that are inherited from base interfaces. -Members of a constructed interface type are considered to have any type parameters replaced with the corresponding type arguments as specified in [§14.3.3](classes.md#1433-members-of-constructed-types). +Members of a constructed interface type are considered to have any type parameters replaced with the corresponding type arguments as specified in [§15.3.3](classes.md#1533-members-of-constructed-types). > *Example*: For example, given the generic interface declaration: > @@ -1031,7 +1042,7 @@ The members of a base class participate in interface mapping. > > *end example* -### 17.6.6 Interface implementation inheritance +### 18.6.6 Interface implementation inheritance A class inherits all interface implementations provided by its base classes. @@ -1140,7 +1151,7 @@ Since explicit interface member implementations cannot be declared virtual, it i > > *end example* -### 17.6.7 Interface re-implementation +### 18.6.7 Interface re-implementation A class that inherits an interface implementation is permitted to ***re-implement*** the interface by including it in the base class list. @@ -1236,7 +1247,7 @@ When a class implements an interface, it implicitly also implements all that int > > *end example* -### 17.6.8 Abstract classes and interfaces +### 18.6.8 Abstract classes and interfaces Like a non-abstract class, an abstract class shall provide implementations of all members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods. diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index 4c1215a4f..6f718ff1f 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -2,7 +2,7 @@ ## 6.1 Programs -A C# ***program*** consists of one or more source files, known formally as ***compilation units*** ([§13.2](namespaces.md#132-compilation-units)). Although a compilation unit might have a one-to-one correspondence with a file in a file system, such correspondence is not required. +A C# ***program*** consists of one or more source files, known formally as ***compilation units*** ([§14.2](namespaces.md#142-compilation-units)). Although a compilation unit might have a one-to-one correspondence with a file in a file system, such correspondence is not required. Conceptually speaking, a program is compiled using three steps: @@ -16,7 +16,7 @@ Conforming implementations shall accept Unicode compilation units encoded with t -> *Note*: It is beyond the scope of this standard to define how a file using a character representation other than Unicode might be transformed into a sequence of Unicode characters. During such transformation, however, it is recommended that the usual line-separating character (or sequence) in the other character set be translated to the two-character sequence consisting of the Unicode carriage-return character (U+000D) followed by Unicode line-feed character (U+000A). For the most part this transformation will have no visible effects; however, it will affect the interpretation of verbatim string literal tokens ([§6.4.5.6](lexical-structure.md#6456-string-literals)). The purpose of this recommendation is to allow a verbatim string literal to produce the same character sequence when its compilation unit is moved between systems that support differing non-Unicode character sets, in particular, those using differing character sequences for line-separation. *end note* +> *Note*: It is beyond the scope of this specification to define how a file using a character representation other than Unicode might be transformed into a sequence of Unicode characters. During such transformation, however, it is recommended that the usual line-separating character (or sequence) in the other character set be translated to the two-character sequence consisting of the Unicode carriage-return character (U+000D) followed by Unicode line-feed character (U+000A). For the most part this transformation will have no visible effects; however, it will affect the interpretation of verbatim string literal tokens ([§6.4.5.6](lexical-structure.md#6456-string-literals)). The purpose of this recommendation is to allow a verbatim string literal to produce the same character sequence when its compilation unit is moved between systems that support differing non-Unicode character sets, in particular, those using differing character sequences for line-separation. *end note* ## 6.2 Grammars @@ -30,7 +30,7 @@ All terminal characters are to be understood as the appropriate Unicode characte The lexical and syntactic grammars are presented in the ANTLR grammar tool’s Extended Backus-Naur form. -While the ANTLR notation is used, this Standard does not present a complete ANTLR-ready “reference grammar” for C#; writing a lexer and parser, either by hand or using a tool such as ANTLR, is outside the scope of a language specification. With that qualification, this Standard attempts to minimize the gap between the specified grammar and that required to build a lexer and parser in ANTLR. +While the ANTLR notation is used, this specification does not present a complete ANTLR-ready “reference grammar” for C#; writing a lexer and parser, either by hand or using a tool such as ANTLR, is outside the scope of a language specification. With that qualification, this specification attempts to minimize the gap between the specified grammar and that required to build a lexer and parser in ANTLR. ANTLR distinguishes between lexical and syntactic, termed parser by ANTLR, grammars in its notation by starting lexical rules with an uppercase letter and parser rules with a lowercase letter. @@ -48,11 +48,11 @@ Every compilation unit in a C# program shall conform to the *input* production o The syntactic grammar of C# is presented in the clauses, subclauses, and annexes that follow this subclause. The terminal symbols of the syntactic grammar are the tokens defined explicitly by the lexical grammar and implicitly by literal strings in the grammar itself ([§6.2.3](lexical-structure.md#623-lexical-grammar)). The syntactic grammar specifies how tokens are combined to form C# programs. -Every compilation unit in a C# program shall conform to the *compilation_unit* production ([§13.2](namespaces.md#132-compilation-units)) of the syntactic grammar. +Every compilation unit in a C# program shall conform to the *compilation_unit* production ([§14.2](namespaces.md#142-compilation-units)) of the syntactic grammar. ### 6.2.5 Grammar ambiguities -The productions for *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) and *member_access* ([§11.7.6](expressions.md#1176-member-access)) can give rise to ambiguities in the grammar for expressions. +The productions for *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) and *member_access* ([§12.8.7](expressions.md#1287-member-access)) can give rise to ambiguities in the grammar for expressions. > *Example*: The statement: > @@ -65,13 +65,14 @@ The productions for *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) > > *end example* -If a sequence of tokens can be parsed (in context) as a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)), *member_access* ([§11.7.6](expressions.md#1176-member-access)), or *pointer_member_access* ([§22.6.3](unsafe-code.md#2263-pointer-member-access)) ending with a *type_argument_list* ([§8.4.2](types.md#842-type-arguments)), the token immediately following the closing `>` token is examined. If it is one of +If a sequence of tokens can be parsed (in context) as a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)), *member_access* ([§12.8.7](expressions.md#1287-member-access)), or *pointer_member_access* ([§23.6.3](unsafe-code.md#2363-pointer-member-access)) ending with a *type_argument_list* ([§8.4.2](types.md#842-type-arguments)), the token immediately following the closing `>` token is examined, to see if it is -```csharp -( ) ] : ; , . ? == != -``` +- One of `( ) ] } : ; , . ? == != | ^ && || & [`; or +- One of the relational operators `< > <= >= is as`; or +- A contextual query keyword appearing inside a query expression; or +- In certain contexts, *identifier* is treated as a disambiguating token. Those contexts are where the sequence of tokens being disambiguated is immediately preceded by one of the keywords `is`, `case` or `out`, or arises while parsing the first element of a tuple literal (in which case the tokens are preceded by `(` or `:` and the identifier is followed by a `,`) or a subsequent element of a tuple literal. -then the *type_argument_list* is retained as part of the *simple_name*, *member_access*, or *pointer_member_access* and any other possible parse of the sequence of tokens is discarded. Otherwise, the *type_argument_list* is not considered part of the *simple_name*, *member_access*, or *pointer_member_access*, even if there is no other possible parse of the sequence of tokens. +If the following token is among this list, or an identifier in such a context, then the *type_argument_list* is retained as part of the *simple_name*, *member_access* or *pointer_member-access* and any other possible parse of the sequence of tokens is discarded. Otherwise, the *type_argument_list* is not considered to be part of the *simple_name*, *member_access* or *pointer_member_access*, even if there is no other possible parse of the sequence of tokens. (These rules are not applied when parsing a *type_argument_list* in a *namespace_or_type_name* [§7.8](basic-concepts.md#78-namespace-and-type-names).) > *Note*: These rules are not applied when parsing a *type_argument_list* in a *namespace_or_type_name* ([§7.8](basic-concepts.md#78-namespace-and-type-names)). *end note* @@ -106,10 +107,25 @@ then the *type_argument_list* is retained as part of the *simple_name*, *member_ > x = y is C && z; > ``` > -> the tokens `C` are interpreted as a *namespace_or_type_name* with a *type_argument_list* due to being on the right-hand side of the `is` operator ([§11.11.1](expressions.md#11111-general)). Because `C` parses as a *namespace_or_type_name*, not a *simple_name*, *member_access*, or *pointer_member_access*, the above rule does not apply, and it is considered to have a *type_argument_list* regardless of the token that follows. +> the tokens `C` are interpreted as a *namespace_or_type_name* with a *type_argument_list* due to the presence of +> the disambiguating token `&&` after the *type_argument_list*. +> +> The expression `(A < B, C > D)` is a tuple with two elements, each a comparison. +> +> The expression `(A D, E)` is a tuple with two elements, the first of which is a declaration expression. +> +> The invocation `M(A < B, C > D, E)` has three arguments. +> +> The invocation `M(out A D, E)` has two arguments, the first of which is an `out` declaration. +> +> The expression `e is A C` uses a declaration pattern. +> +> The case label `case A C:` uses a declaration pattern. > > *end example* +A *relational_expression* ([§12.12.1](expressions.md#12121-general)) can have the form “*relational_expression* `is` *type*” or “*relational_expression* `is` *constant_pattern*,” either of which might be a valid parse of a qualified identifier. In this case, an attempt is made to bind it as a type ([§7.8.1](basic-concepts.md#781-general)); however, if that fails, it is bound as an expression, and the result must be a constant. + ## 6.3 Lexical analysis ### 6.3.1 General @@ -335,7 +351,7 @@ token ### 6.4.2 Unicode character escape sequences -A Unicode escape sequence represents a Unicode code point. Unicode escape sequences are processed in identifiers ([§6.4.3](lexical-structure.md#643-identifiers)), character literals ([§6.4.5.5](lexical-structure.md#6455-character-literals)), regular string literals ([§6.4.5.6](lexical-structure.md#6456-string-literals)), and interpolated regular string expressions ([§11.7.3](expressions.md#1173-interpolated-string-expressions)). A Unicode escape sequence is not processed in any other location (for example, to form an operator, punctuator, or keyword). +A Unicode escape sequence represents a Unicode code point. Unicode escape sequences are processed in identifiers ([§6.4.3](lexical-structure.md#643-identifiers)), character literals ([§6.4.5.5](lexical-structure.md#6455-character-literals)), regular string literals ([§6.4.5.6](lexical-structure.md#6456-string-literals)), and interpolated regular string expressions ([§12.8.3](expressions.md#1283-interpolated-string-expressions)). A Unicode escape sequence is not processed in any other location (for example, to form an operator, punctuator, or keyword). ```ANTLR fragment Unicode_Escape_Sequence @@ -476,7 +492,7 @@ fragment Formatting_Character > *Note*: > > - For information on the Unicode character classes mentioned above, see *The Unicode Standard*. -> - The fragment *Available_Identifier* requires the exclusion of keywords and contextual keywords. If the grammar in this Standard is processed with ANTLR then this exclusion is handled automatically by the semantics of ANTLR: +> - The fragment *Available_Identifier* requires the exclusion of keywords and contextual keywords. If the grammar in this specification is processed with ANTLR then this exclusion is handled automatically by the semantics of ANTLR: > - Keywords and contextual keywords occur in the grammar as literal strings. > - ANTLR creates implicit lexical token rules are created from these literal strings. > - ANTLR considers these implicit rules before the explicit lexical rules in the grammar. @@ -536,6 +552,11 @@ Two identifiers are considered the same if they are identical after the followin - Each *Unicode_Escape_Sequence* is transformed into its corresponding Unicode character. - Any *Formatting_Character*s are removed. +The semantics of an identifier named `_` depends on the context in which it appears: + +- It can denote a named program element, such as a variable, class, or method, or +- It can denote a discard ([§9.2.9.1](variables.md#9291-discards)). + Identifiers containing two consecutive underscore characters (`U+005F`) are reserved for use by the implementation; however, no diagnostic is required if such an identifier is defined. > *Note*: For example, an implementation might provide extended keywords that begin with two underscores. *end note* @@ -569,22 +590,22 @@ A ***contextual keyword*** is an identifier-like sequence of characters that has ```ANTLR contextual_keyword - : 'add' | 'alias' | 'ascending' | 'async' | 'await' - | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' - | 'get' | 'global' | 'group' | 'into' | 'join' - | 'let' | 'nameof' | 'on' | 'orderby' | 'partial' - | 'remove' | 'select' | 'set' | 'value' | 'var' - | 'when' | 'where' | 'yield' + : 'add' | 'alias' | 'ascending' | 'async' | 'await' + | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' + | 'get' | 'global' | 'group' | 'into' | 'join' + | 'let' | 'nameof' | 'on' | 'orderby' | 'partial' + | 'remove' | 'select' | 'set' | 'unmanaged' | 'value' + | 'var' | 'when' | 'where' | 'yield' ; ``` > *Note*: The rules *keyword* and *contextual_keyword* are parser rules as they do not introduce new token kinds. All keywords and contextual keywords are defined by implicit lexical rules as they occur as literal strings in the grammar ([§6.2.3](lexical-structure.md#623-lexical-grammar)). *end note* -In most cases, the syntactic location of contextual keywords is such that they can never be confused with ordinary identifier usage. For example, within a property declaration, the `get` and `set` identifiers have special meaning ([§14.7.3](classes.md#1473-accessors)). An identifier other than `get` or `set` is never permitted in these locations, so this use does not conflict with a use of these words as identifiers. +In most cases, the syntactic location of contextual keywords is such that they can never be confused with ordinary identifier usage. For example, within a property declaration, the `get` and `set` identifiers have special meaning ([§15.7.3](classes.md#1573-accessors)). An identifier other than `get` or `set` is never permitted in these locations, so this use does not conflict with a use of these words as identifiers. -In certain cases the grammar is not enough to distinguish contextual keyword usage from identifiers. In all such cases it will be specified how to disambiguate between the two. For example, the contextual keyword `var` in implicitly typed local variable declarations ([§12.6.2](statements.md#1262-local-variable-declarations)) might conflict with a declared type called `var`, in which case the declared name takes precedence over the use of the identifier as a contextual keyword. +In certain cases the grammar is not enough to distinguish contextual keyword usage from identifiers. In all such cases it will be specified how to disambiguate between the two. For example, the contextual keyword `var` in implicitly typed local variable declarations ([§13.6.2](statements.md#1362-local-variable-declarations)) might conflict with a declared type called `var`, in which case the declared name takes precedence over the use of the identifier as a contextual keyword. -Another example such disambiguation is the contextual keyword `await` ([§11.8.8.1](expressions.md#11881-general)), which is considered a keyword only when inside a method declared `async`, but can be used as an identifier elsewhere. +Another example such disambiguation is the contextual keyword `await` ([§12.9.8.1](expressions.md#12981-general)), which is considered a keyword only when inside a method declared `async`, but can be used as an identifier elsewhere. Just as with keywords, contextual keywords can be used as ordinary identifiers by prefixing them with the `@` character. @@ -594,7 +615,7 @@ Just as with keywords, contextual keywords can be used as ordinary identifiers b #### 6.4.5.1 General -A ***literal*** ([§11.7.2](expressions.md#1172-literals)) is a source-code representation of a value. +A ***literal*** ([§12.8.2](expressions.md#1282-literals)) is a source-code representation of a value. ```ANTLR literal @@ -690,8 +711,8 @@ If the value represented by an integer literal is outside the range of the `ulon To permit the smallest possible `int` and `long` values to be written as integer literals, the following two rules exist: -- When an *Integer_Literal* representing the value `2147483648` (2³¹) and no *Integer_Type_Suffix* appears as the token immediately following a unary minus operator token ([§11.8.3](expressions.md#1183-unary-minus-operator)), the result (of both tokens) is a constant of type int with the value `−2147483648` (−2³¹). In all other situations, such an *Integer_Literal* is of type `uint`. -- When an *Integer_Literal* representing the value `9223372036854775808` (2⁶³) and no *Integer_Type_Suffix* or the *Integer_Type_Suffix* `L` or `l` appears as the token immediately following a unary minus operator token ([§11.8.3](expressions.md#1183-unary-minus-operator)), the result (of both tokens) is a constant of type `long` with the value `−9223372036854775808` (−2⁶³). In all other situations, such an *Integer_Literal* is of type `ulong`. +- When an *Integer_Literal* representing the value `2147483648` (2³¹) and no *Integer_Type_Suffix* appears as the token immediately following a unary minus operator token ([§12.9.3](expressions.md#1293-unary-minus-operator)), the result (of both tokens) is a constant of type int with the value `−2147483648` (−2³¹). In all other situations, such an *Integer_Literal* is of type `uint`. +- When an *Integer_Literal* representing the value `9223372036854775808` (2⁶³) and no *Integer_Type_Suffix* or the *Integer_Type_Suffix* `L` or `l` appears as the token immediately following a unary minus operator token ([§12.9.3](expressions.md#1293-unary-minus-operator)), the result (of both tokens) is a constant of type `long` with the value `−9223372036854775808` (−2⁶³). In all other situations, such an *Integer_Literal* is of type `ulong`. > *Example*: > @@ -935,7 +956,7 @@ fragment Quote_Escape_Sequence The type of a *String_Literal* is `string`. -Each string literal does not necessarily result in a new string instance. When two or more string literals that are equivalent according to the string equality operator ([§11.11.8](expressions.md#11118-string-equality-operators)), appear in the same assembly, these string literals refer to the same string instance. +Each string literal does not necessarily result in a new string instance. When two or more string literals that are equivalent according to the string equality operator ([§12.12.8](expressions.md#12128-string-equality-operators)), appear in the same assembly, these string literals refer to the same string instance. > *Example*: For instance, the output produced by > @@ -996,7 +1017,7 @@ right_shift_assignment > *Note*: *right_shift* and *right_shift_assignment* are parser rules as they do not introduce a new token kind but represent a sequence of two tokens. The *operator_or_punctuator* rule exists for descriptive purposes only and is not used elsewhere in the grammar. *end note* -*right_shift* is made up of the two tokens `>` and `>`. Similarly, *right_shift_assignment* is made up of the two tokens `>` and `>=`. Unlike other productions in the syntactic grammar, no characters of any kind (not even whitespace) are allowed between the two tokens in each of these productions. These productions are treated specially in order to enable the correct handling of *type_parameter_lists* ([§14.2.3](classes.md#1423-type-parameters)). +*right_shift* is made up of the two tokens `>` and `>`. Similarly, *right_shift_assignment* is made up of the two tokens `>` and `>=`. Unlike other productions in the syntactic grammar, no characters of any kind (not even whitespace) are allowed between the two tokens in each of these productions. These productions are treated specially in order to enable the correct handling of *type_parameter_lists* ([§15.2.3](classes.md#1523-type-parameters)). > *Note*: Prior to the addition of generics to C#, `>>` and `>>=` were both single tokens. However, the syntax for generics uses the `<` and `>` characters to delimit type parameters and type arguments. It is often desirable to use nested constructed types, such as `List>`. Rather than requiring the programmer to separate the `>` and `>` by a space, the definition of the two *operator_or_punctuator*s was changed. *end note* @@ -1004,7 +1025,7 @@ right_shift_assignment ### 6.5.1 General -The pre-processing directives provide the ability to skip conditionally sections of compilation units, to report error and warning conditions, and to delineate distinct regions of source code. +The pre-processing directives provide the ability to conditionally skip sections of compilation units, to report error and warning conditions, and to delineate distinct regions of source code. > *Note*: The term “pre-processing directives” is used only for consistency with the C and C++ programming languages. In C#, there is no separate pre-processing step; pre-processing directives are processed as part of the lexical analysis phase. *end note* @@ -1161,7 +1182,7 @@ fragment PP_Primary_Expression When referenced in a pre-processing expression, a defined conditional compilation symbol has the Boolean value `true`, and an undefined conditional compilation symbol has the Boolean value `false`. -Evaluation of a pre-processing expression always yields a Boolean value. The rules of evaluation for a pre-processing expression are the same as those for a constant expression ([§11.21](expressions.md#1121-constant-expressions)), except that the only user-defined entities that can be referenced are conditional compilation symbols. +Evaluation of a pre-processing expression always yields a Boolean value. The rules of evaluation for a pre-processing expression are the same as those for a constant expression ([§12.23](expressions.md#1223-constant-expressions)), except that the only user-defined entities that can be referenced are conditional compilation symbols. ### 6.5.4 Definition directives @@ -1276,7 +1297,7 @@ Conditional compilation directives shall be written in groups consisting of, in At most one of the contained conditional sections is selected for normal lexical processing: -- The *PP_Expression*s of the `#if` and `#elif` directives are evaluated in order until one yields `true`. If an expression yields `true`, the conditional section following the corresponding directive is selected. +- The *PP_Expression*s of the `#if` and `#elif` directives are evaluated in order until one yields `true`. If an expression yields `true`, the conditional section following the corresponding directive is selected. - If all *PP_Expression*s yield `false`, and if a `#else` directive is present, the conditional section following the `#else` directive is selected. - Otherwise, no conditional section is selected. @@ -1450,7 +1471,7 @@ corresponds exactly to the lexical processing of a conditional compilation direc ### 6.5.8 Line directives -Line directives may be used to alter the line numbers and compilation unit names that are reported by the compiler in output such as warnings and errors. These values are also used by caller-info attributes ([§21.5.5](attributes.md#2155-caller-info-attributes)). +Line directives may be used to alter the line numbers and compilation unit names that are reported by the compiler in output such as warnings and errors. These values are also used by caller-info attributes ([§22.5.5](attributes.md#2255-caller-info-attributes)). > *Note*: Line directives are most commonly used in meta-programming tools that generate C# source code from some other text input. *end note* @@ -1476,13 +1497,15 @@ fragment PP_Compilation_Unit_Name_Character ; ``` -When no `#line` directives are present, the compiler reports true line numbers and compilation unit names in its output. When processing a `#line` directive that includes a *Line_Indicator* that is not `default`, the compiler treats the line *after* the directive as having the given line number (and compilation unit name, if specified). +When no `#line` directives are present, the compiler reports true line numbers and compilation unit names in its output. When processing a `#line` directive that includes a *PP_Line_Indicator* that is not `default`, the compiler treats the line *after* the directive as having the given line number (and compilation unit name, if specified). + +The maximum value allowed for `Decimal_Digit+` is implementation-defined. A `#line default` directive undoes the effect of all preceding `#line` directives. The compiler reports true line information for subsequent lines, precisely as if no `#line` directives had been processed. -A `#line hidden` directive has no effect on the compilation unit and line numbers reported in error messages, or produced by use of `CallerLineNumberAttribute` ([§21.5.5.2](attributes.md#21552-the-callerlinenumber-attribute)). It is intended to affect source-level debugging tools so that, when debugging, all lines between a `#line` hidden directive and the subsequent `#line` directive (that is not `#line hidden`) have no line number information, and are skipped entirely when stepping through code. +A `#line hidden` directive has no effect on the compilation unit and line numbers reported in error messages, or produced by use of `CallerLineNumberAttribute` ([§22.5.5.2](attributes.md#22552-the-callerlinenumber-attribute)). It is intended to affect source-level debugging tools so that, when debugging, all lines between a `#line hidden` directive and the subsequent `#line` directive (that is not `#line hidden`) have no line number information, and are skipped entirely when stepping through code. -> *Note*: Although a *Compilation_Unit_Name* might contain text that looks like an escape sequence, such text is not an escape sequence; in this context a ‘`\`’ character simply designates an ordinary backslash character. *end note* +> *Note*: Although a *PP_Compilation_Unit_Name* might contain text that looks like an escape sequence, such text is not an escape sequence; in this context a ‘`\`’ character simply designates an ordinary backslash character. *end note* ### 6.5.9 Pragma directives diff --git a/standard/namespaces.md b/standard/namespaces.md index c676ac44c..0ed3676fd 100644 --- a/standard/namespaces.md +++ b/standard/namespaces.md @@ -1,12 +1,12 @@ -# 13 Namespaces +# 14 Namespaces -## 13.1 General +## 14.1 General C# programs are organized using namespaces. Namespaces are used both as an “internal” organization system for a program, and as an “external” organization system—a way of presenting program elements that are exposed to other programs. -Using directives ([§13.5](namespaces.md#135-using-directives)) are provided to facilitate the use of namespaces. +Using directives ([§14.5](namespaces.md#145-using-directives)) are provided to facilitate the use of namespaces. -## 13.2 Compilation units +## 14.2 Compilation units A *compilation_unit* consists of zero or more *extern_alias_directive*s followed by zero or more *using_directive*s followed by zero or one *global_attributes* followed by zero or more *namespace_member_declaration*s. The *compilation_unit* defines the overall structure of the input. @@ -23,7 +23,7 @@ The *extern_alias_directive*s of a compilation unit affect the *using_directive* The *using_directive*s of a compilation unit affect the *global_attributes* and *namespace_member_declaration*s of that compilation unit, but have no effect on other compilation units. -The *global_attributes* ([§21.3](attributes.md#213-attribute-specification)) of a compilation unit permit the specification of attributes for the target assembly and module. Assemblies and modules act as physical containers for types. An assembly may consist of several physically separate modules. +The *global_attributes* ([§22.3](attributes.md#223-attribute-specification)) of a compilation unit permit the specification of attributes for the target assembly and module. Assemblies and modules act as physical containers for types. An assembly may consist of several physically separate modules. The *namespace_member_declaration*s of each compilation unit of a program contribute members to a single declaration space called the global namespace. @@ -32,16 +32,16 @@ The *namespace_member_declaration*s of each compilation unit of a program contri > > ```csharp > // File A.cs: -> class A {} +> class A {} > // File B.cs: -> class B {} +> class B {} > ``` > > The two compilation units contribute to the single global namespace, in this case declaring two classes with the fully qualified names `A` and `B`. Because the two compilation units contribute to the same declaration space, it would have been an error if each contained a declaration of a member with the same name. > > *end example* -## 13.3 Namespace declarations +## 14.3 Namespace declarations A *namespace_declaration* consists of the keyword namespace, followed by a namespace name and body, optionally followed by a semicolon. @@ -116,7 +116,7 @@ Namespaces are open-ended, and two namespace declarations with the same fully qu > > *end example* -## 13.4 Extern alias directives +## 14.4 Extern alias directives An *extern_alias_directive* introduces an identifier that serves as an alias for a namespace. The specification of the aliased namespace is external to the source code of the program and applies also to nested namespaces of the aliased namespace. @@ -130,7 +130,7 @@ The scope of an *extern_alias_directive* extends over the *using_directive*s, *g Within a compilation unit or namespace body that contains an *extern_alias_directive*, the identifier introduced by the *extern_alias_directive* can be used to reference the aliased namespace. It is a compile-time error for the *identifier* to be the word `global`. -The alias introduced by an *extern_alias_directive* is very similar to the alias introduced by a *using_alias_directive*. See [§13.5.2](namespaces.md#1352-using-alias-directives) for more detailed discussion of *extern_alias_directive*s and *using_alias_directive*s. +The alias introduced by an *extern_alias_directive* is very similar to the alias introduced by a *using_alias_directive*. See [§14.5.2](namespaces.md#1452-using-alias-directives) for more detailed discussion of *extern_alias_directive*s and *using_alias_directive*s. `alias` is a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)) and only has special meaning when it immediately follows the `extern` keyword in an *extern_alias_directive*. @@ -138,8 +138,7 @@ An error occurs if a program declares an extern alias for which no external defi > *Example*: The following program declares and uses two extern aliases, `X` and `Y`, each of which represent the root of a distinct namespace hierarchy: > -> -> +> > ```csharp > extern alias X; > extern alias Y; @@ -156,11 +155,11 @@ An error occurs if a program declares an extern alias for which no external defi > The program declares the existence of the extern aliases `X` and `Y`, but the actual definitions of the aliases are external to the program. The identically named `N.B` classes can now be referenced as `X.N.B` and `Y.N.B`, or, using the namespace alias qualifier, `X::N.B` and `Y::N.B`. > *end example* -## 13.5 Using directives +## 14.5 Using directives -### 13.5.1 General +### 14.5.1 General -***Using directives*** facilitate the use of namespaces and types defined in other namespaces. Using directives impact the name resolution process of *namespace_or_type_name*s ([§7.8](basic-concepts.md#78-namespace-and-type-names)) and *simple_name*s ([§11.7.4](expressions.md#1174-simple-names)), but unlike declarations, *using_directive*s do not contribute new members to the underlying declaration spaces of the compilation units or namespaces within which they are used. +***Using directives*** facilitate the use of namespaces and types defined in other namespaces. Using directives impact the name resolution process of *namespace_or_type_name*s ([§7.8](basic-concepts.md#78-namespace-and-type-names)) and *simple_name*s ([§12.8.4](expressions.md#1284-simple-names)), but unlike declarations, *using_directive*s do not contribute new members to the underlying declaration spaces of the compilation units or namespaces within which they are used. ```ANTLR using_directive @@ -170,15 +169,15 @@ using_directive ; ``` -A *using_alias_directive* ([§13.5.2](namespaces.md#1352-using-alias-directives)) introduces an alias for a namespace or type. +A *using_alias_directive* ([§14.5.2](namespaces.md#1452-using-alias-directives)) introduces an alias for a namespace or type. -A *using_namespace_directive* ([§13.5.3](namespaces.md#1353-using-namespace-directives)) imports the type members of a namespace. +A *using_namespace_directive* ([§14.5.3](namespaces.md#1453-using-namespace-directives)) imports the type members of a namespace. -A *using_static_directive* ([§13.5.4](namespaces.md#1354-using-static-directives)) imports the nested types and static members of a type. +A *using_static_directive* ([§14.5.4](namespaces.md#1454-using-static-directives)) imports the nested types and static members of a type. The scope of a *using_directive* extends over the *namespace_member_declarations* of its immediately containing compilation unit or namespace body. The scope of a *using_directive* specifically does not include its peer *using_directive*s. Thus, peer *using_directive*s do not affect each other, and the order in which they are written is insignificant. In contrast, the scope of an *extern_alias_directive* includes the *using_directive*s defined in the same compilation unit or namespace body. -### 13.5.2 Using alias directives +### 14.5.2 Using alias directives A *using_alias_directive* introduces an identifier that serves as an alias for a namespace or type within the immediately enclosing compilation unit or namespace body. @@ -224,9 +223,7 @@ Within using directives, global attributes and member declarations in a compilat > *Example*: For example: > -> -> -> +> > ```csharp > namespace N1 > { @@ -238,8 +235,7 @@ Within using directives, global attributes and member declarations in a compilat > > Above, within member declarations in the `N1` namespace, `N2` is an alias for some namespace whose definition is external to the source code of the program. Class `N1.B` derives from class `N2.A`. The same effect can be obtained by creating an alias `A` for `N2.A` and then referencing `A`: > -> -> +> > ```csharp > namespace N1 > { @@ -257,8 +253,7 @@ An *extern_alias_directive* or *using_alias_directive* makes an alias available > *Example*: In the following code > -> -> +> > ```csharp > namespace N3 > { @@ -275,8 +270,7 @@ An *extern_alias_directive* or *using_alias_directive* makes an alias available > > the scopes of the alias directives that introduce `R1` and `R2` only extend to member declarations in the namespace body in which they are contained, so `R1` and `R2` are unknown in the second namespace declaration. However, placing the alias directives in the containing compilation unit causes the alias to become available within both namespace declarations: > -> -> +> > ```csharp > extern alias R1; > @@ -299,18 +293,17 @@ Each *extern_alias_directive* or *using_alias_directive* in a *compilation_unit* > *Example*: > -> -> +> > ```csharp -> extern alias A; -> extern alias B; +> extern alias X; +> extern alias Y; > -> using A = N1.N2; // Error: alias A already exists +> using X = N1.N2; // Error: alias X already exists > -> class B {} // Ok +> class Y {} // Ok > ``` > -> The using alias named `A` causes an error since there is already an alias named `A` in the same compilation unit. The class named `B` does not conflict with the extern alias named `B` since these names are added to distinct declaration spaces. The former is added to the global declaration space and the latter is added to the alias declaration space for this compilation unit. +> The using alias named `X` causes an error since there is already an alias named `X` in the same compilation unit. The class named `Y` does not conflict with the extern alias named `Y` since these names are added to distinct declaration spaces. The former is added to the global declaration space and the latter is added to the alias declaration space for this compilation unit. > > When an alias name matches the name of a member of a namespace, usage of either must be appropriately qualified: > @@ -339,7 +332,7 @@ Each *extern_alias_directive* or *using_alias_directive* in a *compilation_unit* > } > ``` > -> In the second namespace body for `N3`, unqualified use of `B` results in an error, since `N3` contains a member named `B` and the namespace body that also declares an alias with name `B`; likewise for `A`. The class `N3.B` can be referenced as `N3.B` or `global::N3.B`. The alias `A` can be used in a *qualified-alias-member* ([§13.8](namespaces.md#138-qualified-alias-member)), such as `A::B`. The alias `B` is essentially useless. It cannot be used in a *qualified_alias_member* since only namespace aliases can be used in a *qualified_alias_member* and `B` aliases a type. +> In the second namespace body for `N3`, unqualified use of `B` results in an error, since `N3` contains a member named `B` and the namespace body that also declares an alias with name `B`; likewise for `A`. The class `N3.B` can be referenced as `N3.B` or `global::N3.B`. The alias `A` can be used in a *qualified-alias-member* ([§14.8](namespaces.md#148-qualified-alias-member)), such as `A::B`. The alias `B` is essentially useless. It cannot be used in a *qualified_alias_member* since only namespace aliases can be used in a *qualified_alias_member* and `B` aliases a type. > > *end example* @@ -366,23 +359,22 @@ The order in which *extern_alias_directive*s are written has no significance. Li > *Example*: In the following code > -> -> +> > ```csharp > namespace N1.N2 {} > > namespace N3 > { -> extern alias E; +> extern alias X; > -> using R1 = E::N; // OK +> using R1 = X::N; // OK > using R2 = N1; // OK > using R3 = N1.N2; // OK > using R4 = R2.N2; // Error, R2 unknown > } > ``` > -> the last *using_alias_directive* results in a compile-time error because it is not affected by the previous *using_alias_directive*. The first *using_alias_directive* does not result in an error since the scope of the extern alias E includes the *using_alias_directive*. +> the last *using_alias_directive* results in a compile-time error because it is not affected by the previous *using_alias_directive*. The first *using_alias_directive* does not result in an error since the scope of the extern alias X includes the *using_alias_directive*. > > *end example* @@ -417,7 +409,7 @@ Accessing a namespace or type through an alias yields exactly the same result as > > *end example* -Although each part of a partial type ([§14.2.7](classes.md#1427-partial-declarations)) is declared within the same namespace, the parts are typically written within different namespace declarations. Thus, different *extern_alias_directive*s and *using_directive*s can be present for each part. When interpreting simple names ([§11.7.4](expressions.md#1174-simple-names)) within one part, only the *extern_alias_directive*s and *using_directive*s of the namespace bodies and compilation unit enclosing that part are considered. This may result in the same identifier having different meanings in different parts. +Although each part of a partial type ([§15.2.7](classes.md#1527-partial-declarations)) is declared within the same namespace, the parts are typically written within different namespace declarations. Thus, different *extern_alias_directive*s and *using_directive*s can be present for each part. When interpreting simple names ([§12.8.4](expressions.md#1284-simple-names)) within one part, only the *extern_alias_directive*s and *using_directive*s of the namespace bodies and compilation unit enclosing that part are considered. This may result in the same identifier having different meanings in different parts. > *Example*: > @@ -471,7 +463,7 @@ Using aliases can name a closed constructed type, but cannot name an unbound gen > > *end example* -### 13.5.3 Using namespace directives +### 14.5.3 Using namespace directives A *using_namespace_directive* imports the types contained in a namespace into the immediately enclosing compilation unit or namespace body, enabling the identifier of each type to be used without qualification. @@ -630,7 +622,7 @@ Like a *using_alias_directive*, a *using_namespace_directive* does not contribut The *namespace_name* referenced by a *using_namespace_directive* is resolved in the same way as the *namespace_or_type_name* referenced by a *using_alias_directive*. Thus, *using_namespace_directive*s in the same compilation unit or namespace body do not affect each other and can be written in any order. -### 13.5.4 Using static directives +### 14.5.4 Using static directives A *using_static_directive* imports the nested types and static members contained directly in a type declaration into the immediately enclosing compilation unit or namespace body, enabling the identifier of each member and type to be used without qualification. @@ -673,7 +665,7 @@ Within member declarations in a compilation unit or namespace body that contains > > *end example* -A *using_static_directive* specifically does not import extension methods directly as static methods, but makes them available for extension method invocation ([§11.7.8.3](expressions.md#11783-extension-method-invocations)). +A *using_static_directive* specifically does not import extension methods directly as static methods, but makes them available for extension method invocation ([§12.8.9.3](expressions.md#12893-extension-method-invocations)). > *Example*: > @@ -745,11 +737,11 @@ A *using_static_directive* only imports members and types declared directly in t > > *end example* -Ambiguities between multiple *using_namespace_directives* and *using_static_directives* are discussed in [§13.5.3](namespaces.md#1353-using-namespace-directives). +Ambiguities between multiple *using_namespace_directives* and *using_static_directives* are discussed in [§14.5.3](namespaces.md#1453-using-namespace-directives). -## 13.6 Namespace member declarations +## 14.6 Namespace member declarations -A *namespace_member_declaration* is either a *namespace_declaration* ([§13.3](namespaces.md#133-namespace-declarations)) or a *type_declaration* ([§13.7](namespaces.md#137-type-declarations)). +A *namespace_member_declaration* is either a *namespace_declaration* ([§14.3](namespaces.md#143-namespace-declarations)) or a *type_declaration* ([§14.7](namespaces.md#147-type-declarations)). ```ANTLR namespace_member_declaration @@ -760,9 +752,9 @@ namespace_member_declaration A compilation unit or a namespace body can contain *namespace_member_declaration*s, and such declarations contribute new members to the underlying declaration space of the containing compilation unit or namespace body. -## 13.7 Type declarations +## 14.7 Type declarations -A *type_declaration* is a *class_declaration* ([§14.2](classes.md#142-class-declarations)), a *struct_declaration* ([§15.2](structs.md#152-struct-declarations)), an *interface_declaration* ([§17.2](interfaces.md#172-interface-declarations)), an *enum_declaration* ([§18.2](enums.md#182-enum-declarations)), or a *delegate_declaration* ([§19.2](delegates.md#192-delegate-declarations)). +A *type_declaration* is a *class_declaration* ([§15.2](classes.md#152-class-declarations)), a *struct_declaration* ([§16.2](structs.md#162-struct-declarations)), an *interface_declaration* ([§18.2](interfaces.md#182-interface-declarations)), an *enum_declaration* ([§19.2](enums.md#192-enum-declarations)), or a *delegate_declaration* ([§20.2](delegates.md#202-delegate-declarations)). ```ANTLR type_declaration @@ -778,7 +770,7 @@ A *type_declaration* can occur as a top-level declaration in a compilation unit When a type declaration for a type `T` occurs as a top-level declaration in a compilation unit, the fully qualified name ([§7.8.2](basic-concepts.md#782-unqualified-names)) of the type declaration is the same as the unqualified name of the declaration ([§7.8.2](basic-concepts.md#782-unqualified-names)). When a type declaration for a type `T` occurs within a namespace, class, or struct declaration, the fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names)) of the type declarationis `S.N`, where `S` is the fully qualified name of the containing namespace, class, or struct declaration, and `N` is the unqualified name of the declaration. -A type declared within a class or struct is called a nested type ([§14.3.9](classes.md#1439-nested-types)). +A type declared within a class or struct is called a nested type ([§15.3.9](classes.md#1539-nested-types)). The permitted access modifiers and the default access for a type declaration depend on the context in which the declaration takes place ([§7.5.2](basic-concepts.md#752-declared-accessibility)): @@ -786,9 +778,9 @@ The permitted access modifiers and the default access for a type declaration dep - Types declared in classes can have `public`, `protected internal`, `protected`, `private protected`, `internal`, or `private` access. The default is `private` access. - Types declared in structs can have `public`, `internal`, or `private` access. The default is `private` access. -## 13.8 Qualified alias member +## 14.8 Qualified alias member -### 13.8.1 General +### 14.8.1 General The ***namespace alias qualifier*** `::` makes it possible to guarantee that type name lookups are unaffected by the introduction of new types and members. The namespace alias qualifier always appears between two identifiers referred to as the left-hand and right-hand identifiers. Unlike the regular `.` qualifier, the left-hand identifier of the `::` qualifier is looked up only as an extern or using alias. @@ -800,9 +792,9 @@ qualified_alias_member ; ``` -A *qualified_alias_member* can be used as a *namespace_or_type_name* ([§7.8](basic-concepts.md#78-namespace-and-type-names)) or as the left operand in a *member_access* ([§11.7.6](expressions.md#1176-member-access)). +A *qualified_alias_member* can be used as a *namespace_or_type_name* ([§7.8](basic-concepts.md#78-namespace-and-type-names)) or as the left operand in a *member_access* ([§12.8.7](expressions.md#1287-member-access)). -A *qualified_alias_member* consists of two identifiers, referred to as the left-hand and right-hand identifiers, seperated by the `::` token and optionally followed by a *type_argument_list*. When the left-hand identifier is global then the global namespace is searched for the right-hand identifier. For any other left-hand identifier, that identifier is looked up as an extern or using alias ([§13.4](namespaces.md#134-extern-alias-directives) and [§13.5.2](namespaces.md#1352-using-alias-directives)). A compile-time error occurs if there is no such alias or the alias references a type. If the alias references a namespace then that namespace is searched for the right-hand identifier. +A *qualified_alias_member* consists of two identifiers, referred to as the left-hand and right-hand identifiers, seperated by the `::` token and optionally followed by a *type_argument_list*. When the left-hand identifier is global then the global namespace is searched for the right-hand identifier. For any other left-hand identifier, that identifier is looked up as an extern or using alias ([§14.4](namespaces.md#144-extern-alias-directives) and [§14.5.2](namespaces.md#1452-using-alias-directives)). A compile-time error occurs if there is no such alias or the alias references a type. If the alias references a namespace then that namespace is searched for the right-hand identifier. A *qualified_alias_member* has one of two forms: @@ -816,7 +808,7 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as - Otherwise, if the global namespace contains a non-generic type named `I` and `e` is zero, then the *qualified_alias_member* refers to that type. - Otherwise, if the global namespace contains a type named `I` that has `e` type parameters, then the *qualified_alias_member* refers to that type constructed with the given type arguments. - Otherwise, the *qualified_alias_member* is undefined and a compile-time error occurs. -- Otherwise, starting with the namespace declaration ([§13.3](namespaces.md#133-namespace-declarations)) immediately containing the *qualified_alias_member* (if any), continuing with each enclosing namespace declaration (if any), and ending with the compilation unit containing the *qualified_alias_member*, the following steps are evaluated until an entity is located: +- Otherwise, starting with the namespace declaration ([§14.3](namespaces.md#143-namespace-declarations)) immediately containing the *qualified_alias_member* (if any), continuing with each enclosing namespace declaration (if any), and ending with the compilation unit containing the *qualified_alias_member*, the following steps are evaluated until an entity is located: - If the namespace declaration or compilation unit contains a *using_alias_directive* that associates N with a type, then the *qualified_alias_member* is undefined and a compile-time error occurs. - Otherwise, if the namespace declaration or compilation unit contains an *extern_alias_directive* or *using_alias_directive* that associates `N` with a namespace, then: - If the namespace associated with `N` contains a namespace named `I` and `e` is zero, then the *qualified_alias_member* refers to that namespace. @@ -888,7 +880,7 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as > > *end note* -### 13.8.2 Uniqueness of aliases +### 14.8.2 Uniqueness of aliases Each compilation unit and namespace body has a separate declaration space for extern aliases and using aliases. Thus, while the name of an extern alias or using alias shall be unique within the set of extern aliases and using aliases declared in the immediately containing compilation unit or namespace body, an alias is permitted to have the same name as a type or namespace as long as it is used only with the `::` qualifier. diff --git a/standard/patterns.md b/standard/patterns.md new file mode 100644 index 000000000..f5a948263 --- /dev/null +++ b/standard/patterns.md @@ -0,0 +1,185 @@ +# 11 Patterns and pattern matching + +## 11.1 General + +A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.12.12](expressions.md#121212-the-is-operator)) and in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)) to express the shape of data against which incoming data is to be compared. A pattern is tested against the *expression* of a switch statement, or against a *relational_expression* that is on the left-hand side of an `is` operator, each of which is referred to as a ***pattern input value***. + +## 11.2 Pattern forms + +### 11.2.1 General + +A pattern may have one of the following forms: + +```ANTLR +pattern + : declaration_pattern + | constant_pattern + | var_pattern + ; +``` + +A *declaration_pattern* and a *var_pattern* can result in the declaration of a local variable. + +Each pattern form defines the set of types for input values that the pattern may be applied to. A pattern `P` is *applicable to* a type `T` if `T` is among the types whose values the pattern may match. It is a compile-time error if a pattern `P` appears in a program to match a pattern input value ([§11.1](patterns.md#111-general)) of type `T` if `P` is not applicable to `T`. + +Each pattern form defines the set of values for which the pattern *matches* the value at runtime. + +### 11.2.2 Declaration pattern + +A *declaration_pattern* is used to test that a value has a given type and, if the test succeeds, provide the value in a variable of that type. + +```ANTLR +declaration_pattern + : type simple_designation + ; +simple_designation + : single_variable_designation + ; +single_variable_designation + : identifier + ; +``` + +The runtime type of the value is tested against the *type* in the pattern using the same rules specified in the is-type operator ([§12.12.12.1](expressions.md#1212121-the-is-type-operator)). If the test succeeds, the pattern *matches* that value. It is a compile-time error if the *type* is a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)). This pattern form never matches a `null` value. + +> *Note*: The is-type expression `e is T` and the declaration pattern `e is T _` are equivalent when `T` isn’t a nullable type. *end note* + +Given a pattern input value ([§11.1](patterns.md#111-general)) *e*, if the *simple_designation* is the *identifier* `_`, it denotes a discard ([§9.2.9.1](variables.md#9291-discards)) and the value of *e* is not bound to anything. (Although a declared variable with the name `_` may be in scope at that point, that named variable is not seen in this context.) If *simple_designation* is any other identifier, a local variable ([§9.2.9](variables.md#929-local-variables)) of the given type named by the given identifier is introduced. That local variable is assigned the value of the pattern input value when the pattern *matches* the value. + +Certain combinations of static type of the pattern input value and the given type are considered incompatible and result in a compile-time error. A value of static type `E` is said to be ***pattern compatible*** with the type `T` if there exists an identity conversion, an implicit or explicit reference conversion, a boxing conversion, or an unboxing conversion from `E` to `T`, or if either `E` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)). A declaration pattern naming a type `T` is *applicable to* every type `E` for which `E` is pattern compatible with `T`. + +> *Note*: The support for open types can be most useful when checking types that may be either struct or class types, and boxing is to be avoided. *end note* + + +> *Example*: The declaration pattern is useful for performing run-time type tests of reference types, and replaces the idiom +> +> ```csharp +> var v = expr as Type; +> if (v != null) { /* code using v */ } +> ``` +> +> with the slightly more concise +> +> ```csharp +> if (expr is Type v) { /* code using v */ } +> ``` +> +> *end example* + +It is an error if *type* is a nullable value type. + +> *Example*: The declaration pattern can be used to test values of nullable types: a value of type `Nullable` (or a boxed `T`) matches a type pattern `T2 id` if the value is non-null and `T2` is `T`, or some base type or interface of `T`. For example, in the code fragment +> +> +> ```csharp +> int? x = 3; +> if (x is int v) { /* code using v */ } +> ``` +> +> The condition of the `if` statement is `true` at runtime and the variable `v` holds the value `3` of type `int` inside the block. *end example* + +### 11.2.3 Constant pattern + +A *constant_pattern* is used to test the value of a pattern input value ([§11.1](patterns.md#111-general)) against the given constant value. + +```ANTLR +constant_pattern + : constant_expression + ; +``` + +A constant pattern `P` is *applicable to* a type `T` if there is an implicit conversion from the constant expression of `P` to the type `T`. + +For a constant pattern `P`, its *converted value* is + +- if the pattern input value’s type is an integral type or an enum type, the pattern’s constant value converted to that type; otherwise +- if the pattern input value’s type is the nullable version of an integral type or an enum type, the pattern’s constant value converted to its underlying type; otherwise +- the value of the pattern’s constant value. + +Given a pattern input value *e* and a constant pattern `P` with converted value *v*, + +- if *e* has integral type or enum type, or a nullable form of one of those, and *v* has integral type, the pattern `P` *matches* the value *e* if result of the expression `e == v` is `true`; otherwise +- the pattern `P` *matches* the value *e* if `object.Equals(e, v)` returns `true`. + +> *Example*: The `switch` statement in the following method uses five constant patterns in its case labels. +> +> +> ```csharp +> static decimal GetGroupTicketPrice(int visitorCount) +> { +> switch (visitorCount) +> { +> case 1: return 12.0m; +> case 2: return 20.0m; +> case 3: return 27.0m; +> case 4: return 32.0m; +> case 0: return 0.0m; +> default: throw new ArgumentException(...); +> } +> } +> ``` +> +> *end example* + +### 11.2.4 Var pattern + +A *var_pattern* *matches* every value. That is, a pattern-matching operation with a *var_pattern* always succeeds. + +A *var_pattern* is *applicable to* every type. + +```ANTLR +var_pattern + : 'var' designation + ; +designation + : simple_designation + ; +``` + +Given a pattern input value ([§11.1](patterns.md#111-general)) *e*, if *designation* is the *identifier* `_`, it denotes a discard ([§9.2.9.1](variables.md#9291-discards)), and the value of *e* is not bound to anything. (Although a declared variable with that name may be in scope at that point, that named variable is not seen in this context.) If *designation* is any other identifier, at runtime the value of *e* is bound to a newly introduced local variable ([§9.2.9](variables.md#929-local-variables)) of that name whose type is the static type of *e*, and the pattern input value is assigned to that local variable. + +It is an error if the name `var` would bind to a type where a *var_pattern* is used. + +## 11.3 Pattern subsumption + +In a switch statement, it is an error if a case’s pattern is *subsumed* by the preceding set of unguarded cases ([§13.8.3](statements.md#1383-the-switch-statement)). +Informally, this means that any input value would have been matched by one of the previous cases. +The following rules define when a set of patterns subsumes a given pattern: + +A pattern `P` *would match* a constant `K` if the specification for that pattern’s runtime behavior is that `P` matches `K`. + +A set of patterns `Q` *subsumes* a pattern `P` if any of the following conditions hold: + +- `P` is a constant pattern and any of the patterns in the set `Q` would match `P`’s *converted value* +- `P` is a var pattern and the set of patterns `Q` is *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the type of the pattern input value ([§11.1](patterns.md#111-general)), and either the pattern input value is not of a nullable type or some pattern in `Q` would match `null`. +- `P` is a declaration pattern with type `T` and the set of patterns `Q` is *exhaustive* for the type `T` ([§11.4](patterns.md#114-pattern-exhaustiveness)). + +## 11.4 Pattern exhaustiveness + +Informally, a set of patterns is exhaustive for a type if some pattern in the set is applicable to every possible value of that type other than null. +The following rules define when a set of patterns is *exhaustive* for a type: + +A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold: + +1. `T` is an integral or enum type, or a nullable version of one of those, and for every possible value of `T`’s non-nullable underlying type, some pattern in `Q` would match that value; or +2. Some pattern in `Q` is a *var pattern*; or +3. Some pattern in `Q` is a *declaration pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`. + +> *Example*: +> +> +> ```csharp +> static void M(byte b) +> { +> switch (b) { +> case 0: case 1: case 2: ... // handle every specific value of byte +> break; +> // error: the pattern 'byte other' is subsumed by the (exhaustive) +> // previous cases +> case byte other: +> break; +> } +> } +> ``` +> +> *end example* diff --git a/standard/portability-issues.md b/standard/portability-issues.md index e7e9e48ac..7e846a428 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -10,54 +10,57 @@ This annex collects some information about portability that appears in this spec The behavior is undefined in the following circumstances: -1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§11.8.8.4](expressions.md#11884-run-time-evaluation-of-await-expressions)). -1. Passing pointers as `ref` or `out` parameters ([§22.3](unsafe-code.md#223-pointer-types)). -1. When dereferencing the result of converting one pointer type to another and the resulting pointer is not correctly aligned for the pointed-to type. ([§22.5.1](unsafe-code.md#2251-general)). -1. When the unary `*` operator is applied to a pointer containing an invalid value ([§22.6.2](unsafe-code.md#2262-pointer-indirection)). -1. When a pointer is subscripted to access an out-of-bounds element ([§22.6.4](unsafe-code.md#2264-pointer-element-access)). -1. Modifying objects of managed type through fixed pointers ([§22.7](unsafe-code.md#227-the-fixed-statement)). -1. The content of memory newly allocated by `stackalloc` ([§22.9](unsafe-code.md#229-stack-allocation)). -1. Attempting to allocate a negative number of items using `stackalloc`([§22.9](unsafe-code.md#229-stack-allocation)). +1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§12.9.8.4](expressions.md#12984-run-time-evaluation-of-await-expressions)). +1. Passing pointers as `ref` or `out` parameters ([§23.3](unsafe-code.md#233-pointer-types)). +1. When dereferencing the result of converting one pointer type to another and the resulting pointer is not correctly aligned for the pointed-to type. ([§23.5.1](unsafe-code.md#2351-general)). +1. When the unary `*` operator is applied to a pointer containing an invalid value ([§23.6.2](unsafe-code.md#2362-pointer-indirection)). +1. When a pointer is subscripted to access an out-of-bounds element ([§23.6.4](unsafe-code.md#2364-pointer-element-access)). +1. Modifying objects of managed type through fixed pointers ([§23.7](unsafe-code.md#237-the-fixed-statement)). +1. The content of memory newly allocated by `stackalloc` ([§12.8.21](expressions.md#12821-stack-allocation)). +1. Attempting to allocate a negative number of items using `stackalloc`([§12.8.21](expressions.md#12821-stack-allocation)). +1. Implicit dynamic conversions ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions)) of `in` parameters with value arguments ([§12.6.4.2](expressions.md#12642-applicable-function-member)). ## B.3 Implementation-defined behavior A conforming implementation is required to document its choice of behavior in each of the areas listed in this subclause. The following are implementation-defined: 1. The behavior when an identifier not in Normalization Form C is encountered ([§6.4.3](lexical-structure.md#643-identifiers)). +1. The maximum value allowed for `Decimal_Digit+` in `PP_Line_Indicator` ([§6.5.8](lexical-structure.md#658-line-directives)). 1. The interpretation of the *input_characters* in the *pp_pragma-text* of a #pragma directive ([§6.5.9](lexical-structure.md#659-pragma-directives)). 1. The values of any application parameters passed to `Main` by the host environment prior to application startup ([§7.1](basic-concepts.md#71-application-startup)). 1. The precise structure of the expression tree, as well as the exact process for creating it, when an anonymous function is converted to an expression-tree ([§10.7.3](conversions.md#1073-evaluation-of-lambda-expression-conversions-to-expression-tree-types)). -1. Whether a `System.ArithmeticException` (or a subclass thereof) is thrown or the overflow goes unreported with the resulting value being that of the left operand, when in an `unchecked` context and the left operand of an integer division is the maximum negative `int` or `long` value and the right operand is `–1` ([§11.9.3](expressions.md#1193-division-operator)). -1. When a `System.ArithmeticException` (or a subclass thereof) is thrown when performing a decimal remainder operation ([§11.9.4](expressions.md#1194-remainder-operator)). -1. The impact of thread termination when a thread has no handler for an exception, and the thread is itself terminated ([§12.10.6](statements.md#12106-the-throw-statement)). -1. The impact of thread termination when no matching `catch` clause is found for an exception and the code that initially started that thread is reached. ([§20.4](exceptions.md#204-how-exceptions-are-handled)). -1. The mappings between pointers and integers ([§22.5.1](unsafe-code.md#2251-general)). -1. The effect of applying the unary `*` operator to a `null` pointer ([§22.6.2](unsafe-code.md#2262-pointer-indirection)). -1. The behavior when pointer arithmetic overflows the domain of the pointer type ([§22.6.6](unsafe-code.md#2266-pointer-increment-and-decrement), [§22.6.7](unsafe-code.md#2267-pointer-arithmetic)). -1. The result of the `sizeof` operator for non-pre-defined value types ([§22.6.9](unsafe-code.md#2269-the-sizeof-operator)). -1. The behavior of the `fixed` statement if the array expression is `null` or if the array has zero elements ([§22.7](unsafe-code.md#227-the-fixed-statement)). -1. The behavior of the `fixed` statement if the string expression is `null` ([§22.7](unsafe-code.md#227-the-fixed-statement)). -1. The value returned when a stack allocation of size zero is made ([§22.9](unsafe-code.md#229-stack-allocation)). +1. Whether a `System.ArithmeticException` (or a subclass thereof) is thrown or the overflow goes unreported with the resulting value being that of the left operand, when in an `unchecked` context and the left operand of an integer division is the maximum negative `int` or `long` value and the right operand is `–1` ([§12.10.3](expressions.md#12103-division-operator)). +1. When a `System.ArithmeticException` (or a subclass thereof) is thrown when performing a decimal remainder operation ([§12.10.4](expressions.md#12104-remainder-operator)). +1. The impact of thread termination when a thread has no handler for an exception, and the thread is itself terminated ([§13.10.6](statements.md#13106-the-throw-statement)). +1. The impact of thread termination when no matching `catch` clause is found for an exception and the code that initially started that thread is reached. ([§21.4](exceptions.md#214-how-exceptions-are-handled)). +1. The mappings between pointers and integers ([§23.5.1](unsafe-code.md#2351-general)). +1. The effect of applying the unary `*` operator to a `null` pointer ([§23.6.2](unsafe-code.md#2362-pointer-indirection)). +1. The behavior when pointer arithmetic overflows the domain of the pointer type ([§23.6.6](unsafe-code.md#2366-pointer-increment-and-decrement), [§23.6.7](unsafe-code.md#2367-pointer-arithmetic)). +1. The result of the `sizeof` operator for non-pre-defined value types ([§23.6.9](unsafe-code.md#2369-the-sizeof-operator)). +1. The behavior of the `fixed` statement if the array expression is `null` or if the array has zero elements ([§23.7](unsafe-code.md#237-the-fixed-statement)). +1. The behavior of the `fixed` statement if the string expression is `null` ([§23.7](unsafe-code.md#237-the-fixed-statement)). +1. The value returned when a stack allocation of size zero is made ([§12.8.21](expressions.md#12821-stack-allocation)). ## B.4 Unspecified behavior 1. The time at which the finalizer (if any) for an object is run, once that object has become eligible for finalization ([§7.9](basic-concepts.md#79-automatic-memory-management)). +1. The representation of `true` ([§8.3.9](types.md#839-the-bool-type)). 1. The value of the result when converting out-of-range values from `float` or `double` values to an integral type in an `unchecked` context ([§10.3.2](conversions.md#1032-explicit-numeric-conversions)). 1. The exact target object and target method of the delegate produced from an *anonymous_method_expression* contains ([§10.7.2](conversions.md#1072-evaluation-of-anonymous-function-conversions-to-delegate-types)). -1. The layout of arrays, except in an unsafe context ([§11.7.15.5](expressions.md#117155-array-creation-expressions)). -1. Whether there is any way to execute the *block* of an anonymous function other than through evaluation and invocation of the *lambda_expression* or *anonymous_method-expression* ([§11.17.3](expressions.md#11173-anonymous-function-bodies)). -1. The exact timing of static field initialization ([§14.5.6.2](classes.md#14562-static-field-initialization)). -1. The result of invoking `MoveNext` when an enumerator object is running ([§14.14.5.2](classes.md#141452-the-movenext-method)). -1. The result of accessing `Current` when an enumerator object is in the before, running, or after states ([§14.14.5.3](classes.md#141453-the-current-property)). -1. The result of invoking `Dispose` when an enumerator object is in the running state ([§14.14.5.4](classes.md#141454-the-dispose-method)). -1. The attributes of a type declared in multiple parts are determined by combining, in an unspecified order, the attributes of each of its parts ([§21.3](attributes.md#213-attribute-specification)). -1. The order in which members are packed into a struct ([§22.6.9](unsafe-code.md#2269-the-sizeof-operator)). -1. An exception occurs during finalizer execution, and that execution is not caught ([§20.4](exceptions.md#204-how-exceptions-are-handled)). -1. If more than one member matches, which member is the implementation of I.M. ([§17.6.5](interfaces.md#1765-interface-mapping)) - -## B.5 Other Issues - -1. The exact results of floating-point expression evaluation can vary from one implementation to another, because an implementation is permitted to evaluate such expressions using a greater range and/or precision than is required. ([§8.3.7](types.md#837-floating-point-types)) -1. The CLI reserves certain signatures for compatibility with other programming languages. ([§14.3.10](classes.md#14310-reserved-member-names)) +1. The layout of arrays, except in an unsafe context ([§12.8.16.5](expressions.md#128165-array-creation-expressions)). +1. Whether there is any way to execute the *block* of an anonymous function other than through evaluation and invocation of the *lambda_expression* or *anonymous_method-expression* ([§12.19.3](expressions.md#12193-anonymous-function-bodies)). +1. The exact timing of static field initialization ([§15.5.6.2](classes.md#15562-static-field-initialization)). +1. The result of invoking `MoveNext` when an enumerator object is running ([§15.14.5.2](classes.md#151452-the-movenext-method)). +1. The result of accessing `Current` when an enumerator object is in the before, running, or after states ([§15.14.5.3](classes.md#151453-the-current-property)). +1. The result of invoking `Dispose` when an enumerator object is in the running state ([§15.14.5.4](classes.md#151454-the-dispose-method)). +1. The attributes of a type declared in multiple parts are determined by combining, in an unspecified order, the attributes of each of its parts ([§22.3](attributes.md#223-attribute-specification)). +1. The order in which members are packed into a struct ([§23.6.9](unsafe-code.md#2369-the-sizeof-operator)). +1. An exception occurs during finalizer execution, and that exception is not caught ([§21.4](exceptions.md#214-how-exceptions-are-handled)). +1. If more than one member matches, which member is the implementation of `I.M` ([§18.6.5](interfaces.md#1865-interface-mapping)). + +## B.5 Other issues + +1. The exact results of floating-point expression evaluation can vary from one implementation to another, because an implementation is permitted to evaluate such expressions using a greater range and/or precision than is required ([§8.3.7](types.md#837-floating-point-types)). +1. The CLI reserves certain signatures for compatibility with other programming languages ([§15.3.10](classes.md#15310-reserved-member-names)). **End of informative text.** diff --git a/standard/standard-library.md b/standard/standard-library.md index 8e94f52fa..79a6d3c3c 100644 --- a/standard/standard-library.md +++ b/standard/standard-library.md @@ -21,6 +21,9 @@ It is expected that a conforming C# implementation will supply a significantly ## C.2 Standard Library Types defined in ISO/IEC 23271 +> *Note:* Some `struct` types below have the `readonly` modifier. This modifier was not available +> when ISO/IEC 23271 was released, but is required for conforming implementations of this specification. *end note* + ```csharp namespace System { @@ -91,10 +94,10 @@ namespace System public AttributeTargets ValidOn { get; } } - public struct Boolean { } - public struct Byte { } - public struct Char { } - public struct Decimal { } + public readonly struct Boolean { } + public readonly struct Byte { } + public readonly struct Char { } + public readonly struct Decimal { } public abstract class Delegate { } public class DivideByZeroException : ArithmeticException @@ -104,7 +107,7 @@ namespace System public DivideByZeroException(string message, Exception innerException); } - public struct Double { } + public readonly struct Double { } public abstract class Enum : ValueType { @@ -137,10 +140,10 @@ namespace System Exception innerException); } - public struct Int16 { } - public struct Int32 { } - public struct Int64 { } - public struct IntPtr { } + public readonly struct Int16 { } + public readonly struct Int32 { } + public readonly struct Int64 { } + public readonly struct IntPtr { } public class InvalidCastException : Exception { @@ -215,8 +218,8 @@ namespace System public OverflowException(string message, Exception innerException); } - public struct SByte { } - public struct Single { } + public readonly struct SByte { } + public readonly struct Single { } public sealed class StackOverflowException : Exception { @@ -240,10 +243,82 @@ namespace System Exception innerException); } - public struct UInt16 { } - public struct UInt32 { } - public struct UInt64 { } - public struct UIntPtr { } + public readonly struct UInt16 { } + public readonly struct UInt32 { } + public readonly struct UInt64 { } + public readonly struct UIntPtr { } + + public struct ValueTuple + { + public T1 Item1; + public ValueTuple(T1 item1); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public ValueTuple(T1 item1, T2 item2); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public T3 Item3; + public ValueTuple(T1 item1, T2 item2, T3 item3); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public T3 Item3; + public T4 Item4; + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public T3 Item3; + public T4 Item4; + public T5 Item5; + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public T3 Item3; + public T4 Item4; + public T5 Item5; + public T6 Item6; + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, + T6 item6); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public T3 Item3; + public T4 Item4; + public T5 Item5; + public T6 Item6; + public T7 Item7; + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, + T6 item6, T7 item7); + } + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public T3 Item3; + public T4 Item4; + public T5 Item5; + public T6 Item6; + public T7 Item7; + public TRest Rest; + public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, + T6 item6, T7 item7, TRest rest); + } public abstract class ValueType { @@ -355,6 +430,11 @@ namespace System.Runtime.CompilerServices { public IndexerNameAttribute(String indexerName); } + + public static class Unsafe + { + public static ref T NullRef(); + } } namespace System.Threading @@ -371,7 +451,7 @@ namespace System.Threading The following types, including the members listed, must be defined in a conforming standard library. (These types might be defined in a future edition of ISO/IEC 23271.) It is expected that many of these types will have more members available than are listed. -A conforming implementation may provide `Task.GetAwaiter()` and `Task.GetAwaiter()` as extension methods. +A conforming implementation may provide `Task.GetAwaiter()` and `Task.GetAwaiter()` as extension methods. ```csharp namespace System @@ -388,6 +468,16 @@ namespace System.Linq.Expressions } namespace System.Runtime.CompilerServices { + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | + AttributeTargets.Interface, + Inherited = false, AllowMultiple = false)] + public sealed class AsyncMethodBuilderAttribute : Attribute + { + public AsyncMethodBuilderAttribute(Type builderType) {} + + public Type BuilderType { get; } + } + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] public sealed class CallerFilePathAttribute : Attribute { @@ -422,17 +512,34 @@ namespace System.Runtime.CompilerServices void OnCompleted(Action continuation); } - public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion + public readonly struct TaskAwaiter : ICriticalNotifyCompletion, + INotifyCompletion + { + public bool IsCompleted { get; } + public void GetResult(); + } + + public readonly struct TaskAwaiter : ICriticalNotifyCompletion, + INotifyCompletion + { + public bool IsCompleted { get; } + public TResult GetResult(); + } + + public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion, + INotifyCompletion { public bool IsCompleted { get; } public void GetResult(); } - public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion + public readonly struct ValueTaskAwaiter + : ICriticalNotifyCompletion, INotifyCompletion { public bool IsCompleted { get; } - public T GetResult(); + public TResult GetResult(); } + } namespace System.Threading.Tasks @@ -445,12 +552,39 @@ namespace System.Threading.Tasks { public new System.Runtime.CompilerServices.TaskAwaiter GetAwaiter(); } + public readonly struct ValueTask : System.IEquatable + { + public System.Runtime.CompilerServices.ValueTaskAwaiter GetAwaiter(); + } + public readonly struct ValueTask + : System.IEquatable> + { + public new System.Runtime.CompilerServices.ValueTaskAwaiter + GetAwaiter(); + } +} +``` + +```csharp +namespace System +{ + public readonly ref struct ReadOnlySpan + { + public int Length { get; } + public ref readonly T this[int index] { get; } + } + public readonly ref struct Span + { + public int Length { get; } + public ref T this[int index] { get; } + public static implicit operator ReadOnlySpan(Span span); + } } ``` ## C.4 Format Specifications -The meaning of the formats, as used in interpolated string expressions ([§11.7.3](expressions.md#1173-interpolated-string-expressions)), are defined in ISO/IEC 23271:2012. For convenience the following text is copied from the description of `System.IFormatable`. +The meaning of the formats, as used in interpolated string expressions ([§12.8.3](expressions.md#1283-interpolated-string-expressions)), are defined in ISO/IEC 23271:2012. For convenience the following text is copied from the description of `System.IFormatable`. **This text is informative.** @@ -461,11 +595,11 @@ alphabetic character called the *format specifier*, and *xx* is an integer betwe of formatting applied to the value being represented as a string. The *precision specifier* controls the number of significant digits or decimal places in the string, if applicable. -> *Note:* For the list of standard format specifiers, see the table below. Note that a given data type, such as `System.Int32`, might not support one or more of the standard format specifiers. *end note* +> *Note*: For the list of standard format specifiers, see the table below. Note that a given data type, such as `System.Int32`, might not support one or more of the standard format specifiers. *end note* -> *Note:* When a format includes symbols that vary by culture, such as the currencysymbol included by the ‘C’ and ‘c’ formats, a formatting object supplies the actual characters used in the string representation. A method might include a parameter to pass a `System.IFormatProvider` object that supplies a formatting object, or the method might use the default formatting object, which contains the symbol definitions for the current culture. The current culture typically uses the same set of symbols used system-wide by default. In the Base Class Library, the formatting object for system-supplied numeric types is a `System.Globalization.NumberFormatInfo` instance. For `System.DateTime` instances, a `System.Globalization.DateTimeFormatInfo` is used. *end note* +> *Note*: When a format includes symbols that vary by culture, such as the currencysymbol included by the ‘C’ and ‘c’ formats, a formatting object supplies the actual characters used in the string representation. A method might include a parameter to pass a `System.IFormatProvider` object that supplies a formatting object, or the method might use the default formatting object, which contains the symbol definitions for the current culture. The current culture typically uses the same set of symbols used system-wide by default. In the Base Class Library, the formatting object for system-supplied numeric types is a `System.Globalization.NumberFormatInfo` instance. For `System.DateTime` instances, a `System.Globalization.DateTimeFormatInfo` is used. *end note* The following table describes the standard format specifiers and associated formatting object members that are used with numeric data types in the Base Class @@ -898,7 +1032,7 @@ The following library types are referenced in this specification. The full names - `global::System.Threading.Tasks.Task` - `global::System.Threading.Tasks.Task` - `global::System.Type` -- `global::System.TypeInializationException` +- `global::System.TypeInitializationException` - `global::System.UInt16` - `global::System.UInt32` - `global::System.UInt64` diff --git a/standard/statements.md b/standard/statements.md index 40decb7f5..a8fc58a60 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -1,6 +1,6 @@ -# 12 Statements +# 13 Statements -## 12.1 General +## 13.1 General C# provides a variety of statements. @@ -31,7 +31,7 @@ embedded_statement ; ``` -*unsafe_statement* ([§22.2](unsafe-code.md#222-unsafe-contexts)) and *fixed_statement* ([§22.7](unsafe-code.md#227-the-fixed-statement)) are only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_statement* ([§23.2](unsafe-code.md#232-unsafe-contexts)) and *fixed_statement* ([§23.7](unsafe-code.md#237-the-fixed-statement)) are only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). The *embedded_statement* nonterminal is used for statements that appear within other statements. The use of *embedded_statement* rather than *statement* excludes the use of declaration statements and labeled statements in these contexts. @@ -50,7 +50,7 @@ The *embedded_statement* nonterminal is used for statements that appear within o > > *end example* -## 12.2 End points and reachability +## 13.2 End points and reachability Every statement has an ***end point***. In intuitive terms, the end point of a statement is the location that immediately follows the statement. The execution rules for composite statements (statements that contain embedded statements) specify the action that is taken when control reaches the end point of an embedded statement. @@ -78,7 +78,7 @@ If a statement can possibly be reached by execution, the statement is said to be A warning is reported if a statement other than *throw_statement*, *block*, or *empty_statement* is unreachable. It is specifically not an error for a statement to be unreachable. -> *Note*: To determine whether a particular statement or end point is reachable, the compiler performs flow analysis according to the reachability rules defined for each statement. The flow analysis takes into account the values of constant expressions ([§11.21](expressions.md#1121-constant-expressions)) that control the behavior of statements, but the possible values of non-constant expressions are not considered. In other words, for purposes of control flow analysis, a non-constant expression of a given type is considered to have any possible value of that type. +> *Note*: To determine whether a particular statement or end point is reachable, the compiler performs flow analysis according to the reachability rules defined for each statement. The flow analysis takes into account the values of constant expressions ([§12.23](expressions.md#1223-constant-expressions)) that control the behavior of statements, but the possible values of non-constant expressions are not considered. In other words, for purposes of control flow analysis, a non-constant expression of a given type is considered to have any possible value of that type. > > In the example > @@ -124,9 +124,9 @@ The *block* of a function member or an anonymous function is always considered r > > the reachability of the second `Console.WriteLine` is determined as follows: > -> - The first `Console.WriteLine` expression statement is reachable because the block of the `F` method is reachable ([§12.3](statements.md#123-blocks)). -> - The end point of the first `Console.WriteLine` expression statement is reachable because that statement is reachable ([§12.7](statements.md#127-expression-statements) and [§12.3](statements.md#123-blocks)). -> - The `if` statement is reachable because the end point of the first `Console.WriteLine` expression statement is reachable ([§12.7](statements.md#127-expression-statements) and [§12.3](statements.md#123-blocks)). +> - The first `Console.WriteLine` expression statement is reachable because the block of the `F` method is reachable ([§13.3](statements.md#133-blocks)). +> - The end point of the first `Console.WriteLine` expression statement is reachable because that statement is reachable ([§13.7](statements.md#137-expression-statements) and [§13.3](statements.md#133-blocks)). +> - The `if` statement is reachable because the end point of the first `Console.WriteLine` expression statement is reachable ([§13.7](statements.md#137-expression-statements) and [§13.3](statements.md#133-blocks)). > - The second `Console.WriteLine` expression statement is reachable because the Boolean expression of the `if` statement does not have the constant value `false`. > > *end example* @@ -135,11 +135,11 @@ There are two situations in which it is a compile-time error for the end point o - Because the `switch` statement does not permit a switch section to “fall through” to the next switch section, it is a compile-time error for the end point of the statement list of a switch section to be reachable. If this error occurs, it is typically an indication that a `break` statement is missing. -- It is a compile-time error for the end point of the block of a function member or an anonymous function that computes a value to be reachable. If this error occurs, it typically is an indication that a `return` statement is missing ([§12.10.5](statements.md#12105-the-return-statement)). +- It is a compile-time error for the end point of the block of a function member or an anonymous function that computes a value to be reachable. If this error occurs, it typically is an indication that a `return` statement is missing ([§13.10.5](statements.md#13105-the-return-statement)). -## 12.3 Blocks +## 13.3 Blocks -### 12.3.1 General +### 13.3.1 General A *block* permits multiple statements to be written in contexts where a single statement is allowed. @@ -149,9 +149,9 @@ block ; ``` -A *block* consists of an optional *statement_list* ([§12.3.2](statements.md#1232-statement-lists)), enclosed in braces. If the statement list is omitted, the block is said to be empty. +A *block* consists of an optional *statement_list* ([§13.3.2](statements.md#1332-statement-lists)), enclosed in braces. If the statement list is omitted, the block is said to be empty. -A block may contain declaration statements ([§12.6](statements.md#126-declaration-statements)). The scope of a local variable or constant declared in a block is the block. +A block may contain declaration statements ([§13.6](statements.md#136-declaration-statements)). The scope of a local variable or constant declared in a block is the block. A block is executed as follows: @@ -162,14 +162,14 @@ The statement list of a block is reachable if the block itself is reachable. The end point of a block is reachable if the block is empty or if the end point of the statement list is reachable. -A *block* that contains one or more `yield` statements ([§12.15](statements.md#1215-the-yield-statement)) is called an iterator block. Iterator blocks are used to implement function members as iterators ([§14.14](classes.md#1414-iterators)). Some additional restrictions apply to iterator blocks: +A *block* that contains one or more `yield` statements ([§13.15](statements.md#1315-the-yield-statement)) is called an iterator block. Iterator blocks are used to implement function members as iterators ([§15.14](classes.md#1514-iterators)). Some additional restrictions apply to iterator blocks: - It is a compile-time error for a `return` statement to appear in an iterator block (but `yield return` statements are permitted). -- It is a compile-time error for an iterator block to contain an unsafe context ([§22.2](unsafe-code.md#222-unsafe-contexts)). An iterator block always defines a safe context, even when its declaration is nested in an unsafe context. +- It is a compile-time error for an iterator block to contain an unsafe context ([§23.2](unsafe-code.md#232-unsafe-contexts)). An iterator block always defines a safe context, even when its declaration is nested in an unsafe context. -### 12.3.2 Statement lists +### 13.3.2 Statement lists -A ***statement list*** consists of one or more statements written in sequence. Statement lists occur in *block*s ([§12.3](statements.md#123-blocks)) and in *switch_block*s ([§12.8.3](statements.md#1283-the-switch-statement)). +A ***statement list*** consists of one or more statements written in sequence. Statement lists occur in *block*s ([§13.3](statements.md#133-blocks)) and in *switch_block*s ([§13.8.3](statements.md#1383-the-switch-statement)). ```ANTLR statement_list @@ -187,7 +187,7 @@ A statement in a statement list is reachable if at least one of the following is The end point of a statement list is reachable if the end point of the last statement in the list is reachable. -## 12.4 The empty statement +## 13.4 The empty statement An *empty_statement* does nothing. @@ -232,7 +232,7 @@ Execution of an empty statement simply transfers control to the end point of the > > *end example* -## 12.5 Labeled statements +## 13.5 Labeled statements A *labeled_statement* permits a statement to be prefixed by a label. Labeled statements are permitted in blocks, but are not permitted as embedded statements. @@ -244,7 +244,7 @@ labeled_statement A labeled statement declares a label with the name given by the *identifier*. The scope of a label is the whole block in which the label is declared, including any nested blocks. It is a compile-time error for two labels with the same name to have overlapping scopes. -A label can be referenced from `goto` statements ([§12.10.4](statements.md#12104-the-goto-statement)) within the scope of the label. +A label can be referenced from `goto` statements ([§13.10.4](statements.md#13104-the-goto-statement)) within the scope of the label. > *Note*: This means that `goto` statements can transfer control within blocks and out of blocks, but never into blocks. *end note* @@ -274,85 +274,48 @@ Execution of a labeled statement corresponds exactly to execution of the stateme In addition to the reachability provided by normal flow of control, a labeled statement is reachable if the label is referenced by a reachable `goto` statement, unless the `goto` statement is inside the `try` block or a `catch` block of a *try_statement* that includes a `finally` block whose end point is unreachable, and the labeled statement is outside the *try_statement*. -## 12.6 Declaration statements +## 13.6 Declaration statements -### 12.6.1 General +### 13.6.1 General -A *declaration_statement* declares a local variable, local constant, or local function. Declaration statements are permitted in blocks, but are not permitted as embedded statements. +A *declaration_statement* declares one or more local variables, one or more local constants, or a local function. Declaration statements are permitted in blocks and switch blocks, but are not permitted as embedded statements. ```ANTLR declaration_statement : local_variable_declaration ';' | local_constant_declaration ';' - | local_function_declaration + | local_function_declaration ; ``` -A local variable is declared using a *local_variable_declaration* ([§12.6.2](statements.md#1262-local-variable-declarations)). A local constant is declared using a *local_constant_declaration* ([§12.6.3](statements.md#1263-local-constant-declarations)). A local function is declared using a *local_function_declaration* ([§12.6.4](statements.md#1264-local-function-declarations)). +A local variable is declared using a *local_variable_declaration* ([§13.6.2](statements.md#1362-local-variable-declarations)). A local constant is declared using a *local_constant_declaration* ([§13.6.3](statements.md#1363-local-constant-declarations)). A local function is declared using a *local_function_declaration* ([§13.6.4](statements.md#1364-local-function-declarations)). -### 12.6.2 Local variable declarations +The declared names are introduced into the nearest enclosing declaration space ([§7.3](basic-concepts.md#73-declarations)). + +### 13.6.2 Local variable declarations A *local_variable_declaration* declares one or more local variables. ```ANTLR local_variable_declaration - : local_variable_type local_variable_declarators - ; - -local_variable_type - : type - | 'var' - ; - -local_variable_declarators - : local_variable_declarator - | local_variable_declarators ',' local_variable_declarator - ; - -local_variable_declarator - : identifier - | identifier '=' local_variable_initializer - ; - -local_variable_initializer - : expression - | array_initializer - | stackalloc_initializer // unsafe code support + : implicitly_typed_local_variable_declaration + | explicitly_typed_local_variable_declaration + | ref_local_variable_declaration ; ``` -*stackalloc_initializer* ([§22.9](unsafe-code.md#229-stack-allocation)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). - -The *local_variable_type* of a *local_variable_declaration* either directly specifies the type of the variables introduced by the declaration, or indicates with the identifier `var` that the type should be inferred based on an initializer. The type is followed by a list of *local_variable_declarator*s, each of which introduces a new variable. A *local_variable_declarator* consists of an *identifier* that names the variable, optionally followed by an “`=`” token and a *local_variable_initializer* that gives the initial value of the variable. - -In the context of a local variable declaration, the identifier `var` acts as a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)).When the *local_variable_type* is specified as `var` and no type named `var` is in scope, the declaration is an ***implicitly typed local variable declaration***, whose type is inferred from the type of the associated initializer expression. Implicitly typed local variable declarations are subject to the following restrictions: - -- The *local_variable_declaration* cannot include multiple *local_variable_declarator*s. -- The *local_variable_declarator* shall include a *local_variable_initializer*. -- The *local_variable_initializer* shall be an *expression*. -- The initializer *expression* shall have a compile-time type. -- The initializer *expression* cannot refer to the declared variable itself +Local variable declarations fall into one of the three categories: implicitly typed, explicitly typed, and ref local. -> *Example*: The following are incorrect implicitly typed local variable declarations: -> -> -> ```csharp -> var x; // Error, no initializer to infer type from -> var y = {1, 2, 3}; // Error, array initializer not permitted -> var z = null; // Error, null does not have a type -> var u = x => x + 1; // Error, anonymous functions do not have a type -> var v = v++; // Error, initializer cannot refer to v itself -> ``` -> -> *end example* +Implicitly typed declarations contain the contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)) `var` resulting in a syntactic ambiguity between the three categories which is resolved as follows: -The value of a local variable is obtained in an expression using a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)). A local variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) at each location where its value is obtained. +- If there is no type named `var` in scope and the input matches *implicitly_typed_local_variable_declaration* then it is chosen; +- Otherwise if a type named `var` is in scope then *implicitly_typed_local_variable_declaration* is not considered as a possible match. -The scope of a local variable declared in a *local_variable_declaration* is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the *local_variable_declarator* of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name. +Within a *local_variable_declaration* each variable is introduced by a ***declarator***, which is one of *implicitly_typed_local_variable_declarator*, *explicitly_typed_local_variable_declarator* or *ref_local_variable_declarator* for impicitly typed, explicitly typed and ref local variables respectively. The declarator defines the name (*identifier*) and initial value, if any, of the introduced variable. -A local variable declaration that declares multiple variables is equivalent to multiple declarations of single variables with the same type. Furthermore, a variable initializer in a local variable declaration corresponds exactly to an assignment statement that is inserted immediately after the declaration. +If there are multiple declarators in a declaration then they are processed, including any initializing expressions, in order left to right ([§9.4.4.5](variables.md#9445-declaration-statements)). -> *Example*: The example +> *Note*: For a *local_variable_declaration* not occuring as a *for_initializer* ([§13.9.4](statements.md#1394-the-for-statement)) or *resource_acquisition* ([§13.14](statements.md#1314-the-using-statement)) this left to right order is equivalent to each declarator being within a separate *local_variable_declaration*. For example: > > > ```csharp @@ -362,47 +325,135 @@ A local variable declaration that declares multiple variables is equivalent to m > } > ``` > -> corresponds exactly to +> is equivalent to: > > > ```csharp > void F() > { -> int x; x = 1; +> int x = 1; > int y; -> int z; z = x * 2; +> int z = x * 2; > } > ``` > -> *end example* +> *end note* + +The value of a local variable is obtained in an expression using a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)). A local variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) at each location where its value is obtained. Each local variable introduced by a *local_variable_declaration* is *initially unassigned* ([§9.4.3](variables.md#943-initially-unassigned-variables)). If a declarator has an initializing expression then the introduced local variable is classified as *assigned* at the end of the declarator ([§9.4.4.5](variables.md#9445-declaration-statements)). + +The scope of a local variable introduced by a *local_variable_declaration* is defined as follows ([§7.7](basic-concepts.md#77-scopes)): + +- If the declaration occurs as a *for_initializer* then the scope is the *for_initializer*, *for_condition*, *for_iterator*, and *embedded_statement* ([§13.9.4](statements.md#1394-the-for-statement)); +- If the declaration occurs as a *resource_acquisition* then the scope is the outermost block of the semantically equivalent expansion of the *using_statement* ([§13.14](statements.md#1314-the-using-statement)); +- Otherwise the scope is the block in which the declaration occurs. + +It is an error to refer to a local variable by name in a textual position that precedes its declarator, or within any initializing expression within its declarator. Within the scope of a local variable, it is a compile-time error to declare another local variable, local function or constant with the same name. + +The ref-safe-context ([§9.7.2](variables.md#972-ref-safe-contexts)) of a ref local variable is the ref-safe-context of its initializing *variable_reference*. The ref-safe-context of non-ref local variables is *declaration-block*. + +#### 13.6.2.1 Implicitly typed local variable declarations + +```ANTLR +implicitly_typed_local_variable_declaration + : 'var' implicitly_typed_local_variable_declarator + | ref_kind 'var' ref_local_variable_declarator + ; -In an implicitly typed local variable declaration, the type of the local variable being declared is taken to be the same as the type of the expression used to initialize the variable. +implicitly_typed_local_variable_declarator + : identifier '=' expression + ; +``` + +An *implicity_typed_local_variable_declaration* introduces a single local variable, *identifier*. The *expression* or *variable_reference* must have a compile-time type, `T`. The first alternative declares a variable with type `T` and an initial value of *expression*. The second alternative declares a ref variable with type `ref T` and an initial value of `ref` *variable_reference*. > *Example*: > -> +> > ```csharp > var i = 5; > var s = "Hello"; > var d = 1.0; > var numbers = new int[] {1, 2, 3}; > var orders = new Dictionary(); +> ref var j = ref i; +> ref readonly var k = ref i; > ``` > > The implicitly typed local variable declarations above are precisely equivalent to the following explicitly typed declarations: > -> +> > ```csharp > int i = 5; > string s = "Hello"; > double d = 1.0; > int[] numbers = new int[] {1, 2, 3}; > Dictionary orders = new Dictionary(); +> ref int j = ref i; +> ref readonly int k = ref i; +> ``` +> +> The following are incorrect implicitly typed local variable declarations: +> +> +> ```csharp +> var x; // Error, no initializer to infer type from +> var y = {1, 2, 3}; // Error, array initializer not permitted +> var z = null; // Error, null does not have a type +> var u = x => x + 1; // Error, anonymous functions do not have a type +> var v = v++; // Error, initializer cannot refer to v itself > ``` > > *end example* -### 12.6.3 Local constant declarations +#### 13.6.2.2 Explicitly typed local variable declarations + +```ANTLR +explicitly_typed_local_variable_declaration + : type explicitly_typed_local_variable_declarators + ; + +explicitly_typed_local_variable_declarators + : explicitly_typed_local_variable_declarator + (',' explicitly_typed_local_variable_declarator)* + ; + +explicitly_typed_local_variable_declarator + : identifier ('=' local_variable_initializer)? + ; + +local_variable_initializer + : expression + | array_initializer + ; +``` + +An *explicity_typed_local_variable_declaration* introduces one or more local variables with the specified *type*. + +If a *local_variable_initializer* is present then its type must be appropriate according to the rules of simple assignment ([§12.21.2](expressions.md#12212-simple-assignment)) or array initialization ([§17.7](arrays.md#177-array-initializers)) and its value is assigned as the initial value of the variable. + +#### 13.6.2.3 Ref local variable declarations + +```ANTLR +ref_local_variable_declaration + : ref_kind type ref_local_variable_declarators + ; + +ref_local_variable_declarators + : ref_local_variable_declarator (',' ref_local_variable_declarator)* + ; + +ref_local_variable_declarator + : identifier '=' 'ref' variable_reference + ; +``` + +The initializing *variable_reference* must have type *type* and meet the same requirements as for a *ref assignment* ([§12.21.3](expressions.md#12213-ref-assignment)). + +If *ref_kind* is `ref readonly`, the *identifier*(s) being declared are references to variables that are treated as read-only. Otherwise, if *ref_kind* is `ref`, the *identifier*(s) being declared are references to variables that shall be writable. + +It is a compile-time error to declare a ref local variable, or a variable of a `ref struct` type, within a method declared with the *method_modifier* `async`, or within an iterator ([§15.14](classes.md#1514-iterators)). + +### 13.6.3 Local constant declarations A *local_constant_declaration* declares one or more local constants. @@ -420,32 +471,41 @@ constant_declarator ; ``` -The *type* of a *local_constant_declaration* specifies the type of the constants introduced by the declaration. The type is followed by a list of *constant_declarator*s, each of which introduces a new constant. A *constant_declarator* consists of an *identifier* that names the constant, followed by an “`=`” token, followed by a *constant_expression* ([§11.21](expressions.md#1121-constant-expressions)) that gives the value of the constant. +The *type* of a *local_constant_declaration* specifies the type of the constants introduced by the declaration. The type is followed by a list of *constant_declarator*s, each of which introduces a new constant. A *constant_declarator* consists of an *identifier* that names the constant, followed by an “`=`” token, followed by a *constant_expression* ([§12.23](expressions.md#1223-constant-expressions)) that gives the value of the constant. -The *type* and *constant_expression* of a local constant declaration shall follow the same rules as those of a constant member declaration ([§14.4](classes.md#144-constants)). +The *type* and *constant_expression* of a local constant declaration shall follow the same rules as those of a constant member declaration ([§15.4](classes.md#154-constants)). -The value of a local constant is obtained in an expression using a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)). +The value of a local constant is obtained in an expression using a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)). -The scope of a local constant is the block in which the declaration occurs. It is an error to refer to a local constant in a textual position that precedes the end of its *constant_declarator*. Within the scope of a local constant, it is a compile-time error to declare another local variable or constant with the same name. +The scope of a local constant is the block in which the declaration occurs. It is an error to refer to a local constant in a textual position that precedes the end of its *constant_declarator*. Within the scope of a local constant, it is a compile-time error to declare another local variable, local function or constant with the same name. A local constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same type. -### 12.6.4 Local function declarations +### 13.6.4 Local function declarations A *local_function_declaration* declares a local function. ```ANTLR local_function_declaration - : local_function_header local_function_body + : local_function_modifier* return_type local_function_header + local_function_body + | ref_local_function_modifier* ref_kind ref_return_type + local_function_header ref_local_function_body ; local_function_header - : local_function_modifier* return_type identifier type_parameter_list? - ( formal_parameter_list? ) type_parameter_constraints_clause* + : identifier '(' formal_parameter_list? ')' + | identifier type_parameter_list '(' formal_parameter_list? ')' + type_parameter_constraints_clause* ; + local_function_modifier - : 'async' - | 'unsafe' + : ref_local_function_modifier + | 'async' + ; + +ref_local_function_modifier + : unsafe_modifier // unsafe code support ; local_function_body @@ -453,9 +513,14 @@ local_function_body | '=>' null_conditional_invocation_expression ';' | '=>' expression ';' ; + +ref_local_function_body + : block + | '=>' 'ref' variable_reference ';' + ; ``` -Grammar note: When recognising a *local_function_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen. ([§14.6.1](classes.md#1461-general)) +Grammar note: When recognising a *local_function_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen. ([§15.6.1](classes.md#1561-general)) > *Example*: There are two common use cases for local functions: iterator methods and async methods. In iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. In async methods, any exceptions are only observed when the returned Task is awaited. The following example demonstrates separating parameter validation from the iterator implementation using a local function: > @@ -465,15 +530,18 @@ Grammar note: When recognising a *local_function_body* if both the *null_conditi > { > if (start < 'a' || start > 'z') > { -> throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter"); +> throw new ArgumentOutOfRangeException(paramName: nameof(start), +> message: "start must be a letter"); > } > if (end < 'a' || end > 'z') > { -> throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter"); +> throw new ArgumentOutOfRangeException(paramName: nameof(end), +> message: "end must be a letter"); > } > if (end <= start) > { -> throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}"); +> throw new ArgumentException( +> $"{nameof(end)} must be greater than {nameof(start)}"); > } > return AlphabetSubsetImplementation(); > @@ -489,17 +557,23 @@ Grammar note: When recognising a *local_function_body* if both the *null_conditi > > *end example* -Unless specified otherwise below, the semantics of all grammar elements is the same as for *method_declaration* ([§14.6.1](classes.md#1461-general)), read in the context of a local function instead of a method. +Unless specified otherwise below, the semantics of all grammar elements is the same as for *method_declaration* ([§15.6.1](classes.md#1561-general)), read in the context of a local function instead of a method. + +The *identifier* of a *local_function_declaration* must be unique in its declared block scope, including any enclosing local variable declaration spaces. One consequence of this is that overloaded *local_function_declaration*s are not allowed. -The *identifier* of a *local_function_declaration* must be unique in its declared block scope. One consequence of this is that overloaded *local_function_declaration*s are not allowed. +A *local_function_declaration* may include one `async` ([§15.15](classes.md#1515-async-functions)) modifier and one `unsafe` ([§23.1](unsafe-code.md#231-general)) modifier. If the declaration includes the `async` modifier then the return type shall be `void` or a `«TaskType»` type ([§15.15.1](classes.md#15151-general)). The `unsafe` modifier uses the containing lexical scope. The `async` modifier does not use the containing lexical scope. It is a compile-time error for *type_parameter_list* or *formal_parameter_list* to contain *attributes*. -A *local_function_declaration* may include one `async` ([§14.15](classes.md#1415-async-functions)) modifier and one `unsafe` ([§22.1](unsafe-code.md#221-general)) modifier. If the declaration includes the `async` modifier then the return type shall be `void` or a task type ([§14.15.1](classes.md#14151-general)). The `unsafe` modifier uses the containing lexical scope. The `async` modifier does not use the containing lexical scope. It is a compile-time error for *type_parameter_list* or *formal_parameter_list* to contain *attributes*. +A local function is declared at block scope, and that function may capture variables from the enclosing scopes. It is a compile-time error if a captured variable is read by the body of the local function but is not definitely assigned before each call to the function. The compiler shall determine which variables are definitely assigned on return ([§9.4.4.33](variables.md#94433-rules-for-variables-in-local-functions)). -A local function is declared at block scope, and that function may capture variables from the enclosing scope. It is a compile-time error if a captured variable is read by the body of the local function but is not definitely assigned before each call to the function. The compiler shall determine which variables are definitely assigned on return ([§9.4.4.33](variables.md#94433-rules-for-variables-in-local-functions)). +When the type of `this` is a struct type, it is a compile-time error for the body of a local function to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule only prohibits such access and does not affect whether member lookup results in a member of the struct. + +It is a compile-time error for the body of the local function to contain a `goto` statement, a `break` statement, or a `continue` statement whose target is outside the body of the local function. + +> *Note*: the above rules for `this` and `goto` mirror the rules for anonymous functions in [§12.19.3](expressions.md#12193-anonymous-function-bodies). *end note* A local function may be called from a lexical point prior to its declaration. However, it is a compile-time error for the function to be declared lexically prior to the declaration of a variable used in the local function ([§7.7](basic-concepts.md#77-scopes)). -It is a compile-time error for a local function to declare a parameter or local variable with the same name as one declared in the enclosing scope. +It is a compile-time error for a local function to declare a parameter, type parameter or local variable with the same name as one declared in any enclosing local variable declaration space. Local function bodies are always reachable. The endpoint of a local function declaration is reachable if the beginning point of the local function declaration is reachable. @@ -507,26 +581,30 @@ Local function bodies are always reachable. The endpoint of a local function dec > > > ```csharp -> class C +> class C > { -> int M() +> int M() > { > L(); -> return 1; // Beginning of L is not reachable -> int L() -> { -> return 2; // The body of L is reachable +> return 1; +> +> // Beginning of L is not reachable +> int L() +> { +> // The body of L is reachable +> return 2; > } -> return 3; // Not reachable, because beginning point of L is not reachable +> // Not reachable, because beginning point of L is not reachable +> return 3; > } > } > ``` > > In other words, the location of a local function declaration doesn’t affect the reachability of any statements in the containing function. *end example* -If the argument to a local function is dynamic, the function to be called must be resolved at compile time, not runtime. +If the type of the argument to a local function is `dynamic`, the function to be called must be resolved at compile time, not runtime. -## 12.7 Expression statements +## 13.7 Expression statements An *expression_statement* evaluates a given expression. The value computed by the expression, if any, is discarded. @@ -554,9 +632,9 @@ Not all expressions are permitted as statements. Execution of an *expression_statement* evaluates the contained expression and then transfers control to the end point of the *expression_statement*. The end point of an *expression_statement* is reachable if that *expression_statement* is reachable. -## 12.8 Selection statements +## 13.8 Selection statements -### 12.8.1 General +### 13.8.1 General Selection statements select one of a number of possible statements for execution based on the value of some expression. @@ -567,7 +645,7 @@ selection_statement ; ``` -### 12.8.2 The if statement +### 13.8.2 The if statement The `if` statement selects a statement for execution based on the value of a Boolean expression. @@ -611,7 +689,7 @@ An `else` part is associated with the lexically nearest preceding `if` that is a An `if` statement is executed as follows: -- The *boolean_expression* ([§11.22](expressions.md#1122-boolean-expressions)) is evaluated. +- The *boolean_expression* ([§12.24](expressions.md#1224-boolean-expressions)) is evaluated. - If the Boolean expression yields `true`, control is transferred to the first embedded statement. When and if control reaches the end point of that statement, control is transferred to the end point of the `if` statement. - If the Boolean expression yields `false` and if an `else` part is present, control is transferred to the second embedded statement. When and if control reaches the end point of that statement, control is transferred to the end point of the `if` statement. - If the Boolean expression yields `false` and if an `else` part is not present, control is transferred to the end point of the `if` statement. @@ -622,7 +700,7 @@ The second embedded statement of an `if` statement, if present, is reachable if The end point of an `if` statement is reachable if the end point of at least one of its embedded statements is reachable. In addition, the end point of an `if` statement with no `else` part is reachable if the `if` statement is reachable and the Boolean expression does not have the constant value `true`. -### 12.8.3 The switch statement +### 13.8.3 The switch statement The `switch` statement selects for execution a statement list having an associated switch label that corresponds to the value of the switch expression. @@ -640,29 +718,54 @@ switch_section ; switch_label - : 'case' constant_expression ':' + : 'case' pattern case_guard? ':' | 'default' ':' ; + +case_guard + : 'when' expression + ; ``` -A *switch_statement* consists of the keyword `switch`, followed by a parenthesized expression (called the ***switch expression***), followed by a *switch_block*. The *switch_block* consists of zero or more *switch_section*s, enclosed in braces. Each *switch_section* consists of one or more *switch_label*s followed by a *statement_list* ([§12.3.2](statements.md#1232-statement-lists)). +A *switch_statement* consists of the keyword `switch`, followed by a parenthesized expression (called the ***switch expression***), followed by a *switch_block*. The *switch_block* consists of zero or more *switch_section*s, enclosed in braces. Each *switch_section* consists of one or more *switch_label*s followed by a *statement_list* ([§13.3.2](statements.md#1332-statement-lists)). Each *switch_label* containing `case` has an associated pattern ([§11](patterns.md#11-patterns-and-pattern-matching)) against which the value of the switch expression is tested. If *case_guard* is present, its expression shall be implicitly convertible to the type `bool` and that expression is evaluated as an additional condition for the case to be considered satisfied. The ***governing type*** of a `switch` statement is established by the switch expression. - If the type of the switch expression is `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `bool`, `string`, or an *enum_type*, or if it is the nullable value type corresponding to one of these types, then that is the governing type of the `switch` statement. -- Otherwise, exactly one user-defined implicit conversion shall exist from the type of the switch expression to one of the following possible governing types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `string`, or, a nullable value type corresponding to one of those types. -- Otherwise, a compile-time error occurs. - -The constant expression of each `case` label shall denote a value of a type that is implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the governing type of the `switch` statement. A compile-time error occurs if two or more case labels in the same switch statement specify the same constant value. +- Otherwise, if exactly one user-defined implicit conversion exists from the type of the switch expression to one of the following possible governing types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `string`, or, a nullable value type corresponding to one of those types, then the converted type is the governing type of the `switch` statement. +- Otherwise, the governing type of the `switch` statement is the type of the switch expression. It is an error if no such type exists. There can be at most one `default` label in a `switch` statement. +It is an error if the pattern of any switch label is not *applicable* ([§11.2.1](patterns.md#1121-general)) to the type of the input expression. + +It is an error if the pattern of any switch label is *subsumed* by ([§11.3](patterns.md#113-pattern-subsumption)) the set of patterns of earlier switch labels of the switch statement that do not have a case guard or whose case guard is a constant expression with the value true. + +> *Example*: +> +> ```csharp +> switch (shape) +> { +> case var x: +> break; +> case var _: // error: pattern subsumed, as previous case always matches +> break; +> default: +> break; // warning: unreachable, all possible values already handled. +> } +> ``` +> +> *end example* + A `switch` statement is executed as follows: - The switch expression is evaluated and converted to the governing type. -- If one of the constants specified in a `case` label in the same `switch` statement is equal to the value of the switch expression, control is transferred to the statement list following the matched `case` label. -- If none of the constants specified in `case` labels in the same `switch` statement is equal to the value of the switch expression, and if a `default` label is present, control is transferred to the statement list following the `default` label. -- If none of the constants specified in `case` labels in the same `switch` statement is equal to the value of the switch expression, and if no `default` label is present, control is transferred to the end point of the `switch` statement. +- Control is transferred according to the value of the converted switch expression: + - The lexically first pattern in the set of `case` labels in the same `switch` statement that matches the value of the switch expression, and for which the guard expression is either absent or evaluates to true, causes control to be transferred to the statement list following the matched `case` label. + - Otherwise, if a `default` label is present, control is transferred to the statement list following the `default` label. + - Otherwise, control is transferred to the end point of the `switch` statement. + +> *Note*: The order in which patterns are matched at runtime is not defined. A compiler is permitted (but not required) to match patterns out of order, and to reuse the results of already matched patterns to compute the result of matching of other patterns. Nevertheless, the compiler is required to determine the lexically first pattern that matches the expression and for which the guard clause is either absent or evaluates to `true`. *end note* If the end point of the statement list of a switch section is reachable, a compile-time error occurs. This is known as the “no fall through” rule. @@ -825,27 +928,56 @@ Multiple labels are permitted in a *switch_section*. -> *Note*: Like the string equality operators ([§11.11.8](expressions.md#11118-string-equality-operators)), the `switch` statement is case sensitive and will execute a given switch section only if the switch expression string exactly matches a `case` label constant. *end note* +> *Note*: Like the string equality operators ([§12.12.8](expressions.md#12128-string-equality-operators)), the `switch` statement is case sensitive and will execute a given switch section only if the switch expression string exactly matches a `case` label constant. *end note* When the governing type of a `switch` statement is `string` or a nullable value type, the value `null` is permitted as a `case` label constant. -The *statement_list*s of a *switch_block* may contain declaration statements ([§12.6](statements.md#126-declaration-statements)). The scope of a local variable or constant declared in a switch block is the switch block. +The *statement_list*s of a *switch_block* may contain declaration statements ([§13.6](statements.md#136-declaration-statements)). The scope of a local variable or constant declared in a switch block is the switch block. -The statement list of a given switch section is reachable if the `switch` statement is reachable and at least one of the following is true: +A switch label is reachable if at least one of the following is true: -- The switch expression is a non-constant value. -- The switch expression is a constant value that matches a `case` label in the switch section. -- The switch expression is a constant value that doesn’t match any `case` label, and the switch section contains the `default` label. -- A switch label of the switch section is referenced by a reachable `goto case` or `goto default` statement. +- The switch expression is a constant value and either + - the label is a `case` whose pattern *would match* ([§11.2.1](patterns.md#1121-general)) that value, and label’s guard is either absent or not a constant expression with the value false; or + - it is a `default` label, and no switch section contains a case label whose pattern would match that value, and whose guard is either absent or a constant expression with the value true. +- The switch expression is not a constant value and either + - the label is a `case` without a guard or with a guard whose value is not the constant false; or + - it is a `default` label and + - the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true, is not *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the switch governing type; or + - the switch governing type is a nullable type and the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true does not contain a pattern that would match the value `null`. +- The switch label is referenced by a reachable `goto case` or `goto default` statement. -The end point of a `switch` statement is reachable if at least one of the following is true: +The statement list of a given switch section is reachable if the `switch` statement is reachable and the switch section contains a reachable switch label. + +The end point of a `switch` statement is reachable if the switch statement is reachable and at least one of the following is true: - The `switch` statement contains a reachable `break` statement that exits the `switch` statement. -- The `switch` statement is reachable, the switch expression is a non-constant value, and no `default` label is present. -- The `switch` statement is reachable, the switch expression is a constant value that doesn’t match any `case` label, and no `default` label is present. +- No `default` label is present and either + - The switch expression is a non-constant value, and the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true, is not *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the switch governing type. + - The switch expression is a non-constant value of a nullable type, and no pattern appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true would match the value `null`. + - The switch expression is a constant value and no `case` label without a guard or whose guard is the constant true would match that value. -## 12.9 Iteration statements +> *Example*: The following code shows a succinct use of the `when` clause: +> +> ```csharp +> static object CreateShape(string shapeDescription) +> { +> switch (shapeDescription) +> { +> case "circle": +> return new Circle(2); +> … +> case var o when string.IsNullOrWhiteSpace(o): +> return null; +> default: +> return "invalid shape description"; +> } +> } +> ``` +> +> The var case matches `null`, the empty string, or any string that contains only white space. *end example* -### 12.9.1 General +## 13.9 Iteration statements + +### 13.9.1 General Iteration statements repeatedly execute an embedded statement. @@ -858,7 +990,7 @@ iteration_statement ; ``` -### 12.9.2 The while statement +### 13.9.2 The while statement The `while` statement conditionally executes an embedded statement zero or more times. @@ -870,11 +1002,11 @@ while_statement A `while` statement is executed as follows: -- The *boolean_expression* ([§11.22](expressions.md#1122-boolean-expressions)) is evaluated. +- The *boolean_expression* ([§12.24](expressions.md#1224-boolean-expressions)) is evaluated. - If the Boolean expression yields `true`, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a `continue` statement), control is transferred to the beginning of the `while` statement. - If the Boolean expression yields `false`, control is transferred to the end point of the `while` statement. -Within the embedded statement of a `while` statement, a `break` statement ([§12.10.2](statements.md#12102-the-break-statement)) may be used to transfer control to the end point of the `while` statement (thus ending iteration of the embedded statement), and a `continue` statement ([§12.10.3](statements.md#12103-the-continue-statement)) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the `while` statement). +Within the embedded statement of a `while` statement, a `break` statement ([§13.10.2](statements.md#13102-the-break-statement)) may be used to transfer control to the end point of the `while` statement (thus ending iteration of the embedded statement), and a `continue` statement ([§13.10.3](statements.md#13103-the-continue-statement)) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the `while` statement). The embedded statement of a `while` statement is reachable if the `while` statement is reachable and the Boolean expression does not have the constant value `false`. @@ -883,7 +1015,7 @@ The end point of a `while` statement is reachable if at least one of the followi - The `while` statement contains a reachable `break` statement that exits the `while` statement. - The `while` statement is reachable and the Boolean expression does not have the constant value `true`. -### 12.9.3 The do statement +### 13.9.3 The do statement The `do` statement conditionally executes an embedded statement one or more times. @@ -896,9 +1028,9 @@ do_statement A `do` statement is executed as follows: - Control is transferred to the embedded statement. -- When and if control reaches the end point of the embedded statement (possibly from execution of a `continue` statement), the *boolean_expression* ([§11.22](expressions.md#1122-boolean-expressions)) is evaluated. If the Boolean expression yields `true`, control is transferred to the beginning of the `do` statement. Otherwise, control is transferred to the end point of the `do` statement. +- When and if control reaches the end point of the embedded statement (possibly from execution of a `continue` statement), the *boolean_expression* ([§12.24](expressions.md#1224-boolean-expressions)) is evaluated. If the Boolean expression yields `true`, control is transferred to the beginning of the `do` statement. Otherwise, control is transferred to the end point of the `do` statement. -Within the embedded statement of a `do` statement, a `break` statement ([§12.10.2](statements.md#12102-the-break-statement)) may be used to transfer control to the end point of the `do` statement (thus ending iteration of the embedded statement), and a `continue` statement ([§12.10.3](statements.md#12103-the-continue-statement)) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the `do` statement). +Within the embedded statement of a `do` statement, a `break` statement ([§13.10.2](statements.md#13102-the-break-statement)) may be used to transfer control to the end point of the `do` statement (thus ending iteration of the embedded statement), and a `continue` statement ([§13.10.3](statements.md#13103-the-continue-statement)) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the `do` statement). The embedded statement of a `do` statement is reachable if the `do` statement is reachable. @@ -907,7 +1039,7 @@ The end point of a `do` statement is reachable if at least one of the following - The `do` statement contains a reachable `break` statement that exits the `do` statement. - The end point of the embedded statement is reachable and the Boolean expression does not have the constant value `true`. -### 12.9.4 The for statement +### 13.9.4 The for statement The `for` statement evaluates a sequence of initialization expressions and then, while a condition is true, repeatedly executes an embedded statement and evaluates a sequence of iteration expressions. @@ -935,11 +1067,11 @@ statement_expression_list ; ``` -The *for_initializer*, if present, consists of either a *local_variable_declaration* ([§12.6.2](statements.md#1262-local-variable-declarations)) or a list of *statement_expression*s ([§12.7](statements.md#127-expression-statements)) separated by commas. The scope of a local variable declared by a *for_initializer* starts at the *local_variable_declarator* for the variable and extends to the end of the embedded statement. The scope includes the *for_condition* and the *for_iterator*. +The *for_initializer*, if present, consists of either a *local_variable_declaration* ([§13.6.2](statements.md#1362-local-variable-declarations)) or a list of *statement_expression*s ([§13.7](statements.md#137-expression-statements)) separated by commas. The scope of a local variable declared by a *for_initializer* is the *for_initializer*, *for_condition*, *for_iterator*, and *embedded_statement*. -The *for_condition*, if present, shall be a *boolean_expression* ([§11.22](expressions.md#1122-boolean-expressions)). +The *for_condition*, if present, shall be a *boolean_expression* ([§12.24](expressions.md#1224-boolean-expressions)). -The *for_iterator*, if present, consists of a list of *statement_expression*s ([§12.7](statements.md#127-expression-statements)) separated by commas. +The *for_iterator*, if present, consists of a list of *statement_expression*s ([§13.7](statements.md#137-expression-statements)) separated by commas. A `for` statement is executed as follows: @@ -948,7 +1080,7 @@ A `for` statement is executed as follows: - If the *for_condition* is not present or if the evaluation yields `true`, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a `continue` statement), the expressions of the *for_iterator*, if any, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the *for_condition* in the step above. - If the *for_condition* is present and the evaluation yields `false`, control is transferred to the end point of the `for` statement. -Within the embedded statement of a `for` statement, a `break` statement ([§12.10.2](statements.md#12102-the-break-statement)) may be used to transfer control to the end point of the `for` statement (thus ending iteration of the embedded statement), and a `continue` statement ([§12.10.3](statements.md#12103-the-continue-statement)) may be used to transfer control to the end point of the embedded statement (thus executing the *for_iterator* and performing another iteration of the `for` statement, starting with the *for_condition*). +Within the embedded statement of a `for` statement, a `break` statement ([§13.10.2](statements.md#13102-the-break-statement)) may be used to transfer control to the end point of the `for` statement (thus ending iteration of the embedded statement), and a `continue` statement ([§13.10.3](statements.md#13103-the-continue-statement)) may be used to transfer control to the end point of the embedded statement (thus executing the *for_iterator* and performing another iteration of the `for` statement, starting with the *for_condition*). The embedded statement of a `for` statement is reachable if one of the following is true: @@ -960,18 +1092,22 @@ The end point of a `for` statement is reachable if at least one of the following - The `for` statement contains a reachable `break` statement that exits the `for` statement. - The `for` statement is reachable and a *for_condition* is present and does not have the constant value `true`. -### 12.9.5 The foreach statement +### 13.9.5 The foreach statement The `foreach` statement enumerates the elements of a collection, executing an embedded statement for each element of the collection. ```ANTLR foreach_statement - : 'foreach' '(' local_variable_type identifier 'in' expression ')' - embedded_statement + : 'foreach' '(' ref_kind? local_variable_type identifier 'in' + expression ')' embedded_statement ; ``` -The *local_variable_type* and *identifier* of a `foreach` statement declare the ***iteration variable*** of the statement. If the `var` identifier is given as the *local_variable_type*, and no type named var is in scope, the iteration variable is said to be an ***implicitly typed iteration variable***, and its type is taken to be the iteration type of the `foreach` statement, as specified below. The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement. During execution of a `foreach` statement, the iteration variable represents the collection element for which an iteration is currently being performed. A compile-time error occurs if the embedded statement attempts to modify the iteration variable (via assignment or the `++` and `--` operators) or pass the iteration variable as a `ref` or `out` parameter. +The *local_variable_type* and *identifier* of a foreach statement declare the ***iteration variable*** of the statement. If the `var` identifier is given as the *local_variable_type*, and no type named `var` is in scope, the iteration variable is said to be an ***implicitly typed iteration variable***, and its type is taken to be the element type of the `foreach` statement, as specified below. + +If the *foreach_statement* contains both or neither `ref` and `readonly`, the iteration variable denotes a variable that is treated as read-only. Otherwise, if *foreach_statement* contains `ref` without `readonly`, the iteration variable denotes a variable that shall be writable. + +The iteration variable corresponds to a local variable with a scope that extends over the embedded statement. During execution of a `foreach` statement, the iteration variable represents the collection element for which an iteration is currently being performed. If the iteration variable denotes a read-only variable, a compile-time error occurs if the embedded statement attempts to modify it (via assignment or the `++` and `--` operators) or pass it as a `ref` or `out` parameter. In the following, for brevity, `IEnumerable`, `IEnumerator`, `IEnumerable` and `IEnumerator` refer to the corresponding types in the namespaces `System.Collections` and `System.Collections.Generic`. @@ -986,20 +1122,20 @@ The compile-time processing of a `foreach` statement first determines the ***col - Member lookup is performed on `E` with the identifier `Current` and no type arguments. If the member lookup produces no match, the result is an error, or the result is anything except a public instance property that permits reading, an error is produced and no further steps are taken. - Member lookup is performed on `E` with the identifier `MoveNext` and no type arguments. If the member lookup produces no match, the result is an error, or the result is anything except a method group, an error is produced and no further steps are taken. - Overload resolution is performed on the method group with an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, or its return type is not `bool`, an error is produced and no further steps are taken. - - The collection type is `X`, the enumerator type is `E`, and the iteration type is the type of the `Current` property. + - The collection type is `X`, the enumerator type is `E`, and the iteration type is the type of the `Current` property. The `Current` property may include the `ref` modifier, in which case, the expression returned is a *variable_reference* ([§9.5](variables.md#95-variable-references)) that is optionally read-only. - Otherwise, check for an enumerable interface: - If among all the types `Tᵢ` for which there is an implicit conversion from `X` to `IEnumerable`, there is a unique type `T` such that `T` is not `dynamic` and for all the other `Tᵢ` there is an implicit conversion from `IEnumerable` to `IEnumerable`, then the collection type is the interface `IEnumerable`, the enumerator type is the interface `IEnumerator`, and the iteration type is `T`. - Otherwise, if there is more than one such type `T`, then an error is produced and no further steps are taken. - Otherwise, if there is an implicit conversion from `X` to the `System.Collections.IEnumerable` interface, then the collection type is this interface, the enumerator type is the interface `System.Collections.IEnumerator`, and the iteration type is `object`. - Otherwise, an error is produced and no further steps are taken. -The above steps, if successful, unambiguously produce a collection type `C`, enumerator type `E` and iteration type `T`. A `foreach` statement of the form +The above steps, if successful, unambiguously produce a collection type `C`, enumerator type `E` and iteration type `T`, `ref T`, or `ref readonly T`. A `foreach` statement of the form ```csharp foreach (V v in x) «embedded_statement» ``` -is then expanded to: +is then equivalent to: ```csharp { @@ -1021,11 +1157,41 @@ is then expanded to: The variable `e` is not visible to or accessible to the expression `x` or the embedded statement or any other source code of the program. The variable `v` is read-only in the embedded statement. If there is not an explicit conversion ([§10.3](conversions.md#103-explicit-conversions)) from `T` (the iteration type) to `V` (the *local_variable_type* in the `foreach` statement), an error is produced and no further steps are taken. +When the iteration variable is a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)), a `foreach` statement of the form + +```csharp +foreach (ref V v in x) «embedded_statement» +``` + +is then equivalent to: + +```csharp +{ + E e = ((C)(x)).GetEnumerator(); + try + { + while (e.MoveNext()) + { + ref V v = ref e.Current; + «embedded_statement» + } + } + finally + { + ... // Dispose e + } +} +``` + +The variable `e` is not visible or accessible to the expression `x` or the embedded statement or any other source code of the program. The reference variable `v` is read-write in the embedded statement, but `v` shall not be ref-reassigned ([§12.21.3](expressions.md#12213-ref-assignment)). If there is not an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) from `T` (the iteration type) to `V` (the *local_variable_type* in the `foreach` statement), an error is produced and no further steps are taken. + +A `foreach` statement of the form `foreach (ref readonly V v in x) «embedded_statement»` has a similar equivalent form, but the reference variable `v` is `ref readonly` in the embedded statement, and therefore cannot be ref-reassigned or reassigned. + > *Note*: If `x` has the value `null`, a `System.NullReferenceException` is thrown at run-time. *end note* An implementation is permitted to implement a given *foreach_statement* differently; e.g., for performance reasons, as long as the behavior is consistent with the above expansion. -The placement of `v` inside the `while` loop is important for how it is captured ([§11.17.6.2](expressions.md#111762-captured-outer-variables)) by any anonymous function occurring in the *embedded_statement*. +The placement of `v` inside the `while` loop is important for how it is captured ([§12.19.6.2](expressions.md#121962-captured-outer-variables)) by any anonymous function occurring in the *embedded_statement*. > *Example*: > @@ -1143,9 +1309,9 @@ The order in which `foreach` traverses the elements of an array, is as follows: > > *end example* -## 12.10 Jump statements +## 13.10 Jump statements -### 12.10.1 General +### 13.10.1 General Jump statements unconditionally transfer control. @@ -1209,7 +1375,7 @@ Execution of jump statements is complicated by the presence of intervening `try` > > *end example* -### 12.10.2 The break statement +### 13.10.2 The break statement The `break` statement exits the nearest enclosing `switch`, `while`, `do`, `for`, or `foreach` statement. @@ -1221,9 +1387,9 @@ break_statement The target of a `break` statement is the end point of the nearest enclosing `switch`, `while`, `do`, `for`, or `foreach` statement. If a `break` statement is not enclosed by a `switch`, `while`, `do`, `for`, or `foreach` statement, a compile-time error occurs. -When multiple `switch`, `while`, `do`, `for`, or `foreach` statements are nested within each other, a `break` statement applies only to the innermost statement. To transfer control across multiple nesting levels, a `goto` statement ([§12.10.4](statements.md#12104-the-goto-statement)) shall be used. +When multiple `switch`, `while`, `do`, `for`, or `foreach` statements are nested within each other, a `break` statement applies only to the innermost statement. To transfer control across multiple nesting levels, a `goto` statement ([§13.10.4](statements.md#13104-the-goto-statement)) shall be used. -A `break` statement cannot exit a `finally` block ([§12.11](statements.md#1211-the-try-statement)). When a `break` statement occurs within a `finally` block, the target of the `break` statement shall be within the same `finally` block; otherwise a compile-time error occurs. +A `break` statement cannot exit a `finally` block ([§13.11](statements.md#1311-the-try-statement)). When a `break` statement occurs within a `finally` block, the target of the `break` statement shall be within the same `finally` block; otherwise a compile-time error occurs. A `break` statement is executed as follows: @@ -1232,7 +1398,7 @@ A `break` statement is executed as follows: Because a `break` statement unconditionally transfers control elsewhere, the end point of a `break` statement is never reachable. -### 12.10.3 The continue statement +### 13.10.3 The continue statement The `continue` statement starts a new iteration of the nearest enclosing `while`, `do`, `for`, or `foreach` statement. @@ -1244,9 +1410,9 @@ continue_statement The target of a `continue` statement is the end point of the embedded statement of the nearest enclosing `while`, `do`, `for`, or `foreach` statement. If a `continue` statement is not enclosed by a `while`, `do`, `for`, or `foreach` statement, a compile-time error occurs. -When multiple `while`, `do`, `for`, or `foreach` statements are nested within each other, a `continue` statement applies only to the innermost statement. To transfer control across multiple nesting levels, a `goto` statement ([§12.10.4](statements.md#12104-the-goto-statement)) shall be used. +When multiple `while`, `do`, `for`, or `foreach` statements are nested within each other, a `continue` statement applies only to the innermost statement. To transfer control across multiple nesting levels, a `goto` statement ([§13.10.4](statements.md#13104-the-goto-statement)) shall be used. -A `continue` statement cannot exit a `finally` block ([§12.11](statements.md#1211-the-try-statement)). When a `continue` statement occurs within a `finally` block, the target of the `continue` statement shall be within the same `finally` block; otherwise a compile-time error occurs. +A `continue` statement cannot exit a `finally` block ([§13.11](statements.md#1311-the-try-statement)). When a `continue` statement occurs within a `finally` block, the target of the `continue` statement shall be within the same `finally` block; otherwise a compile-time error occurs. A `continue` statement is executed as follows: @@ -1255,7 +1421,7 @@ A `continue` statement is executed as follows: Because a `continue` statement unconditionally transfers control elsewhere, the end point of a `continue` statement is never reachable. -### 12.10.4 The goto statement +### 13.10.4 The goto statement The `goto` statement transfers control to a statement that is marked by a label. @@ -1308,11 +1474,11 @@ The target of a `goto` *identifier* statement is the labeled statement with the > > *end note* -The target of a `goto case` statement is the statement list in the immediately enclosing `switch` statement ([§12.8.3](statements.md#1283-the-switch-statement)) which contains a`case` label with the given constant value. If the `goto case` statement is not enclosed by a `switch` statement, if the *constant_expression* is not implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the governing type of the nearest enclosing `switch` statement, or if the nearest enclosing `switch` statement does not contain a `case` label with the given constant value, a compile-time error occurs. +The target of a `goto case` statement is the statement list in the immediately enclosing `switch` statement ([§13.8.3](statements.md#1383-the-switch-statement)) which contains a `case` label with a constant pattern of the given constant value and no guard. If the `goto case` statement is not enclosed by a `switch` statement, if the nearest enclosing `switch` statement does not contain such a `case`, or if the *constant_expression* is not implicitly convertible ([§10.2](conversions.md#102-implicit-conversions)) to the governing type of the nearest enclosing `switch` statement, a compile-time error occurs. -The target of a `goto default` statement is the statement list in the immediately enclosing `switch` statement ([§12.8.3](statements.md#1283-the-switch-statement)), which contains a `default` label. If the `goto default` statement is not enclosed by a `switch` statement, or if the nearest enclosing `switch` statement does not contain a `default` label, a compile-time error occurs. +The target of a `goto default` statement is the statement list in the immediately enclosing `switch` statement ([§13.8.3](statements.md#1383-the-switch-statement)), which contains a `default` label. If the `goto default` statement is not enclosed by a `switch` statement, or if the nearest enclosing `switch` statement does not contain a `default` label, a compile-time error occurs. -A `goto` statement cannot exit a `finally` block ([§12.11](statements.md#1211-the-try-statement)). When a `goto` statement occurs within a `finally` block, the target of the `goto` statement shall be within the same `finally` block, or otherwise a compile-time error occurs. +A `goto` statement cannot exit a `finally` block ([§13.11](statements.md#1311-the-try-statement)). When a `goto` statement occurs within a `finally` block, the target of the `goto` statement shall be within the same `finally` block, or otherwise a compile-time error occurs. A `goto` statement is executed as follows: @@ -1321,34 +1487,48 @@ A `goto` statement is executed as follows: Because a `goto` statement unconditionally transfers control elsewhere, the end point of a `goto` statement is never reachable. -### 12.10.5 The return statement +### 13.10.5 The return statement -The `return` statement returns control to the current caller of the function member in which the return statement appears. +The `return` statement returns control to the current caller of the function member in which the return statement appears, optionally returning a value or a *variable_reference* ([§9.5](variables.md#95-variable-references)). ```ANTLR return_statement - : 'return' expression? ';' + : 'return' ';' + | 'return' expression ';' + | 'return' 'ref' variable_reference ';' ; ``` -A function member is said to ***compute a value*** if it is a method with a non-`void` result type ([§14.6.11](classes.md#14611-method-body)), the get accessor of a property or indexer, or a user-defined operator. Function members that do not compute a value are methods with the effective return type `void`, set accessors of properties and indexers, add and remove accessors of event, instance constructors, static constructors and finalizers. +A *return_statement* without *expression* is called a ***return-no-value***; one containing `ref` *expression* is called a ***return-by-ref***; and one containing only *expression* is called a ***return-by-value***. + +It is a compile-time error to use a return-no-value from a method declared as being returns-by-value or returns-by-ref ([§15.6.1](classes.md#1561-general)). + +It is a compile-time error to use a return-by-ref from a method declared as being returns-no-value or returns-by-value. + +It is a compile-time error to use a return-by-value from a method declared as being returns-no-value or returns-by-ref. + +It is a compile-time error to use a return-by-ref if *expression* is not a *variable_reference* or is a reference to a variable whose ref-safe-context is not caller-context ([§9.7.2](variables.md#972-ref-safe-contexts)). + +It is a compile-time error to use a return-by-ref from a method declared with the *method_modifier* `async`. + +A function member is said to ***compute a value*** if it is a method with a returns-by-value method ([§15.6.11](classes.md#15611-method-body)), a returns-by-value `get` accessor of a property or indexer, or a user-defined operator. Function members that are returns-no-value do not compute a value and are methods with the effective return type `void`, `set` accessors of properties and indexers, `add` and `remove` accessors of event, instance constructors, static constructors and finalizers. Function members that are returns-by-ref do not compute a value. -Within a function member, a `return` statement with no expression can only be used if the function member does not compute a value. Within a function member, a `return` statement with an expression can only be used if the function member computes a value. Where the `return` statement includes an expression, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) shall exist from the type of the expression to the effective return type of the containing function member. +For a return-by-value, an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) shall exist from the type of *expression* to the effective return type ([§15.6.11](classes.md#15611-method-body)) of the containing function member. For a return-by-ref, an identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) shall exist between the type of *expression* and the effective return type of the containing function member. -`return` statements can also be used in the body of anonymous function expressions ([§11.17](expressions.md#1117-anonymous-function-expressions)), and participate in determining which conversions exist for those functions ([§10.7.1](conversions.md#1071-general)). +`return` statements can also be used in the body of anonymous function expressions ([§12.19](expressions.md#1219-anonymous-function-expressions)), and participate in determining which conversions exist for those functions ([§10.7.1](conversions.md#1071-general)). -It is a compile-time error for a `return` statement to appear in a `finally` block ([§12.11](statements.md#1211-the-try-statement)). +It is a compile-time error for a `return` statement to appear in a `finally` block ([§13.11](statements.md#1311-the-try-statement)). A `return` statement is executed as follows: -- If the `return` statement specifies an expression, the expression is evaluated and its value is converted to the effective return type of the containing function by an implicit conversion. The result of the conversion becomes the result value produced by the function. +- For a return-by-value, *expression* is evaluated and its value is converted to the effective return type of the containing function by an implicit conversion. The result of the conversion becomes the result value produced by the function. For a return-by-ref, a reference to the *variable_reference* designated by *expression* becomes the result produced by the function. That result is a variable. If the enclosing method’s return-by-ref includes `readonly`, the resulting variable is read-only. - If the `return` statement is enclosed by one or more `try` or `catch` blocks with associated `finally` blocks, control is initially transferred to the `finally` block of the innermost `try` statement. When and if control reaches the end point of a `finally` block, control is transferred to the `finally` block of the next enclosing `try` statement. This process is repeated until the `finally` blocks of all enclosing `try` statements have been executed. - If the containing function is not an async function, control is returned to the caller of the containing function along with the result value, if any. -- If the containing function is an async function, control is returned to the current caller, and the result value, if any, is recorded in the return task as described in ([§14.15.2](classes.md#14152-evaluation-of-a-task-returning-async-function)). +- If the containing function is an async function, control is returned to the current caller, and the result value, if any, is recorded in the return task as described in ([§15.15.3](classes.md#15153-evaluation-of-a-task-returning-async-function)). Because a `return` statement unconditionally transfers control elsewhere, the end point of a `return` statement is never reachable. -### 12.10.6 The throw statement +### 13.10.6 The throw statement The `throw` statement throws an exception. @@ -1368,16 +1548,16 @@ When an exception is thrown, control is transferred to the first `catch` clause - In the current function member, each `try` statement that encloses the throw point is examined. For each statement `S`, starting with the innermost `try` statement and ending with the outermost `try` statement, the following steps are evaluated: - - If the `try` block of `S` encloses the throw point and if `S` has one or more `catch` clauses, the `catch` clauses are examined in order of appearance to locate a suitable handler for the exception. The first `catch` clause that specifies an exception type `T` (or a type parameter that at run-time denotes an exception type `T`) such that the run-time type of `E` derives from `T` is considered a match. If the clause contains an exception filter, the exception object is assigned to the exception variable, and the exception filter is evaluated. When a `catch` clause contains an exception filter, that `catch` clause is considered a match if the exception filter evaluates to `true`. A general `catch` ([§12.11](statements.md#1211-the-try-statement)) clause is considered a match for any exception type. If a matching `catch` clause is located, the exception propagation is completed by transferring control to the block of that `catch` clause. + - If the `try` block of `S` encloses the throw point and if `S` has one or more `catch` clauses, the `catch` clauses are examined in order of appearance to locate a suitable handler for the exception. The first `catch` clause that specifies an exception type `T` (or a type parameter that at run-time denotes an exception type `T`) such that the run-time type of `E` derives from `T` is considered a match. If the clause contains an exception filter, the exception object is assigned to the exception variable, and the exception filter is evaluated. When a `catch` clause contains an exception filter, that `catch` clause is considered a match if the exception filter evaluates to `true`. A general `catch` ([§13.11](statements.md#1311-the-try-statement)) clause is considered a match for any exception type. If a matching `catch` clause is located, the exception propagation is completed by transferring control to the block of that `catch` clause. - Otherwise, if the `try` block or a `catch` block of `S` encloses the throw point and if `S` has a `finally` block, control is transferred to the `finally` block. If the `finally` block throws another exception, processing of the current exception is terminated. Otherwise, when control reaches the end point of the `finally` block, processing of the current exception is continued. - If an exception handler was not located in the current function invocation, the function invocation is terminated, and one of the following occurs: - If the current function is non-async, the steps above are repeated for the caller of the function with a throw point corresponding to the statement from which the function member was invoked. - - If the current function is async and task-returning, the exception is recorded in the return task, which is put into a faulted or cancelled state as described in [§14.15.2](classes.md#14152-evaluation-of-a-task-returning-async-function). - - If the current function is async and `void`-returning, the synchronization context of the current thread is notified as described in [§14.15.3](classes.md#14153-evaluation-of-a-void-returning-async-function). + - If the current function is async and task-returning, the exception is recorded in the return task, which is put into a faulted or cancelled state as described in [§15.15.3](classes.md#15153-evaluation-of-a-task-returning-async-function). + - If the current function is async and `void`-returning, the synchronization context of the current thread is notified as described in [§15.15.4](classes.md#15154-evaluation-of-a-void-returning-async-function). - If the exception processing terminates all function member invocations in the current thread, indicating that the thread has no handler for the exception, then the thread is itself terminated. The impact of such termination is implementation-defined. -## 12.11 The try statement +## 13.11 The try statement The `try` statement provides a mechanism for catching exceptions that occur during execution of a block. Furthermore, the `try` statement provides the ability to specify a block of code that is always executed when control leaves the `try` statement. @@ -1418,7 +1598,7 @@ A *try_statement* consists of the keyword `try` followed by a *block*, then zero In an *exception_specifier* the *type*, or its effective base class if it is a *type_parameter*, shall be `System.Exception` or a type that derives from it. -When a `catch` clause specifies both a *class_type* and an *identifier*, an ***exception variable*** of the given name and type is declared. The exception variable corresponds to a local variable with a scope that extends over the `catch` block. During execution of the *exception_filter* and `catch` block, the exception variable represents the exception currently being handled. For purposes of definite assignment checking, the exception variable is considered definitely assigned in its entire scope. +When a `catch` clause specifies both a *class_type* and an *identifier*, an ***exception variable*** of the given name and type is declared. The exception variable is introduced into the declaration space of the *specific_catch_clause* ([§7.3](basic-concepts.md#73-declarations)). During execution of the *exception_filter* and `catch` block, the exception variable represents the exception currently being handled. For purposes of definite assignment checking, the exception variable is considered definitely assigned in its entire scope. Unless a `catch` clause includes an exception variable name, it is impossible to access the exception object in the filter and `catch` block. @@ -1430,7 +1610,7 @@ In order to locate a handler for an exception, `catch` clauses are examined in l > *Note*: Without this restriction, it would be possible to write unreachable `catch` clauses. *end note* -Within a `catch` block, a `throw` statement ([§12.10.6](statements.md#12106-the-throw-statement)) with no expression can be used to re-throw the exception that was caught by the `catch` block. Assignments to an exception variable do not alter the exception that is re-thrown. +Within a `catch` block, a `throw` statement ([§13.10.6](statements.md#13106-the-throw-statement)) with no expression can be used to re-throw the exception that was caught by the `catch` block. Assignments to an exception variable do not alter the exception that is re-thrown. > *Example*: In the following code > @@ -1488,9 +1668,9 @@ It is a compile-time error for a `break`, `continue`, or `goto` statement to tra It is a compile-time error for a `return` statement to occur in a `finally` block. -When execution reaches a`try` statement, control is transferred to the `try` block. If control reaches the end point of the `try` block without an exception being propagated, control is transferred to the `finally` block if one exists. If no `finally` block exists, control is transferred to the end point of the `try` statement. +When execution reaches a `try` statement, control is transferred to the `try` block. If control reaches the end point of the `try` block without an exception being propagated, control is transferred to the `finally` block if one exists. If no `finally` block exists, control is transferred to the end point of the `try` statement. -If an exception has been propagated, the `catch` clauses, if any, are examined in lexical order seeking the first match for the exception. The search for a matching `catch` clause continues with all enclosing blocks as described in [§12.10.6](statements.md#12106-the-throw-statement). A `catch` clause is a match if the exception type matches any *exception_specifier* and any *exception_filter* is true. A `catch` clause without an *exception_specifier* matches any exception type. The exception type matches the *exception_specifier* when the *exception_specifier* specifies the exception type or a base type of the exception type. If the clause contains an exception filter, the exception object is assigned to the exception variable, and the exception filter is evaluated. +If an exception has been propagated, the `catch` clauses, if any, are examined in lexical order seeking the first match for the exception. The search for a matching `catch` clause continues with all enclosing blocks as described in [§13.10.6](statements.md#13106-the-throw-statement). A `catch` clause is a match if the exception type matches any *exception_specifier* and any *exception_filter* is true. A `catch` clause without an *exception_specifier* matches any exception type. The exception type matches the *exception_specifier* when the *exception_specifier* specifies the exception type or a base type of the exception type. If the clause contains an exception filter, the exception object is assigned to the exception variable, and the exception filter is evaluated. If an exception has been propagated and a matching `catch` clause is found, control is transferred to the first matching `catch` block. If control reaches the end point of the `catch` block without an exception being propagated, control is transferred to the `finally` block if one exists. If no `finally` block exists, control is transferred to the end point of the `try` statement. If an exception has been propagated from the `catch` block, control transfers to the `finally` block if one exists. The exception is propagated to the next enclosing `try` statement. @@ -1498,7 +1678,7 @@ If an exception has been propagated, and no matching `catch` clause is found, co The statements of a `finally` block are always executed when control leaves a `try` statement. This is true whether the control transfer occurs as a result of normal execution, as a result of executing a `break`, `continue`, `goto`, or `return` statement, or as a result of propagating an exception out of the `try` statement. If control reaches the end point of the `finally` block without an exception being propagated, control is transferred to the end point of the `try` statement. -If an exception is thrown during execution of a `finally` block, and is not caught within the same `finally` block,the exception is propagated to the next enclosing `try` statement. If another exception was in the process of being propagated, that exception is lost. The process of propagating an exception is discussed further in the description of the `throw` statement ([§12.10.6](statements.md#12106-the-throw-statement)). +If an exception is thrown during execution of a `finally` block, and is not caught within the same `finally` block, the exception is propagated to the next enclosing `try` statement. If another exception was in the process of being propagated, that exception is lost. The process of propagating an exception is discussed further in the description of the `throw` statement ([§13.10.6](statements.md#13106-the-throw-statement)). > *Example*: In the following code > @@ -1559,7 +1739,7 @@ The end point of a `try` statement is reachable if both of the following are tru - The end point of the `try` block is reachable or the end point of at least one `catch` block is reachable. - If a `finally` block is present, the end point of the `finally` block is reachable. -## 12.12 The checked and unchecked statements +## 13.12 The checked and unchecked statements The `checked` and `unchecked` statements are used to control the ***overflow-checking context*** for integral-type arithmetic operations and conversions. @@ -1575,9 +1755,9 @@ unchecked_statement The `checked` statement causes all expressions in the *block* to be evaluated in a checked context, and the `unchecked` statement causes all expressions in the *block* to be evaluated in an unchecked context. -The `checked` and `unchecked` statements are precisely equivalent to the `checked` and `unchecked` operators ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)), except that they operate on blocks instead of expressions. +The `checked` and `unchecked` statements are precisely equivalent to the `checked` and `unchecked` operators ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)), except that they operate on blocks instead of expressions. -## 12.13 The lock statement +## 13.13 The lock statement The `lock` statement obtains the mutual-exclusion lock for a given object, executes a statement, and then releases the lock. @@ -1615,7 +1795,7 @@ except that `x` is only evaluated once. While a mutual-exclusion lock is held, code executing in the same execution thread can also obtain and release the lock. However, code executing in other threads is blocked from obtaining the lock until the lock is released. -## 12.14 The using statement +## 13.14 The using statement The `using` statement obtains one or more resources, executes a statement, and then disposes of the resource. @@ -1632,7 +1812,7 @@ resource_acquisition A ***resource*** is a class or struct that implements the `System.IDisposable` interface, which includes a single parameterless method named `Dispose`. Code that is using a resource can call `Dispose` to indicate that the resource is no longer needed. -If the form of *resource_acquisition* is *local_variable_declaration* then the type of the *local_variable_declaration* shall be either dynamic or a type that can be implicitly converted to `System.IDisposable`. If the form of *resource_acquisition* is *expression* then this expression shall be implicitly convertible to `System.IDisposable`. +If the form of *resource_acquisition* is *local_variable_declaration* then the type of the *local_variable_declaration* shall be either `dynamic` or a type that can be implicitly converted to `System.IDisposable`. If the form of *resource_acquisition* is *expression* then this expression shall be implicitly convertible to `System.IDisposable`. Local variables declared in a *resource_acquisition* are read-only, and shall include an initializer. A compile-time error occurs if the embedded statement attempts to modify these local variables (via assignment or the `++` and `--` operators), take the address of them, or pass them as `ref` or `out` parameters. @@ -1644,7 +1824,7 @@ A `using` statement of the form using (ResourceType resource = «expression» ) «statement» ``` -corresponds to one of three possible expansions. When `ResourceType` is a non-nullable value type or a type parameter with the value type constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)), the expansion is semantically equivalent to +corresponds to one of three possible expansions. When `ResourceType` is a non-nullable value type or a type parameter with the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)), the expansion is semantically equivalent to ```csharp { @@ -1759,9 +1939,9 @@ using (ResourceType rN = eN) > > *end example* -## 12.15 The yield statement +## 13.15 The yield statement -The `yield` statement is used in an iterator block ([§12.3](statements.md#123-blocks)) to yield a value to the enumerator object ([§14.14.5](classes.md#14145-enumerator-objects)) or enumerable object ([§14.14.6](classes.md#14146-enumerable-objects)) of an iterator or to signal the end of the iteration. +The `yield` statement is used in an iterator block ([§13.3](statements.md#133-blocks)) to yield a value to the enumerator object ([§15.14.5](classes.md#15145-enumerator-objects)) or enumerable object ([§15.14.6](classes.md#15146-enumerable-objects)) of an iterator or to signal the end of the iteration. ```ANTLR yield_statement @@ -1821,7 +2001,7 @@ There are several restrictions on where a `yield` statement can appear, as descr > > *end example* -An implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) shall exist from the type of the expression in the `yield return` statement to the yield type ([§14.14.4](classes.md#14144-yield-type)) of the iterator. +An implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) shall exist from the type of the expression in the `yield return` statement to the yield type ([§15.14.4](classes.md#15144-yield-type)) of the iterator. A `yield return` statement is executed as follows: diff --git a/standard/structs.md b/standard/structs.md index d00835c6c..2c128e2d5 100644 --- a/standard/structs.md +++ b/standard/structs.md @@ -1,6 +1,6 @@ -# 15 Structs +# 16 Structs -## 15.1 General +## 16.1 General Structs are similar to classes in that they represent data structures that can contain data members and function members. However, unlike classes, structs are value types and do not require heap allocation. A variable of a `struct` type directly contains the data of the `struct`, whereas a variable of a class type contains a reference to the data, the latter known as an object. @@ -8,27 +8,29 @@ Structs are similar to classes in that they represent data structures that can c As described in [§8.3.5](types.md#835-simple-types), the simple types provided by C#, such as `int`, `double`, and `bool`, are, in fact, all struct types. -## 15.2 Struct declarations +## 16.2 Struct declarations -### 15.2.1 General +### 16.2.1 General -A *struct_declaration* is a *type_declaration* ([§13.7](namespaces.md#137-type-declarations)) that declares a new struct: +A *struct_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-type-declarations)) that declares a new struct: ```ANTLR struct_declaration - : attributes? struct_modifier* 'partial'? 'struct' + : attributes? struct_modifier* 'ref'? 'partial'? 'struct' identifier type_parameter_list? struct_interfaces? type_parameter_constraints_clause* struct_body ';'? ; ``` -A *struct_declaration* consists of an optional set of *attributes* ([§21](attributes.md#21-attributes)), followed by an optional set of *struct_modifier*s ([§15.2.2](structs.md#1522-struct-modifiers)), followed by an optional partial modifier ([§14.2.7](classes.md#1427-partial-declarations)), followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§14.2.3](classes.md#1423-type-parameters)), followed by an optional *struct_interfaces* specification ([§15.2.4](structs.md#1524-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§14.2.5](classes.md#1425-type-parameter-constraints)), followed by a *struct_body* ([§15.2.5](structs.md#1525-struct-body)), optionally followed by a semicolon. +A *struct_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *struct_modifier*s ([§16.2.2](structs.md#1622-struct-modifiers)), followed by an optional `ref` modifier ([§16.2.3](structs.md#1623-ref-modifier)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *struct_interfaces* specification ([§16.2.5](structs.md#1625-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *struct_body* ([§16.2.6](structs.md#1626-struct-body)), optionally followed by a semicolon. A struct declaration shall not supply a *type_parameter_constraints_clauses* unless it also supplies a *type_parameter_list*. A struct declaration that supplies a *type_parameter_list* is a generic struct declaration. Additionally, any struct nested inside a generic class declaration or a generic struct declaration is itself a generic struct declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)). -### 15.2.2 Struct modifiers +A struct declaration that includes a `ref` keyword shall not have a *struct_interfaces* part. + +### 16.2.2 Struct modifiers A *struct_declaration* may optionally include a sequence of *struct_modifier*s: @@ -44,29 +46,52 @@ struct_modifier ; ``` -*unsafe_modifier* ([§22.2](unsafe-code.md#222-unsafe-contexts)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). It is a compile-time error for the same modifier to appear multiple times in a struct declaration. -Except for `readonly`, the modifiers of a struct declaration have the same meaning as those of a class declaration ([§14.2.2](classes.md#1422-class-modifiers)). +Except for `readonly`, the modifiers of a struct declaration have the same meaning as those of a class declaration ([§15.2.2](classes.md#1522-class-modifiers)). The `readonly` modifier indicates that the *struct_declaration* declares a type whose instances are immutable. A readonly struct has the following constraints: - Each of its instance fields shall also be declared `readonly`. -- None of its instance properties shall have a *set_accessor_declaration* ([§14.7.3](classes.md#1473-accessors)). -- It shall not declare any field-like events ([§14.8.2](classes.md#1482-field-like-events)). +- None of its instance properties shall have a *set_accessor_declaration* ([§15.7.3](classes.md#1573-accessors)). +- It shall not declare any field-like events ([§15.8.2](classes.md#1582-field-like-events)). When an instance of a readonly struct is passed to a method, its `this` is treated like an `in` argument/parameter, which disallows write access to any instance fields (except by constructors). -### 15.2.3 Partial modifier +### 16.2.3 Ref modifier + +The `ref` modifier indicates that the *struct_declaration* declares a type whose instances are allocated on the execution stack. These types are are called ***ref struct*** types. The `ref` modifier declares that instances may contain ref-like fields, and may not be copied out of its safe-context ([§16.4.12](structs.md#16412-safe-context-constraint)). The rules for determining the safe context of a ref struct are described in [§16.4.12](structs.md#16412-safe-context-constraint). + +It is a compile-time error if a ref struct type is used in any of the following contexts: + +- As the element type of an array. +- As the declared type of a field of a class or a struct that does not have the `ref` modifier. +- Being boxed to `System.ValueType` or `System.Object`. +- As a type argument. +- As the type of a tuple element. +- An async method. +- An iterator. +- There is no conversion from a `ref struct` type to the type `object` or the type `System.ValueType`. +- A `ref struct` type shall not be declared to implement any interface. +- An instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type shall not be called with a receiver of that `ref struct` type. +- An instance method of a `ref struct` type shall not be captured by method group conversion to a delegate type. +- A ref struct shall not be captured by a lambda expression or a local function. + +> *Note*: A `ref struct` shall not declare `async` instance methods nor use a `yield return` or `yield break` statement within an instance method, because the implicit `this` parameter cannot be used in those contexts. *end note* + +These constraints ensure that a variable of `ref struct` type does not refer to stack memory that is no longer valid, or to variables that are no longer valid. -The `partial` modifier indicates that this *struct_declaration* is a partial type declaration. Multiple partial struct declarations with the same name within an enclosing namespace or type declaration combine to form one struct declaration, following the rules specified in [§14.2.7](classes.md#1427-partial-declarations). +### 16.2.4 Partial modifier -### 15.2.4 Struct interfaces +The `partial` modifier indicates that this *struct_declaration* is a partial type declaration. Multiple partial struct declarations with the same name within an enclosing namespace or type declaration combine to form one struct declaration, following the rules specified in [§15.2.7](classes.md#1527-partial-declarations). -A struct declaration may include a *struct_interfaces* specification, in which case the struct is said to directly implement the given interface types. For a constructed struct type, including a nested type declared within a generic type declaration ([§14.3.9.7](classes.md#14397-nested-types-in-generic-classes)), each implemented interface type is obtained by substituting, for each *type_parameter* in the given interface, the corresponding *type_argument* of the constructed type. +### 16.2.5 Struct interfaces + +A struct declaration may include a *struct_interfaces* specification, in which case the struct is said to directly implement the given interface types. For a constructed struct type, including a nested type declared within a generic type declaration ([§15.3.9.7](classes.md#15397-nested-types-in-generic-classes)), each implemented interface type is obtained by substituting, for each *type_parameter* in the given interface, the corresponding *type_argument* of the constructed type. ```ANTLR struct_interfaces @@ -74,11 +99,11 @@ struct_interfaces ; ``` -The handling of interfaces on multiple parts of a partial struct declaration ([§14.2.7](classes.md#1427-partial-declarations)) are discussed further in [§14.2.4.3](classes.md#14243-interface-implementations). +The handling of interfaces on multiple parts of a partial struct declaration ([§15.2.7](classes.md#1527-partial-declarations)) are discussed further in [§15.2.4.3](classes.md#15243-interface-implementations). -Interface implementations are discussed further in [§17.6](interfaces.md#176-interface-implementations). +Interface implementations are discussed further in [§18.6](interfaces.md#186-interface-implementations). -### 15.2.5 Struct body +### 16.2.6 Struct body The *struct_body* of a struct defines the members of the struct. @@ -88,7 +113,7 @@ struct_body ; ``` -## 15.3 Struct members +## 16.3 Struct members The members of a struct consist of the members introduced by its *struct_member_declaration*s and the members inherited from the type `System.ValueType`. @@ -108,29 +133,29 @@ struct_member_declaration ; ``` -*fixed_size_buffer_declaration* ([§22.8.2](unsafe-code.md#2282-fixed-size-buffer-declarations)) is only available in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*fixed_size_buffer_declaration* ([§23.8.2](unsafe-code.md#2382-fixed-size-buffer-declarations)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). > *Note*: All kinds of *class_member_declaration*s except *finalizer_declaration* are also *struct_member_declaration*s. *end note* -Except for the differences noted in [§15.4](structs.md#154-class-and-struct-differences), the descriptions of class members provided in [§14.3](classes.md#143-class-members) through [§14.12](classes.md#1412-static-constructors) apply to struct members as well. +Except for the differences noted in [§16.4](structs.md#164-class-and-struct-differences), the descriptions of class members provided in [§15.3](classes.md#153-class-members) through [§15.12](classes.md#1512-static-constructors) apply to struct members as well. -## 15.4 Class and struct differences +## 16.4 Class and struct differences -### 15.4.1 General +### 16.4.1 General Structs differ from classes in several important ways: -- Structs are value types ([§15.4.2](structs.md#1542-value-semantics)). -- All struct types implicitly inherit from the class `System.ValueType` ([§15.4.3](structs.md#1543-inheritance)). -- Assignment to a variable of a struct type creates a *copy* of the value being assigned ([§15.4.4](structs.md#1544-assignment)). -- The default value of a struct is the value produced by setting all fields to their default value ([§15.4.5](structs.md#1545-default-values)). -- Boxing and unboxing operations are used to convert between a struct type and certain reference types ([§15.4.6](structs.md#1546-boxing-and-unboxing)). -- The meaning of `this` is different within struct members ([§15.4.7](structs.md#1547-meaning-of-this)). -- Instance field declarations for a struct are not permitted to include variable initializers ([§15.4.8](structs.md#1548-field-initializers)). -- A struct is not permitted to declare a parameterless instance constructor ([§15.4.9](structs.md#1549-constructors)). +- Structs are value types ([§16.4.2](structs.md#1642-value-semantics)). +- All struct types implicitly inherit from the class `System.ValueType` ([§16.4.3](structs.md#1643-inheritance)). +- Assignment to a variable of a struct type creates a *copy* of the value being assigned ([§16.4.4](structs.md#1644-assignment)). +- The default value of a struct is the value produced by setting all fields to their default value ([§16.4.5](structs.md#1645-default-values)). +- Boxing and unboxing operations are used to convert between a struct type and certain reference types ([§16.4.6](structs.md#1646-boxing-and-unboxing)). +- The meaning of `this` is different within struct members ([§16.4.7](structs.md#1647-meaning-of-this)). +- Instance field declarations for a struct are not permitted to include variable initializers ([§16.4.8](structs.md#1648-field-initializers)). +- A struct is not permitted to declare a parameterless instance constructor ([§16.4.9](structs.md#1649-constructors)). - A struct is not permitted to declare a finalizer. -### 15.4.2 Value semantics +### 16.4.2 Value semantics Structs are value types ([§8.3](types.md#83-value-types)) and are said to have value semantics. Classes, on the other hand, are reference types ([§8.2](types.md#82-reference-types)) and are said to have reference semantics. @@ -160,7 +185,7 @@ A variable of a struct type directly contains the data of the struct, whereas a > > *end example* -With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data (except in the case of `ref` and `out` parameter variables), and it is not possible for operations on one to affect the other. Furthermore, except when explicitly nullable ([§8.3.11](types.md#8311-nullable-value-types)), it is not possible for values of a struct type to be `null`. +With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data (except in the case of `in`, `out` and `ref` parameter variables), and it is not possible for operations on one to affect the other. Furthermore, except when explicitly nullable ([§8.3.12](types.md#8312-nullable-value-types)), it is not possible for values of a struct type to be `null`. > *Note*: If a struct contains a field of reference type then the contents of the object referenced can be altered by other operations. However the value of the field itself, i.e., which object it references, cannot be changed through a mutation of a different struct value. *end note* @@ -197,7 +222,7 @@ With classes, it is possible for two variables to reference the same object, and > > *end example* -### 15.4.3 Inheritance +### 16.4.3 Inheritance All struct types implicitly inherit from the class `System.ValueType`, which, in turn, inherits from class `object`. A struct declaration may specify a list of implemented interfaces, but it is not possible for a struct declaration to specify a base class. @@ -207,15 +232,15 @@ Since inheritance isn’t supported for structs, the declared accessibility of a Function members in a struct cannot be abstract or virtual, and the `override` modifier is allowed only to override methods inherited from `System.ValueType`. -### 15.4.4 Assignment +### 16.4.4 Assignment Assignment to a variable of a struct type creates a *copy* of the value being assigned. This differs from assignment to a variable of a class type, which copies the reference but not the object identified by the reference. -Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. A struct may be passed by reference to a function member using a `ref` or `out` parameter. +Similar to an assignment, when a struct is passed as a value parameter or returned as the result of a function member, a copy of the struct is created. A struct may be passed by reference to a function member using an `in`, `out`, or `ref` parameter. -When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access shall be classified as a variable. If the `instance` expression is classified as a value, a compile-time error occurs. This is described in further detail in [§11.19.2](expressions.md#11192-simple-assignment). +When a property or indexer of a struct is the target of an assignment, the instance expression associated with the property or indexer access shall be classified as a variable. If the `instance` expression is classified as a value, a compile-time error occurs. This is described in further detail in [§12.21.2](expressions.md#12212-simple-assignment). -### 15.4.5 Default values +### 16.4.5 Default values As described in [§9.3](variables.md#93-default-values), several kinds of variables are automatically initialized to their default value when they are created. For variables of class types and other reference types, this default value is `null`. However, since structs are value types that cannot be `null`, the default value of a struct is the value produced by setting all value type fields to their default value and all reference type fields to `null`. @@ -258,19 +283,19 @@ The default value of a struct corresponds to the value returned by the default c > > *end note* -### 15.4.6 Boxing and unboxing +### 16.4.6 Boxing and unboxing A value of a class type can be converted to type `object` or to an interface type that is implemented by the class simply by treating the reference as another type at compile-time. Likewise, a value of type `object` or a value of an interface type can be converted back to a class type without changing the reference (but, of course, a run-time type check is required in this case). -Since structs are not reference types, these operations are implemented differently for struct types. When a value of a struct type is converted to certain reference types (as defined in [§10.2.9](conversions.md#1029-boxing-conversions)), a boxing operation takes place. Likewise, when a value of certain reference types (as defined in [§10.3.6](conversions.md#1036-unboxing-conversions)) is converted back to a struct type, an unboxing operation takes place. A key difference from the same operations on class types is that boxing and unboxing *copies* the struct value either into or out of the boxed instance. +Since structs are not reference types, these operations are implemented differently for struct types. When a value of a struct type is converted to certain reference types (as defined in [§10.2.9](conversions.md#1029-boxing-conversions)), a boxing operation takes place. Likewise, when a value of certain reference types (as defined in [§10.3.7](conversions.md#1037-unboxing-conversions)) is converted back to a struct type, an unboxing operation takes place. A key difference from the same operations on class types is that boxing and unboxing *copies* the struct value either into or out of the boxed instance. > *Note*: Thus, following a boxing or unboxing operation, changes made to the unboxed `struct` are not reflected in the boxed `struct`. *end note* -For further details on boxing and unboxing, see [§10.2.9](conversions.md#1029-boxing-conversions) and [§10.3.6](conversions.md#1036-unboxing-conversions). +For further details on boxing and unboxing, see [§10.2.9](conversions.md#1029-boxing-conversions) and [§10.3.7](conversions.md#1037-unboxing-conversions). -### 15.4.7 Meaning of this +### 16.4.7 Meaning of this -The meaning of `this` in a struct differs from the meaning of `this` in a class, as described in [§11.7.12](expressions.md#11712-this-access). When a struct type overrides a virtual method inherited from `System.ValueType` (such as `Equals`, `GetHashCode`, or `ToString`), invocation of the virtual method through an instance of the struct type does not cause boxing to occur. This is true even when the struct is used as a type parameter and the invocation occurs through an instance of the type parameter type. +The meaning of `this` in a struct differs from the meaning of `this` in a class, as described in [§12.8.13](expressions.md#12813-this-access). When a struct type overrides a virtual method inherited from `System.ValueType` (such as `Equals`, `GetHashCode`, or `ToString`), invocation of the virtual method through an instance of the struct type does not cause boxing to occur. This is true even when the struct is used as a type parameter and the invocation occurs through an instance of the type parameter type. > *Example*: > @@ -358,9 +383,9 @@ Similarly, boxing never implicitly occurs when accessing a member on a constrain > > *end example* -### 15.4.8 Field initializers +### 16.4.8 Field initializers -As described in [§15.4.5](structs.md#1545-default-values), the default value of a struct consists of the value that results from setting all value type fields to their default value and all reference type fields to `null`. For this reason, a struct does not permit instance field declarations to include variable initializers. This restriction applies only to instance fields. Static fields of a struct are permitted to include variable initializers. +As described in [§16.4.5](structs.md#1645-default-values), the default value of a struct consists of the value that results from setting all value type fields to their default value and all reference type fields to `null`. For this reason, a struct does not permit instance field declarations to include variable initializers. This restriction applies only to instance fields. Static fields of a struct are permitted to include variable initializers. > *Example*: The following > @@ -377,7 +402,7 @@ As described in [§15.4.5](structs.md#1545-default-values), the default value of > > *end example* -### 15.4.9 Constructors +### 16.4.9 Constructors Unlike a class, a struct is not permitted to declare a parameterless instance constructor. Instead, every struct implicitly has a parameterless instance constructor, which always returns the value that results from setting all value type fields to their default value and all reference type fields to `null` ([§8.3.3](types.md#833-default-constructors)). A struct can declare instance constructors having parameters. @@ -443,7 +468,7 @@ If the struct instance constructor specifies a constructor initializer, that ini > ``` > > No instance function member (including the set accessors for the properties `X` and `Y`) can be called until all fields of the struct being constructed have been definitely assigned. Note, however, that if `Point` were a class instead of a struct, the instance constructor implementation would be permitted. -> There is one exception to this, and that involves automatically implemented properties ([§14.7.4](classes.md#1474-automatically-implemented-properties)). The definite assignment rules ([§11.19.2](expressions.md#11192-simple-assignment)) specifically exempt assignment to an auto-property of a struct type within an instance constructor of that struct type: such an assignment is considered a definite assignment of the hidden backing field of the auto-property. Thus, the following is allowed: +> There is one exception to this, and that involves automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)). The definite assignment rules ([§12.21.2](expressions.md#12212-simple-assignment)) specifically exempt assignment to an auto-property of a struct type within an instance constructor of that struct type: such an assignment is considered a definite assignment of the hidden backing field of the auto-property. Thus, the following is allowed: > > > ```csharp @@ -462,17 +487,89 @@ If the struct instance constructor specifies a constructor initializer, that ini > > *end example*] -### 15.4.10 Static constructors +### 16.4.10 Static constructors Static constructors for structs follow most of the same rules as for classes. The execution of a static constructor for a struct type is triggered by the first of the following events to occur within an application domain: - A static member of the struct type is referenced. - An explicitly declared constructor of the struct type is called. -> *Note*: The creation of default values ([§15.4.5](structs.md#1545-default-values)) of struct types does not trigger the static constructor. (An example of this is the initial value of elements in an array.) *end note* +> *Note*: The creation of default values ([§16.4.5](structs.md#1645-default-values)) of struct types does not trigger the static constructor. (An example of this is the initial value of elements in an array.) *end note* -### 15.4.11 Automatically implemented properties +### 16.4.11 Automatically implemented properties -Automatically implemented properties ([§14.7.4](classes.md#1474-automatically-implemented-properties)) use hidden backing fields, which are only accessible to the property accessors. +Automatically implemented properties ([§15.7.4](classes.md#1574-automatically-implemented-properties)) use hidden backing fields, which are only accessible to the property accessors. > *Note*: This access restriction means that constructors in structs containing automatically implemented properties often need an explicit constructor initializer where they would not otherwise need one, to satisfy the requirement of all fields being definitely assigned before any function member is invoked or the constructor returns. *end note* + +### 16.4.12 Safe context constraint + +#### 16.4.12.1 General + +At compile-time, each expression is associated with a context where that instance and all its fields can be safely accessed, its ***safe-context***. The safe-context is a context, enclosing an expression, which it is safe for the value to escape to. + +Any expression whose compile-time type is not a ref struct has a safe-context of caller-context. + +A `default` expression, for any type, has safe-context of caller-context. + +For any non-default expression whose compile-time type is a ref struct has a safe-context defined by the following sections. + +The safe-context records which context a value may be copied into. Given an assignment from an expression `E1` with a safe-context `S1`, to an expression `E2` with safe-context `S2`, it is an error if `S2` is a wider context than `S1`. + +There are three different safe-context values, the same as the ref-safe-context values defined for reference variables ([§9.7.2](variables.md#972-ref-safe-contexts)): **declaration-block**, **function-member**, and **caller-context**. The safe-context of an expression constrains its use as follows: + +- For a return statement `return e1`, the safe-context of `e1` must be caller-context. +- For an assignment `e1 = e2` the safe-context of `e2` must be at least as wide a context as the safe-context of `e1`. + +For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver unless the type is `readonly`), with safe-context `S1`, then no argument (including the receiver) may have a narrower safe-context than `S1`. + +#### 16.4.12.2 Parameter safe context + +A formal parameter of a ref struct type, including the `this` parameter of an instance method, has a safe-context of caller-context. + +#### 16.4.12.3 Local variable safe context + +A local variable of a ref struct type has a safe-context as follows: + +- If the variable is an iteration variable of a `foreach` loop, then the variable’s safe-context is the same as the safe-context of the `foreach` loop’s expression. +- Otherwise if the variable’s declaration has an initializer then the variable’s safe-context is the same as the safe-context of that initializer. +- Otherwise the variable is uninitialized at the point of declaration and has a safe-context of caller-context. + +#### 16.4.12.4 Field safe context + +A reference to a field `e.F`, where the type of `F` is a ref struct type, has a safe-context that is the same as the safe-context of `e`. + +#### 16.4.12.5 Operators + +The application of a user-defined operator is treated as a method invocation ([§16.4.12.6](structs.md#164126-method-and-property-invocation)). + +For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the safe-context of the result is the narrowest context among the safe-contexts of the operands of the operator. As a consequence, for a unary operator that yields a value, such as `+e`, the safe-context of the result is the safe-context of the operand. + +> *Note*: The first operand of a conditional operator is a `bool`, so its safe-context is caller-context. It follows that the resulting safe-context is the narrowest safe-context of the second and third operand. *end note* + +#### 16.4.12.6 Method and property invocation + +A value resulting from a method invocation `e1.M(e2, ...)` or property invocation `e.P` has safe-context of the smallest of the following contexts: + +- caller-context. +- The safe-context of all argument expressions (including the receiver). + +A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. + +#### 16.4.12.7 stackalloc + +The result of a stackalloc expression has safe-context of function-member. + +#### 16.4.12.8 Constructor invocations + +A `new` expression that invokes a constructor obeys the same rules as a method invocation that is considered to return the type being constructed. + +In addition the safe-context is the smallest of the safe-contexts of all arguments and operands of all object initializer expressions, recursively, if any initializer is present. + +> *Note*: These rules rely on `Span` not having a constructor of the following form: +> +> ```csharp +> public Span(ref T p) +> ``` +> +> Such a constructor makes instances of `Span` used as fields indistinguishable from a `ref` field. The safety rules described in this document depend on `ref` fields not being a valid construct in C# or .NET. *end note* diff --git a/standard/terms-and-definitions.md b/standard/terms-and-definitions.md index f4fa4ab0e..6581b2078 100644 --- a/standard/terms-and-definitions.md +++ b/standard/terms-and-definitions.md @@ -35,7 +35,7 @@ For the purposes of this specification, the following definitions apply. Other t - **implementation** - particular set of software (running in a particular translation environment under particular control options) that performs translation of programs for, and supports execution of methods in, a particular execution environment - **module** - - the contents of an assembly produced by a compiler. Some implementations may have facilities to produce assemblies that contain more than one module. The behavior in such situations is outside the scope of this specification + - the contents of an assembly produced by a compiler. Some implementations may have facilities to produce assemblies that contain more than one module. The behavior in such situations is outside the scope of this specification - **namespace** - logical organizational system grouping related program elements - **parameter** diff --git a/standard/types.md b/standard/types.md index 1093358e7..917bab6fa 100644 --- a/standard/types.md +++ b/standard/types.md @@ -13,13 +13,13 @@ type ; ``` -*pointer_type* ([§22.3](unsafe-code.md#223-pointer-types)) is available only in unsafe code ([§22](unsafe-code.md#22-unsafe-code)). +*pointer_type* ([§23.3](unsafe-code.md#233-pointer-types)) is available only in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). Value types differ from reference types in that variables of the value types directly contain their data, whereas variables of the reference types store ***references*** to their data, the latter being known as ***objects***. With reference types, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With value types, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other. > *Note*: When a variable is a ref or out parameter, it does not have its own storage but references the storage of another variable. In this case, the ref or out variable is effectively an alias for another variable and not a distinct variable. *end note* -C#’s type system is unified such that *a value of any type can be treated as an object*. Every type in C# directly or indirectly derives from the `object` class type, and `object` is the ultimate base class of all types. Values of reference types are treated as objects simply by viewing the values as type `object`. Values of value types are treated as objects by performing boxing and unboxing operations ([§8.3.12](types.md#8312-boxing-and-unboxing)). +C#’s type system is unified such that *a value of any type can be treated as an object*. Every type in C# directly or indirectly derives from the `object` class type, and `object` is the ultimate base class of all types. Values of reference types are treated as objects simply by viewing the values as type `object`. Values of value types are treated as objects by performing boxing and unboxing operations ([§8.3.13](types.md#8313-boxing-and-unboxing)). For convenience, throughout this specification, some library type names are written without using their full name qualification. Refer to [§C.5](standard-library.md#c5-library-type-abbreviations) for more information. @@ -71,15 +71,15 @@ delegate_type ; ``` -*pointer_type* is available only in unsafe code ([§22.3](unsafe-code.md#223-pointer-types)). +*pointer_type* is available only in unsafe code ([§23.3](unsafe-code.md#233-pointer-types)). A reference type value is a reference to an ***instance*** of the type, the latter known as an object. The special value `null` is compatible with all reference types and indicates the absence of an instance. ### 8.2.2 Class types -A class type defines a data structure that contains ***data members*** (constants and fields), ***function members*** (methods, properties, events, indexers, operators, instance constructors, finalizers, and static constructors), and nested types. Class types support inheritance, a mechanism whereby derived classes can extend and specialize base classes. Instances of class types are created using *object_creation_expression*s ([§11.7.15.2](expressions.md#117152-object-creation-expressions)). +A class type defines a data structure that contains ***data members*** (constants and fields), ***function members*** (methods, properties, events, indexers, operators, instance constructors, finalizers, and static constructors), and nested types. Class types support inheritance, a mechanism whereby derived classes can extend and specialize base classes. Instances of class types are created using *object_creation_expression*s ([§12.8.16.2](expressions.md#128162-object-creation-expressions)). -Class types are described in [§14](classes.md#14-classes). +Class types are described in [§15](classes.md#15-classes). Certain predefined class types have special meaning in the C# language, as described in the table below. @@ -88,10 +88,10 @@ Certain predefined class types have special meaning in the C# language, as descr `System.Object` | The ultimate base class of all other types. See [§8.2.3](types.md#823-the-object-type). `System.String` | The string type of the C# language. See [§8.2.5](types.md#825-the-string-type). `System.ValueType` | The base class of all value types. See [§8.3.2](types.md#832-the-systemvaluetype-type). -`System.Enum` | The base class of all `enum` types. See [§18.5](enums.md#185-the-systemenum-type). -`System.Array` | The base class of all array types. See [§16.2.2](arrays.md#1622-the-systemarray-type). -`System.Delegate` | The base class of all `delegate` types. See [§19.1](delegates.md#191-general). -`System.Exception` | The base class of all exception types. See [§20.3](exceptions.md#203-the-systemexception-class). +`System.Enum` | The base class of all `enum` types. See [§19.5](enums.md#195-the-systemenum-type). +`System.Array` | The base class of all array types. See [§17.2.2](arrays.md#1722-the-systemarray-type). +`System.Delegate` | The base class of all `delegate` types. See [§20.1](delegates.md#201-general). +`System.Exception` | The base class of all exception types. See [§21.3](exceptions.md#213-the-systemexception-class). ### 8.2.3 The object type @@ -103,7 +103,7 @@ The keyword `object` is simply an alias for the predefined class `System.Object` The `dynamic` type, like `object`, can reference any object. When operations are applied to expressions of type `dynamic`, their resolution is deferred until the program is run. Thus, if the operation cannot legitimately be applied to the referenced object, no error is given during compilation. Instead, an exception will be thrown when resolution of the operation fails at run-time. -The `dynamic` type is further described in [§8.7](types.md#87-the-dynamic-type), and dynamic binding in [§11.3.1](expressions.md#1131-general). +The `dynamic` type is further described in [§8.7](types.md#87-the-dynamic-type), and dynamic binding in [§12.3.1](expressions.md#1231-general). ### 8.2.5 The string type @@ -117,13 +117,13 @@ The keyword `string` is simply an alias for the predefined class `System.String` An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interface types are described in [§17](interfaces.md#17-interfaces). +Interface types are described in [§18](interfaces.md#18-interfaces). ### 8.2.7 Array types An array is a data structure that contains zero or more variables, which are accessed through computed indices. The variables contained in an array, also called the elements of the array, are all of the same type, and this type is called the element type of the array. -Array types are described in [§16](arrays.md#16-arrays). +Array types are described in [§17](arrays.md#17-arrays). ### 8.2.8 Delegate types @@ -131,7 +131,7 @@ A delegate is a data structure that refers to one or more methods. For instance > *Note*: The closest equivalent of a delegate in C or C++ is a function pointer, but whereas a function pointer can only reference static functions, a delegate can reference both static and instance methods. In the latter case, the delegate stores not only a reference to the method’s entry point, but also a reference to the object instance on which to invoke the method. *end note* -Delegate types are described in [§19](delegates.md#19-delegates). +Delegate types are described in [§20](delegates.md#20-delegates). ## 8.3 Value types @@ -153,6 +153,7 @@ non_nullable_value_type struct_type : type_name | simple_type + | tuple_type ; simple_type @@ -183,6 +184,14 @@ floating_point_type | 'double' ; +tuple_type + : '(' tuple_type_element (',' tuple_type_element)+ ')' + ; + +tuple_type_element + : type identifier? + ; + enum_type : type_name ; @@ -192,13 +201,13 @@ nullable_value_type ; ``` -Unlike a variable of a reference type, a variable of a value type can contain the value `null` only if the value type is a nullable value type ([§8.3.11](types.md#8311-nullable-value-types)). For every non-nullable value type there is a corresponding nullable value type denoting the same set of values plus the value `null`. +Unlike a variable of a reference type, a variable of a value type can contain the value `null` only if the value type is a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)). For every non-nullable value type there is a corresponding nullable value type denoting the same set of values plus the value `null`. Assignment to a variable of a value type creates a *copy* of the value being assigned. This differs from assignment to a variable of a reference type, which copies the reference but not the object identified by the reference. ### 8.3.2 The System.ValueType type -All value types implicitly inherit from the `class` `System.ValueType`, which, in turn, inherits from class `object`. It is not possible for any type to derive from a value type, and value types are thus implicitly sealed ([§14.2.2.3](classes.md#14223-sealed-classes)). +All value types implicitly inherit from the `class` `System.ValueType`, which, in turn, inherits from class `object`. It is not possible for any type to derive from a value type, and value types are thus implicitly sealed ([§15.2.2.3](classes.md#15223-sealed-classes)). Note that `System.ValueType` is not itself a *value_type*. Rather, it is a *class_type* from which all *value_type*s are automatically derived. @@ -215,11 +224,11 @@ All value types implicitly declare a public parameterless instance constructor c - For `bool`, the default value is `false`. - For an *enum_type* `E`, the default value is `0`, converted to the type `E`. - For a *struct_type*, the default value is the value produced by setting all value type fields to their default value and all reference type fields to `null`. -- For a *nullable_value_type* the default value is an instance for which the `HasValue` property is false. The default value is also known as the ***null value*** of the nullable value type. Attempting to read the `Value` property of such a value causes an exception of type `System.InvalidOperationException` to be thrown ([§8.3.11](types.md#8311-nullable-value-types)). +- For a *nullable_value_type* the default value is an instance for which the `HasValue` property is false. The default value is also known as the ***null value*** of the nullable value type. Attempting to read the `Value` property of such a value causes an exception of type `System.InvalidOperationException` to be thrown ([§8.3.12](types.md#8312-nullable-value-types)). Like any other instance constructor, the default constructor of a value type is invoked using the `new` operator. -> *Note*: For efficiency reasons, this requirement is not intended to actually have the implementation generate a constructor call. For value types, the default value expression ([§11.7.19](expressions.md#11719-default-value-expressions)) produces the same result as using the default constructor. *end note* +> *Note*: For efficiency reasons, this requirement is not intended to actually have the implementation generate a constructor call. For value types, the default value expression ([§12.8.20](expressions.md#12820-default-value-expressions)) produces the same result as using the default constructor. *end note* @@ -240,11 +249,11 @@ Like any other instance constructor, the default constructor of a value type is > > *end example* -Because every value type implicitly has a public parameterless instance constructor, it is not possible for a struct type to contain an explicit declaration of a parameterless constructor. A struct type is however permitted to declare parameterized instance constructors ([§15.4.9](structs.md#1549-constructors)). +Because every value type implicitly has a public parameterless instance constructor, it is not possible for a struct type to contain an explicit declaration of a parameterless constructor. A struct type is however permitted to declare parameterized instance constructors ([§16.4.9](structs.md#1649-constructors)). ### 8.3.4 Struct types -A struct type is a value type that can declare constants, fields, methods, properties, events, indexers, operators, instance constructors, static constructors, and nested types. The declaration of struct types is described in [§15](structs.md#15-structs). +A struct type is a value type that can declare constants, fields, methods, properties, events, indexers, operators, instance constructors, static constructors, and nested types. The declaration of struct types is described in [§16](structs.md#16-structs). ### 8.3.5 Simple types @@ -284,8 +293,8 @@ Because a simple type aliases a struct type, every simple type has members. > *Note*: The simple types differ from other struct types in that they permit certain additional operations: > > - Most simple types permit values to be created by writing *literals* ([§6.4.5](lexical-structure.md#645-literals)), although C# makes no provision for literals of struct types in general. *Example*: `123` is a literal of type `int` and `'a'` is a literal of type `char`. *end example* -> - When the operands of an expression are all simple type constants, it is possible for the compiler to evaluate the expression at compile-time. Such an expression is known as a *constant_expression* ([§11.21](expressions.md#1121-constant-expressions)). Expressions involving operators defined by other struct types are not considered to be constant expressions -> - Through `const` declarations, it is possible to declare constants of the simple types ([§14.4](classes.md#144-constants)). It is not possible to have constants of other struct types, but a similar effect is provided by static readonly fields. +> - When the operands of an expression are all simple type constants, it is possible for the compiler to evaluate the expression at compile-time. Such an expression is known as a *constant_expression* ([§12.23](expressions.md#1223-constant-expressions)). Expressions involving operators defined by other struct types are not considered to be constant expressions +> - Through `const` declarations, it is possible to declare constants of the simple types ([§15.4](classes.md#154-constants)). It is not possible to have constants of other struct types, but a similar effect is provided by static readonly fields. > - Conversions involving simple types can participate in evaluation of conversion operators defined by other struct types, but a user-defined conversion operator can never participate in evaluation of another user-defined conversion operator ([§10.5.3](conversions.md#1053-evaluation-of-user-defined-conversions)). > > *end note*. @@ -307,7 +316,7 @@ C# supports nine integral types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uin All signed integral types are represented using two’s complement format. -The *integral_type* unary and binary operators always operate with signed 32-bit precision, unsigned 32-bit precision, signed 64-bit precision, or unsigned 64-bit precision, as detailed in [§11.4.7](expressions.md#1147-numeric-promotions). +The *integral_type* unary and binary operators always operate with signed 32-bit precision, unsigned 32-bit precision, signed 64-bit precision, or unsigned 64-bit precision, as detailed in [§12.4.7](expressions.md#1247-numeric-promotions). The `char` type is classified as an integral type, but it differs from the other integral types in two ways: @@ -318,13 +327,13 @@ The `char` type is classified as an integral type, but it differs from the other > `(char)10` is the same as `'\x000A'`. > *end example* -The `checked` and `unchecked` operators and statements are used to control overflow checking for integral-type arithmetic operations and conversions ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)). In a `checked` context, an overflow produces a compile-time error or causes a `System.OverflowException` to be thrown. In an `unchecked` context, overflows are ignored and any high-order bits that do not fit in the destination type are discarded. +The `checked` and `unchecked` operators and statements are used to control overflow checking for integral-type arithmetic operations and conversions ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)). In a `checked` context, an overflow produces a compile-time error or causes a `System.OverflowException` to be thrown. In an `unchecked` context, overflows are ignored and any high-order bits that do not fit in the destination type are discarded. ### 8.3.7 Floating-point types C# supports two floating-point types: `float` and `double`. The `float` and `double` types are represented using the 32-bit single-precision and 64-bit double-precision IEC 60559 formats, which provide the following sets of values: -- Positive zero and negative zero. In most situations, positive zero and negative zero behave identically as the simple value zero, but certain operations distinguish between the two ([§11.9.3](expressions.md#1193-division-operator)). +- Positive zero and negative zero. In most situations, positive zero and negative zero behave identically as the simple value zero, but certain operations distinguish between the two ([§12.10.3](expressions.md#12103-division-operator)). - Positive infinity and negative infinity. Infinities are produced by such operations as dividing a non-zero number by zero. > *Example*: > `1.0 / 0.0` yields positive infinity, and `–1.0 / 0.0` yields negative infinity. @@ -336,7 +345,7 @@ The `float` type can represent values ranging from approximately 1.5 × 10⁻⁴ The `double` type can represent values ranging from approximately 5.0 × 10⁻³²⁴ to 1.7 × 10³⁰⁸ with a precision of 15-16 digits. -If either operand of a binary operator is a floating-point type then standard numeric promotions are applied, as detailed in [§11.4.7](expressions.md#1147-numeric-promotions), and the operation is performed with `float` or `double` precision. +If either operand of a binary operator is a floating-point type then standard numeric promotions are applied, as detailed in [§12.4.7](expressions.md#1247-numeric-promotions), and the operation is performed with `float` or `double` precision. The floating-point operators, including the assignment operators, never produce exceptions. Instead, in exceptional situations, floating-point operations produce zero, infinity, or NaN, as described below: @@ -346,7 +355,7 @@ The floating-point operators, including the assignment operators, never produce - If a floating-point operation is invalid, the result of the operation becomes NaN. - If one or both operands of a floating-point operation is NaN, the result of the operation becomes NaN. -Floating-point operations may be performed with higher precision than the result type of the operation. To force a value of a floating-point type to the exact precision of its type, an explicit cast ([§11.8.7](expressions.md#1187-cast-expressions)) can be used. +Floating-point operations may be performed with higher precision than the result type of the operation. To force a value of a floating-point type to the exact precision of its type, an explicit cast ([§12.9.7](expressions.md#1297-cast-expressions)) can be used. > *Example*: Some hardware architectures support an “extended” or “long double” floating-point type with greater range and precision than the `double` type, and implicitly perform all floating-point operations using this higher precision type. Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with *less* precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects. However, in expressions of the form `x * y / z`, where the multiplication produces a result that is outside the `double` range, but the subsequent division brings the temporary result back into the `double` range, the fact that the expression is evaluated in a higher range format can cause a finite result to be produced instead of an infinity. *end example* @@ -358,7 +367,7 @@ The finite set of values of type `decimal` are of the form (–1)ᵛ × *c* × 1 A `decimal` is represented as an integer scaled by a power of ten. For `decimal`s with an absolute value less than `1.0m`, the value is exact to at least the 28th decimal place. For `decimal`s with an absolute value greater than or equal to `1.0m`, the value is exact to at least 28 digits. Contrary to the `float` and `double` data types, decimal fractional numbers such as `0.1` can be represented exactly in the decimal representation. In the `float` and `double` representations, such numbers often have non-terminating binary expansions, making those representations more prone to round-off errors. -If either operand of a binary operator is of `decimal` type then standard numeric promotions are applied, as detailed in [§11.4.7](expressions.md#1147-numeric-promotions), and the operation is performed with `double` precision. +If either operand of a binary operator is of `decimal` type then standard numeric promotions are applied, as detailed in [§12.4.7](expressions.md#1247-numeric-promotions), and the operation is performed with `double` precision. The result of an operation on values of type `decimal` is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as “banker’s rounding”). That is, results are exact to at least the 28th decimal place. Note that rounding may produce a zero value from a non-zero value. @@ -368,7 +377,7 @@ The `decimal` type has greater precision but may have a smaller range than the f ### 8.3.9 The Bool type -The `bool` type represents Boolean logical quantities. The possible values of type `bool` are `true` and `false`. +The `bool` type represents Boolean logical quantities. The possible values of type `bool` are `true` and `false`. The representation of `false` is described in [§8.3.3](types.md#833-default-constructors). Although the representation of `true` is unspecified, it shall be different from that of `false`. No standard conversions exist between `bool` and other value types. In particular, the `bool` type is distinct and separate from the integral types, a `bool` value cannot be used in place of an integral value, and vice versa. @@ -376,13 +385,55 @@ No standard conversions exist between `bool` and other value types. In particula ### 8.3.10 Enumeration types -An enumeration type is a distinct type with named constants. Every enumeration type has an underlying type, which shall be `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long` or `ulong`. The set of values of the enumeration type is the same as the set of values of the underlying type. Values of the enumeration type are not restricted to the values of the named constants. Enumeration types are defined through enumeration declarations ([§18.2](enums.md#182-enum-declarations)). +An enumeration type is a distinct type with named constants. Every enumeration type has an underlying type, which shall be `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long` or `ulong`. The set of values of the enumeration type is the same as the set of values of the underlying type. Values of the enumeration type are not restricted to the values of the named constants. Enumeration types are defined through enumeration declarations ([§19.2](enums.md#192-enum-declarations)). + +### 8.3.11 Tuple types + +A tuple type represents an ordered, fixed-length sequence of values with optional names and individual types. The number of elements in a tuple type is referred to as its ***arity***. A tuple type is written `(T1 I1, ..., Tn In)` with n ≥ 2, where the identifiers `I1...In` are optional ***tuple element names***. + +This syntax is shorthand for a type constructed with the types `T1...Tn` from `System.ValueTuple<...>`, which shall be a set of generic struct types capable of directly expressing tuple types of any arity between two and seven inclusive. +There does not need to exist a `System.ValueTuple<...>` declaration that directly matches the arity of any tuple type with a corresponding number of type parameters. Instead, tuples with an arity greater than seven are represented with a generic struct type `System.ValueTuple` that in addition to tuple elements has a `Rest` field containing a nested value of the remaining elements, using another `System.ValueTuple<...>` type. Such nesting may be observable in various ways, e.g. via the presence of a `Rest` field. Where only a single additional field is required, the generic struct type `System.ValueTuple` is used; this type is not considered a tuple type in itself. Where more than seven additional fields are required, `System.ValueTuple` is used recursively. + +Element names within a tuple type shall be distinct. A tuple element name of the form `ItemX`, where `X` is any sequence of non-`0`-initiated decimal digits that could represent the position of a tuple element, is only permitted at the position denoted by `X`. + +The optional element names are not represented in the `ValueTuple<...>` types, and are not stored in the runtime representation of a tuple value. There is an identity conversion between all tuple types with the same arity and identity-convertible sequences of element types, as well as to and from the corresponding constructed `ValueTuple<...>` type. + +The `new` operator [§12.8.16.2](expressions.md#128162-object-creation-expressions) cannot be applied with the tuple type syntax `new (T1, ..., Tn)`. Tuple values can be created from tuple expressions ([§12.8.6](expressions.md#1286-tuple-expressions)), or by applying the `new` operator directly to a type constructed from `ValueTuple<...>`. + +Tuple elements are public fields with the names `Item1`, `Item2`, etc., and can be accessed via a member access on a tuple value ([§12.8.7](expressions.md#1287-member-access). Additionally, if the tuple type has a name for a given element, that name can be used to access the element in question. + +> *Note*: Even when large tuples are represented with nested `System.ValueTuple<...>` values, each tuple element can still be accessed directly with the `Item...` name corresponding to its position. *end note* + + +> *Example*: Given the following examples: +> +> +> ```csharp +> (int, string) pair1 = (1, "One"); +> (int, string word) pair2 = (2, "Two"); +> (int number, string word) pair3 = (3, "Three"); +> (int Item1, string Item2) pair4 = (4, "Four"); +> // Error: "Item" names do not match their position +> (int Item2, string Item123) pair5 = (5, "Five"); +> (int, string) pair6 = new ValueTuple(6, "Six"); +> ValueTuple pair7 = (7, "Seven"); +> Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}"); +> ``` +> +> The tuple types for `pair1`, `pair2`, and `pair3` are all valid, with names for no, some or all of the tuple type elements. +> +> The tuple type for `pair4` is valid because the names `Item1` and `Item2` match their positions, whereas the tuple type for `pair5` is disallowed, because the names `Item2` and `Item123` do not. +> +> The declarations for `pair6` and `pair7` demonstrate that tuple types are interchangeable with constructed types of the form `ValueTuple<...>`, and that the `new` operator is allowed with the latter syntax. +> +>The last line shows that tuple elements can be accessed by the `Item` name corresponding to their position, as well as by the corresponding tuple element name, if present in the type. +> *end example* -### 8.3.11 Nullable value types +### 8.3.12 Nullable value types A nullable value type can represent all values of its underlying type plus an additional null value. A nullable value type is written `T?`, where `T` is the underlying type. This syntax is shorthand for `System.Nullable`, and the two forms can be used interchangeably. -Conversely, a ***non-nullable value type*** is any value type other than `System.Nullable` and its shorthand `T?` (for any `T`), plus any type parameter that is constrained to be a non-nullable value type (that is, any type parameter with a value type constraint ([§14.2.5](classes.md#1425-type-parameter-constraints))). The `System.Nullable` type specifies the value type constraint for `T`, which means that the underlying type of a nullable value type can be any non-nullable value type. The underlying type of a nullable value type cannot be a nullable value type or a reference type. For example, `int??` and `string?` are invalid types. +Conversely, a ***non-nullable value type*** is any value type other than `System.Nullable` and its shorthand `T?` (for any `T`), plus any type parameter that is constrained to be a non-nullable value type (that is, any type parameter with a value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints))). The `System.Nullable` type specifies the value type constraint for `T`, which means that the underlying type of a nullable value type can be any non-nullable value type. The underlying type of a nullable value type cannot be a nullable value type or a reference type. For example, `int??` and `string?` are invalid types. An instance of a nullable value type `T?` has two public read-only properties: @@ -403,21 +454,21 @@ creates a non-null instance of `T?` for which the `Value` property is `x`. The p Implicit conversions are available from the `null` literal to `T?` ([§10.2.7](conversions.md#1027-null-literal-conversions)) and from `T` to `T?` ([§10.2.6](conversions.md#1026-implicit-nullable-conversions)). -The nullable type `T?` implements no interfaces ([§17](interfaces.md#17-interfaces)). In particular, this means it does not implement any interface that the underlying type `T` does. +The nullable type `T?` implements no interfaces ([§18](interfaces.md#18-interfaces)). In particular, this means it does not implement any interface that the underlying type `T` does. -### 8.3.12 Boxing and unboxing +### 8.3.13 Boxing and unboxing The concept of boxing and unboxing provide a bridge between *value_type*s and *reference_type*s by permitting any value of a *value_type* to be converted to and from type `object`. Boxing and unboxing enables a unified view of the type system wherein a value of any type can ultimately be treated as an `object`. -Boxing is described in more detail in [§10.2.9](conversions.md#1029-boxing-conversions) and unboxing is described in [§10.3.6](conversions.md#1036-unboxing-conversions). +Boxing is described in more detail in [§10.2.9](conversions.md#1029-boxing-conversions) and unboxing is described in [§10.3.7](conversions.md#1037-unboxing-conversions). ## 8.4 Constructed types ### 8.4.1 General -A generic type declaration, by itself, denotes an ***unbound generic type*** that is used as a “blueprint” to form many different types, by way of applying ***type arguments***. The type arguments are written within angle brackets (`<` and `>`) immediately following the name of the generic type. A type that includes at least one type argument is called a ***constructed type***. A constructed type can be used in most places in the language in which a type name can appear. An unbound generic type can only be used within a *typeof_expression* ([§11.7.16](expressions.md#11716-the-typeof-operator)). +A generic type declaration, by itself, denotes an ***unbound generic type*** that is used as a “blueprint” to form many different types, by way of applying ***type arguments***. The type arguments are written within angle brackets (`<` and `>`) immediately following the name of the generic type. A type that includes at least one type argument is called a ***constructed type***. A constructed type can be used in most places in the language in which a type name can appear. An unbound generic type can only be used within a *typeof_expression* ([§12.8.17](expressions.md#12817-the-typeof-operator)). -Constructed types can also be used in expressions as simple names ([§11.7.4](expressions.md#1174-simple-names)) or when accessing a member ([§11.7.6](expressions.md#1176-member-access)). +Constructed types can also be used in expressions as simple names ([§12.8.4](expressions.md#1284-simple-names)) or when accessing a member ([§12.8.7](expressions.md#1287-member-access)). When a *namespace_or_type_name* is evaluated, only generic types with the correct number of type parameters are considered. Thus, it is possible to use the same identifier to identify different types, as long as the types have different numbers of type parameters. This is useful when mixing generic and non-generic classes in the same program. @@ -445,7 +496,7 @@ When a *namespace_or_type_name* is evaluated, only generic types with the correc > > *end example* -The detailed rules for name lookup in the *namespace_or_type_name* productions is described in [§7.8](basic-concepts.md#78-namespace-and-type-names). The resolution of ambiguities in these productions is described in [§6.2.5](lexical-structure.md#625-grammar-ambiguities). A *type_name* might identify a constructed type even though it doesn’t specify type parameters directly. This can occur where a type is nested within a generic `class` declaration, and the instance type of the containing declaration is implicitly used for name lookup ([§14.3.9.7](classes.md#14397-nested-types-in-generic-classes)). +The detailed rules for name lookup in the *namespace_or_type_name* productions is described in [§7.8](basic-concepts.md#78-namespace-and-type-names). The resolution of ambiguities in these productions is described in [§6.2.5](lexical-structure.md#625-grammar-ambiguities). A *type_name* might identify a constructed type even though it doesn’t specify type parameters directly. This can occur where a type is nested within a generic `class` declaration, and the instance type of the containing declaration is implicitly used for name lookup ([§15.3.9.7](classes.md#15397-nested-types-in-generic-classes)). > *Example*: > @@ -481,7 +532,7 @@ type_argument ; ``` -A *type_argument* shall not be a pointer type ([§22](unsafe-code.md#22-unsafe-code)). Each type argument shall satisfy any constraints on the corresponding type parameter ([§14.2.5](classes.md#1425-type-parameter-constraints)). +Each type argument shall satisfy any constraints on the corresponding type parameter ([§15.2.5](classes.md#1525-type-parameter-constraints)). ### 8.4.3 Open and closed types @@ -501,11 +552,11 @@ Each closed constructed type has its own set of static variables, which are not The term ***unbound type*** refers to a non-generic type or an unbound generic type. The term ***bound type*** refers to a non-generic type or a constructed type. -An unbound type refers to the entity declared by a type declaration. An unbound generic type is not itself a type, and cannot be used as the type of a variable, argument or return value, or as a base type. The only construct in which an unbound generic type can be referenced is the `typeof` expression ([§11.7.16](expressions.md#11716-the-typeof-operator)). +An unbound type refers to the entity declared by a type declaration. An unbound generic type is not itself a type, and cannot be used as the type of a variable, argument or return value, or as a base type. The only construct in which an unbound generic type can be referenced is the `typeof` expression ([§12.8.17](expressions.md#12817-the-typeof-operator)). ### 8.4.5 Satisfying constraints -Whenever a constructed type or generic method is referenced, the supplied type arguments are checked against the type parameter constraints declared on the generic type or method ([§14.2.5](classes.md#1425-type-parameter-constraints)). For each `where` clause, the type argument `A` that corresponds to the named type parameter is checked against each constraint as follows: +Whenever a constructed type or generic method is referenced, the supplied type arguments are checked against the type parameter constraints declared on the generic type or method ([§15.2.5](classes.md#1525-type-parameter-constraints)). For each `where` clause, the type argument `A` that corresponds to the named type parameter is checked against each constraint as follows: - If the constraint is a `class` type, an interface type, or a type parameter, let `C` represent that constraint with the supplied type arguments substituted for any type parameters that appear in the constraint. To satisfy the constraint, it shall be the case that type `A` is convertible to type `C` by one of the following: - An identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)) @@ -519,13 +570,13 @@ Whenever a constructed type or generic method is referenced, the supplied type a - If the constraint is the value type constraint (`struct`), the type `A` shall satisfy one of the following: - `A` is a `struct` type or `enum` type, but not a nullable value type. > *Note*: `System.ValueType` and `System.Enum` are reference types that do not satisfy this constraint. *end note* - - `A` is a type parameter having the value type constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)). + - `A` is a type parameter having the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)). - If the constraint is the constructor constraint `new()`, the type `A` shall not be `abstract` and shall have a public parameterless constructor. This is satisfied if one of the following is true: - `A` is a value type, since all value types have a public default constructor ([§8.3.3](types.md#833-default-constructors)). - - `A` is a type parameter having the constructor constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)). - - `A` is a type parameter having the value type constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)). + - `A` is a type parameter having the constructor constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)). + - `A` is a type parameter having the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)). - `A` is a `class` that is not abstract and contains an explicitly declared public constructor with no parameters. - - `A` is not `abstract` and has a default constructor ([§14.11.5](classes.md#14115-default-constructors)). + - `A` is not `abstract` and has a default constructor ([§15.11.5](classes.md#15115-default-constructors)). A compile-time error occurs if one or more of a type parameter’s constraints are not satisfied by the given type arguments. @@ -556,13 +607,13 @@ Since a type parameter can be instantiated with many different type arguments, t > *Note*: These include: > -> - A type parameter cannot be used directly to declare a base class ([§14.2.4.2](classes.md#14242-base-classes)) or interface ([§17.2.4](interfaces.md#1724-base-interfaces)). -> - The rules for member lookup on type parameters depend on the constraints, if any, applied to the type parameter. They are detailed in [§11.5](expressions.md#115-member-lookup). -> - The available conversions for a type parameter depend on the constraints, if any, applied to the type parameter. They are detailed in [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters) and [§10.3.8](conversions.md#1038-explicit-conversions-involving-type-parameters). -> - The literal `null` cannot be converted to a type given by a type parameter, except if the type parameter is known to be a reference type ([§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters)). However, a default expression ([§11.7.19](expressions.md#11719-default-value-expressions)) can be used instead. In addition, a value with a type given by a type parameter *can* be compared with null using `==` and `!=` ([§11.11.7](expressions.md#11117-reference-type-equality-operators)) unless the type parameter has the value type constraint. -> - A `new` expression ([§11.7.15.2](expressions.md#117152-object-creation-expressions)) can only be used with a type parameter if the type parameter is constrained by a *constructor_constraint* or the value type constraint ([§14.2.5](classes.md#1425-type-parameter-constraints)). +> - A type parameter cannot be used directly to declare a base class ([§15.2.4.2](classes.md#15242-base-classes)) or interface ([§18.2.4](interfaces.md#1824-base-interfaces)). +> - The rules for member lookup on type parameters depend on the constraints, if any, applied to the type parameter. They are detailed in [§12.5](expressions.md#125-member-lookup). +> - The available conversions for a type parameter depend on the constraints, if any, applied to the type parameter. They are detailed in [§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters) and [§10.3.9](conversions.md#1039-explicit-conversions-involving-type-parameters). +> - The literal `null` cannot be converted to a type given by a type parameter, except if the type parameter is known to be a reference type ([§10.2.12](conversions.md#10212-implicit-conversions-involving-type-parameters)). However, a default expression ([§12.8.20](expressions.md#12820-default-value-expressions)) can be used instead. In addition, a value with a type given by a type parameter *can* be compared with null using `==` and `!=` ([§12.12.7](expressions.md#12127-reference-type-equality-operators)) unless the type parameter has the value type constraint. +> - A `new` expression ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) can only be used with a type parameter if the type parameter is constrained by a *constructor_constraint* or the value type constraint ([§15.2.5](classes.md#1525-type-parameter-constraints)). > - A type parameter cannot be used anywhere within an attribute. -> - A type parameter cannot be used in a member access ([§11.7.6](expressions.md#1176-member-access)) or type name ([§7.8](basic-concepts.md#78-namespace-and-type-names)) to identify a static member or a nested type. +> - A type parameter cannot be used in a member access ([§12.8.7](expressions.md#1287-member-access)) or type name ([§7.8](basic-concepts.md#78-namespace-and-type-names)) to identify a static member or a nested type. > - A type parameter cannot be used as an *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)). > > *end note* @@ -571,7 +622,7 @@ As a type, type parameters are purely a compile-time construct. At run-time, eac ## 8.6 Expression tree types -***Expression trees*** permit lambda expressions to be represented as data structures instead of executable code. Expression trees are values of ***expression tree types*** of the form `System.Linq.Expressions.Expression`, where `TDelegate` is any delegate type. For the remainder of this specification we will refer to these types using the shorthand `Expression`. +***Expression trees*** permit lambda expressions to be represented as data structures instead of executable code. Expression trees are values of ***expression tree types*** of the form `System.Linq.Expressions.Expression`, where `TDelegate` is any delegate type. For the remainder of this specification these types will be referred to using the shorthand `Expression`. If a conversion exists from a lambda expression to a delegate type `D`, a conversion also exists to the expression tree type `Expression`. Whereas the conversion of a lambda expression to a delegate type generates a delegate that references executable code for the lambda expression, conversion to an expression tree type creates an expression tree representation of the lambda expression. More details of this conversion are provided in [§10.7.3](conversions.md#1073-evaluation-of-lambda-expression-conversions-to-expression-tree-types). @@ -613,20 +664,20 @@ The API surface provided by `Expression` is implementation-specific b ## 8.7 The dynamic type -The type `dynamic` uses dynamic binding, as described in detail in [§11.3.2](expressions.md#1132-binding-time), as opposed to static binding which is used by all other types. +The type `dynamic` uses dynamic binding, as described in detail in [§12.3.2](expressions.md#1232-binding-time), as opposed to static binding which is used by all other types. `dynamic` is considered identical to `object` except in the following respects: -- Operations on expressions of type `dynamic` can be dynamically bound ([§11.3.3](expressions.md#1133-dynamic-binding)). -- Type inference ([§11.6.3](expressions.md#1163-type-inference)) will prefer `dynamic` over `object` if both are candidates. +- Operations on expressions of type `dynamic` can be dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). +- Type inference ([§12.6.3](expressions.md#1263-type-inference)) will prefer `dynamic` over `object` if both are candidates. - `dynamic` cannot be used as - - the type in an *object_creation_expression* ([§11.7.15.2](expressions.md#117152-object-creation-expressions)) - - a *predefined_type* in a *member_access* ([§11.7.6.1](expressions.md#11761-general)) + - the type in an *object_creation_expression* ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) + - a *predefined_type* in a *member_access* ([§12.8.7.1](expressions.md#12871-general)) - the operand of the `typeof` operator - an attribute argument - a constraint - an extension method type - - any part of a type argument within *struct_interfaces* ([§15.2.4](structs.md#1524-struct-interfaces)) or *interface_type_list* ([§14.2.4.1](classes.md#14241-general)). + - any part of a type argument within *struct_interfaces* ([§16.2.5](structs.md#1625-struct-interfaces)) or *interface_type_list* ([§15.2.4.1](classes.md#15241-general)). Because of this equivalence, the following holds: @@ -645,9 +696,8 @@ unmanaged_type ; ``` -An *unmanaged_type* is any type that isn’t a *reference_type*, a *type_parameter*, or a constructed type, and contains no fields whose type is not an *unmanaged_type*. In other words, an *unmanaged_type* is one of the following: +An *unmanaged_type* is any type that isn’t a *reference_type*, a *type_parameter*, or a constructed type, and contains no instance fields whose type is not an *unmanaged_type*. In other words, an *unmanaged_type* is one of the following: - `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, or `bool`. - Any *enum_type*. -- Any user-defined *struct_type* that is not a constructed type and contains fields of *unmanaged_type*s only. -- In unsafe code ([§22.2](unsafe-code.md#222-unsafe-contexts)), any *pointer_type* ([§22.3](unsafe-code.md#223-pointer-types)). +- Any user-defined *struct_type* that is not a constructed type and contains instance fields of *unmanaged_type*s only. diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 2e843ed9d..15a5406eb 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -1,6 +1,6 @@ -# 22 Unsafe code +# 23 Unsafe code -## 22.1 General +## 23.1 General An implementation that does not support unsafe code is required to diagnose any usage of the syntactic rules defined in this clause. @@ -16,7 +16,7 @@ An implementation that does not support unsafe code is required to diagnose any > > *end note* -## 22.2 Unsafe contexts +## 23.2 Unsafe contexts The unsafe features of C# are available only in unsafe contexts. An unsafe context is introduced by including an `unsafe` modifier in the declaration of a type, member, or local function, or by employing an *unsafe_statement*: @@ -111,11 +111,11 @@ Other than establishing an unsafe context, thus permitting the use of pointer ty > > *end example* -When the `unsafe` modifier is used on a partial type declaration ([§14.2.7](classes.md#1427-partial-declarations)), only that particular part is considered an unsafe context. +When the `unsafe` modifier is used on a partial type declaration ([§15.2.7](classes.md#1527-partial-declarations)), only that particular part is considered an unsafe context. -## 22.3 Pointer types +## 23.3 Pointer types -In an unsafe context, a *type* ([§8.1](types.md#81-general)) can be a *pointer_type* as well as a *value_type*, a *reference_type*, or a *type_parameter*. In an unsafe context a *pointer_type* may also be the element type of an array ([§16](arrays.md#16-arrays)). A *pointer_type* may also be used in a typeof expression ([§11.7.16](expressions.md#11716-the-typeof-operator)) outside of an unsafe context (as such usage is not unsafe). +In an unsafe context, a *type* ([§8.1](types.md#81-general)) can be a *pointer_type* as well as a *value_type*, a *reference_type*, or a *type_parameter*. In an unsafe context a *pointer_type* may also be the element type of an array ([§17](arrays.md#17-arrays)). A *pointer_type* may also be used in a typeof expression ([§12.8.17](expressions.md#12817-the-typeof-operator)) outside of an unsafe context (as such usage is not unsafe). A *pointer_type* is written as an *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)) or the keyword `void`, followed by a `*` token: @@ -128,9 +128,9 @@ pointer_type The type specified before the `*` in a pointer type is called the ***referent type*** of the pointer type. It represents the type of the variable to which a value of the pointer type points. -A *pointer_type* may only be used in an *array_type* in an unsafe context ([§22.2](unsafe-code.md#222-unsafe-contexts)). A *non_array_type* is any type that is not itself an *array_type*. +A *pointer_type* may only be used in an *array_type* in an unsafe context ([§23.2](unsafe-code.md#232-unsafe-contexts)). A *non_array_type* is any type that is not itself an *array_type*. -Unlike references (values of reference types), pointers are not tracked by the garbage collector—the garbage collector has no knowledge of pointers and the data to which they point. For this reason a pointer is not permitted to point to a reference or to a struct that contains references, and the referent type of a pointer shall be an *unmanaged_type*. +Unlike references (values of reference types), pointers are not tracked by the garbage collector—the garbage collector has no knowledge of pointers and the data to which they point. For this reason a pointer is not permitted to point to a reference or to a struct that contains references, and the referent type of a pointer shall be an *unmanaged_type*. Pointer types themselves are unmanaged types, so a pointer type may be used as the referent type for another pointer type. The intuitive rule for mixing of pointers and references is that referents of references (objects) are permitted to contain pointers, but referents of pointers are not permitted to contain references. @@ -156,23 +156,31 @@ For a given implementation, all pointer types shall have the same size and repre > > *end note* -The value of a pointer having type `T*` represents the address of a variable of type `T`. The pointer indirection operator `*` ([§22.6.2](unsafe-code.md#2262-pointer-indirection)) can be used to access this variable. +The value of a pointer having type `T*` represents the address of a variable of type `T`. The pointer indirection operator `*` ([§23.6.2](unsafe-code.md#2362-pointer-indirection)) can be used to access this variable. > *Example*: Given a variable `P` of type `int*`, the expression `*P` denotes the `int` variable found at the address contained in `P`. *end example* -Like an object reference, a pointer may be `null`. Applying the indirection operator to a `null`-valued pointer results in implementation-defined behavior ([§22.6.2](unsafe-code.md#2262-pointer-indirection)). A pointer with value `null` is represented by all-bits-zero. +Like an object reference, a pointer may be `null`. Applying the indirection operator to a `null`-valued pointer results in implementation-defined behavior ([§23.6.2](unsafe-code.md#2362-pointer-indirection)). A pointer with value `null` is represented by all-bits-zero. -The `void*` type represents a pointer to an unknown type. Because the referent type is unknown, the indirection operator cannot be applied to a pointer of type `void*`, nor can any arithmetic be performed on such a pointer. However, a pointer of type `void*` can be cast to any other pointer type (and vice versa) and compared to values of other pointer types ([§22.6.8](unsafe-code.md#2268-pointer-comparison)). +The `void*` type represents a pointer to an unknown type. Because the referent type is unknown, the indirection operator cannot be applied to a pointer of type `void*`, nor can any arithmetic be performed on such a pointer. However, a pointer of type `void*` can be cast to any other pointer type (and vice versa) and compared to values of other pointer types ([§23.6.8](unsafe-code.md#2368-pointer-comparison)). -Pointer types are a separate category of types. Unlike reference types and value types, pointer types do not inherit from `object` and no conversions exist between pointer types and `object`. In particular, boxing and unboxing ([§8.3.12](types.md#8312-boxing-and-unboxing)) are not supported for pointers. However, conversions are permitted between different pointer types and between pointer types and the integral types. This is described in [§22.5](unsafe-code.md#225-pointer-conversions). +Pointer types are a separate category of types. Unlike reference types and value types, pointer types do not inherit from `object` and no conversions exist between pointer types and `object`. In particular, boxing and unboxing ([§8.3.13](types.md#8313-boxing-and-unboxing)) are not supported for pointers. However, conversions are permitted between different pointer types and between pointer types and the integral types. This is described in [§23.5](unsafe-code.md#235-pointer-conversions). -A *pointer_type* cannot be used as a type argument ([§8.4](types.md#84-constructed-types)), and type inference ([§11.6.3](expressions.md#1163-type-inference)) fails on generic method calls that would have inferred a type argument to be a pointer type. +A *pointer_type* cannot be used as a type argument ([§8.4](types.md#84-constructed-types)), and type inference ([§12.6.3](expressions.md#1263-type-inference)) fails on generic method calls that would have inferred a type argument to be a pointer type. -A *pointer_type* cannot be used as a type of a subexpression of a dynamically bound operation ([§11.3.3](expressions.md#1133-dynamic-binding)). +A *pointer_type* cannot be used as a type of a subexpression of a dynamically bound operation ([§12.3.3](expressions.md#1233-dynamic-binding)). -A *pointer_type* may be used as the type of a volatile field ([§14.5.4](classes.md#1454-volatile-fields)). +A *pointer_type* cannot be used as the type of the first parameter in an extension method ([§15.6.10](classes.md#15610-extension-methods)). -> *Note*: Although pointers can be passed as `ref` or `out` parameters, doing so can cause undefined behavior, since the pointer might well be set to point to a local variable that no longer exists when the called method returns, or the fixed object to which it used to point, is no longer fixed. For example: +A *pointer_type* may be used as the type of a volatile field ([§15.5.4](classes.md#1554-volatile-fields)). + +The *dynamic erasure* of a type `E*` is the pointer type with referent type of the dynamic erasure of `E`. + +An expression with a pointer type cannot be used to provide the value in a *member_declarator* within an *anonymous_object_creation_expression* ([§12.8.16.7](expressions.md#128167-anonymous-object-creation-expressions)). + +The default value ([§9.3](variables.md#93-default-values)) for any pointer type is `null`. + +> *Note*: Although pointers can be passed as `in`, `ref` or `out` parameters, doing so can cause undefined behavior, since the pointer might well be set to point to a local variable that no longer exists when the called method returns, or the fixed object to which it used to point, is no longer fixed. For example: > > > @@ -233,37 +241,37 @@ A method can return a value of some type, and that type can be a pointer. In an unsafe context, several constructs are available for operating on pointers: -- The unary `*` operator may be used to perform pointer indirection ([§22.6.2](unsafe-code.md#2262-pointer-indirection)). -- The `->` operator may be used to access a member of a struct through a pointer ([§22.6.3](unsafe-code.md#2263-pointer-member-access)). -- The `[]` operator may be used to index a pointer ([§22.6.4](unsafe-code.md#2264-pointer-element-access)). -- The unary `&` operator may be used to obtain the address of a variable ([§22.6.5](unsafe-code.md#2265-the-address-of-operator)). -- The `++` and `--` operators may be used to increment and decrement pointers ([§22.6.6](unsafe-code.md#2266-pointer-increment-and-decrement)). -- The binary `+` and `-` operators may be used to perform pointer arithmetic ([§22.6.7](unsafe-code.md#2267-pointer-arithmetic)). -- The `==`, `!=`, `<`, `>`, `<=`, and `>=` operators may be used to compare pointers ([§22.6.8](unsafe-code.md#2268-pointer-comparison)). -- The `stackalloc` operator may be used to allocate memory from the call stack ([§22.9](unsafe-code.md#229-stack-allocation)). -- The `fixed` statement may be used to temporarily fix a variable so its address can be obtained ([§22.7](unsafe-code.md#227-the-fixed-statement)). +- The unary `*` operator may be used to perform pointer indirection ([§23.6.2](unsafe-code.md#2362-pointer-indirection)). +- The `->` operator may be used to access a member of a struct through a pointer ([§23.6.3](unsafe-code.md#2363-pointer-member-access)). +- The `[]` operator may be used to index a pointer ([§23.6.4](unsafe-code.md#2364-pointer-element-access)). +- The unary `&` operator may be used to obtain the address of a variable ([§23.6.5](unsafe-code.md#2365-the-address-of-operator)). +- The `++` and `--` operators may be used to increment and decrement pointers ([§23.6.6](unsafe-code.md#2366-pointer-increment-and-decrement)). +- The binary `+` and `-` operators may be used to perform pointer arithmetic ([§23.6.7](unsafe-code.md#2367-pointer-arithmetic)). +- The `==`, `!=`, `<`, `>`, `<=`, and `>=` operators may be used to compare pointers ([§23.6.8](unsafe-code.md#2368-pointer-comparison)). +- The `stackalloc` operator may be used to allocate memory from the call stack ([§23.9](unsafe-code.md#239-stack-allocation)). +- The `fixed` statement may be used to temporarily fix a variable so its address can be obtained ([§23.7](unsafe-code.md#237-the-fixed-statement)). -## 22.4 Fixed and moveable variables +## 23.4 Fixed and moveable variables -The address-of operator ([§22.6.5](unsafe-code.md#2265-the-address-of-operator)) and the `fixed` statement ([§22.7](unsafe-code.md#227-the-fixed-statement)) divide variables into two categories: ***Fixed variables*** and ***moveable variables***. +The address-of operator ([§23.6.5](unsafe-code.md#2365-the-address-of-operator)) and the `fixed` statement ([§23.7](unsafe-code.md#237-the-fixed-statement)) divide variables into two categories: ***Fixed variables*** and ***moveable variables***. Fixed variables reside in storage locations that are unaffected by operation of the garbage collector. (Examples of fixed variables include local variables, value parameters, and variables created by dereferencing pointers.) On the other hand, moveable variables reside in storage locations that are subject to relocation or disposal by the garbage collector. (Examples of moveable variables include fields in objects and elements of arrays.) -The `&` operator ([§22.6.5](unsafe-code.md#2265-the-address-of-operator)) permits the address of a fixed variable to be obtained without restrictions. However, because a moveable variable is subject to relocation or disposal by the garbage collector, the address of a moveable variable can only be obtained using a `fixed statement` ([§22.7](unsafe-code.md#227-the-fixed-statement)), and that address remains valid only for the duration of that `fixed` statement. +The `&` operator ([§23.6.5](unsafe-code.md#2365-the-address-of-operator)) permits the address of a fixed variable to be obtained without restrictions. However, because a moveable variable is subject to relocation or disposal by the garbage collector, the address of a moveable variable can only be obtained using a `fixed statement` ([§23.7](unsafe-code.md#237-the-fixed-statement)), and that address remains valid only for the duration of that `fixed` statement. In precise terms, a fixed variable is one of the following: -- A variable resulting from a *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) that refers to a local variable, value parameter, or parameter array, unless the variable is captured by an anonymous function ([§11.17.6.2](expressions.md#111762-captured-outer-variables)). -- A variable resulting from a *member_access* ([§11.7.6](expressions.md#1176-member-access)) of the form `V.I`, where `V` is a fixed variable of a *struct_type*. -- A variable resulting from a *pointer_indirection_expression* ([§22.6.2](unsafe-code.md#2262-pointer-indirection)) of the form `*P`, a *pointer_member_access* ([§22.6.3](unsafe-code.md#2263-pointer-member-access)) of the form `P->I`, or a *pointer_element_access* ([§22.6.4](unsafe-code.md#2264-pointer-element-access)) of the form `P[E]`. +- A variable resulting from a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) that refers to a local variable, value parameter, or parameter array, unless the variable is captured by an anonymous function ([§12.19.6.2](expressions.md#121962-captured-outer-variables)). +- A variable resulting from a *member_access* ([§12.8.7](expressions.md#1287-member-access)) of the form `V.I`, where `V` is a fixed variable of a *struct_type*. +- A variable resulting from a *pointer_indirection_expression* ([§23.6.2](unsafe-code.md#2362-pointer-indirection)) of the form `*P`, a *pointer_member_access* ([§23.6.3](unsafe-code.md#2363-pointer-member-access)) of the form `P->I`, or a *pointer_element_access* ([§23.6.4](unsafe-code.md#2364-pointer-element-access)) of the form `P[E]`. All other variables are classified as moveable variables. -A static field is classified as a moveable variable. Also, a `ref` or `out` parameter is classified as a moveable variable, even if the argument given for the parameter is a fixed variable. Finally, a variable produced by dereferencing a pointer is always classified as a fixed variable. +A static field is classified as a moveable variable. Also, an `in`, `out`, or `ref` parameter is classified as a moveable variable, even if the argument given for the parameter is a fixed variable. Finally, a variable produced by dereferencing a pointer is always classified as a fixed variable. -## 22.5 Pointer conversions +## 23.5 Pointer conversions -### 22.5.1 General +### 23.5.1 General In an unsafe context, the set of available implicit conversions ([§10.2](conversions.md#102-implicit-conversions)) is extended to include the following implicit pointer conversions: @@ -335,14 +343,14 @@ Mappings between pointers and integers are implementation-defined. > *Note*: However, on 32- and 64-bit CPU architectures with a linear address space, conversions of pointers to or from integral types typically behave exactly like conversions of `uint` or `ulong` values, respectively, to or from those integral types. *end note* -### 22.5.2 Pointer arrays +### 23.5.2 Pointer arrays -Arrays of pointers can be constructed using *array_creation_expression* ([§11.7.15.5](expressions.md#117155-array-creation-expressions)) in an usafe context. Only some of the conversions that apply to other array types are allowed on pointer arrays: +Arrays of pointers can be constructed using *array_creation_expression* ([§12.8.16.5](expressions.md#128165-array-creation-expressions)) in an usafe context. Only some of the conversions that apply to other array types are allowed on pointer arrays: -- The implicit reference conversion ([§10.2.6](conversions.md#1026-implicit-nullable-conversions)) from any *array_type* to `System.Array` and the interfaces it implements also applies to pointer arrays. However, any attempt to access the array elements through `System.Array` or the interfaces it implements may result in an exception at run-time, as pointer types are not convertible to `object`. -- The implicit and explicit reference conversions ([§10.2.6](conversions.md#1026-implicit-nullable-conversions), [§10.3.4](conversions.md#1034-explicit-nullable-conversions)) from a single-dimensional array type `S[]` to `System.Collections.Generic.IList` and its generic base interfaces never apply to pointer arrays. -- The explicit reference conversion ([§10.3.4](conversions.md#1034-explicit-nullable-conversions)) from `System.Array` and the interfaces it implements to any *array_type* applies to pointer arrays. -- The explicit reference conversions ([§10.3.4](conversions.md#1034-explicit-nullable-conversions)) from `System.Collections.Generic.IList` and its base interfaces to a single-dimensional array type `T[]` never applies to pointer arrays, since pointer types cannot be used as type arguments, and there are no conversions from pointer types to non-pointer types. +- The implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)) from any *array_type* to `System.Array` and the interfaces it implements also applies to pointer arrays. However, any attempt to access the array elements through `System.Array` or the interfaces it implements may result in an exception at run-time, as pointer types are not convertible to `object`. +- The implicit and explicit reference conversions ([§10.2.8](conversions.md#1028-implicit-reference-conversions), [§10.3.5](conversions.md#1035-explicit-reference-conversions)) from a single-dimensional array type `S[]` to `System.Collections.Generic.IList` and its generic base interfaces never apply to pointer arrays. +- The explicit reference conversion ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) from `System.Array` and the interfaces it implements to any *array_type* applies to pointer arrays. +- The explicit reference conversions ([§10.3.5](conversions.md#1035-explicit-reference-conversions)) from `System.Collections.Generic.IList` and its base interfaces to a single-dimensional array type `T[]` never applies to pointer arrays, since pointer types cannot be used as type arguments, and there are no conversions from pointer types to non-pointer types. These restrictions mean that the expansion for the `foreach` statement over arrays described in [§9.4.4.17](variables.md#94417-foreach-statements) cannot be applied to pointer arrays. Instead, a `foreach` statement of the form @@ -368,21 +376,21 @@ where the type of `x` is an array type of the form `T[,,...,]`, *n* is the numbe } ``` -The variables `a`, `i0`, `i1`, … `in` are not visible to or accessible to `x` or the *embedded_statement* or any other source code of the program. The variable `v` is read-only in the embedded statement. If there is not an explicit conversion ([§22.5](unsafe-code.md#225-pointer-conversions)) from `T` (the element type) to `V`, an error is produced and no further steps are taken. If `x` has the value `null`, a `System.NullReferenceException` is thrown at run-time. +The variables `a`, `i0`, `i1`, … `in` are not visible to or accessible to `x` or the *embedded_statement* or any other source code of the program. The variable `v` is read-only in the embedded statement. If there is not an explicit conversion ([§23.5](unsafe-code.md#235-pointer-conversions)) from `T` (the element type) to `V`, an error is produced and no further steps are taken. If `x` has the value `null`, a `System.NullReferenceException` is thrown at run-time. > *Note*: Although pointer types are not permitted as type arguments, pointer arrays may be used as type arguments. *end note* -## 22.6 Pointers in expressions +## 23.6 Pointers in expressions -### 22.6.1 General +### 23.6.1 General -In an unsafe context, an expression may yield a result of a pointer type, but outside an unsafe context, it is a compile-time error for an expression to be of a pointer type. In precise terms, outside an unsafe context a compile-time error occurs if any *simple_name* ([§11.7.4](expressions.md#1174-simple-names)), *member_access* ([§11.7.6](expressions.md#1176-member-access)), *invocation_expression* ([§11.7.8](expressions.md#1178-invocation-expressions)), or *element_access* ([§11.7.10](expressions.md#11710-element-access)) is of a pointer type. +In an unsafe context, an expression may yield a result of a pointer type, but outside an unsafe context, it is a compile-time error for an expression to be of a pointer type. In precise terms, outside an unsafe context a compile-time error occurs if any *simple_name* ([§12.8.4](expressions.md#1284-simple-names)), *member_access* ([§12.8.7](expressions.md#1287-member-access)), *invocation_expression* ([§12.8.9](expressions.md#1289-invocation-expressions)), or *element_access* ([§12.8.11](expressions.md#12811-element-access)) is of a pointer type. -In an unsafe context, the *primary_no_array_creation_expression* ([§11.7](expressions.md#117-primary-expressions)) and *unary_expression* ([§11.8](expressions.md#118-unary-operators)) productions permit additional constructs, which are described in the following subclauses. +In an unsafe context, the *primary_no_array_creation_expression* ([§12.8](expressions.md#128-primary-expressions)) and *unary_expression* ([§12.9](expressions.md#129-unary-operators)) productions permit additional constructs, which are described in the following subclauses. > *Note*: The precedence and associativity of the unsafe operators is implied by the grammar. *end note* -### 22.6.2 Pointer indirection +### 23.6.2 Pointer indirection A *pointer_indirection_expression* consists of an asterisk (`*`) followed by a *unary_expression*. @@ -398,11 +406,11 @@ The effect of applying the unary `*` operator to a `null`-valued pointer is impl If an invalid value has been assigned to the pointer, the behavior of the unary `*` operator is undefined. -> *Note*: Among the invalid values for dereferencing a pointer by the unary `*` operator are an address inappropriately aligned for the type pointed to (see example in [§22.5](unsafe-code.md#225-pointer-conversions)), and the address of a variable after the end of its lifetime. +> *Note*: Among the invalid values for dereferencing a pointer by the unary `*` operator are an address inappropriately aligned for the type pointed to (see example in [§23.5](unsafe-code.md#235-pointer-conversions)), and the address of a variable after the end of its lifetime. For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form `*P` is considered initially assigned ([§9.4.2](variables.md#942-initially-assigned-variables)). -### 22.6.3 Pointer member access +### 23.6.3 Pointer member access A *pointer_member_access* consists of a *primary_expression*, followed by a “`->`” token, followed by an *identifier* and an optional *type_argument_list*. @@ -414,7 +422,7 @@ pointer_member_access In a pointer member access of the form `P->I`, `P` shall be an expression of a pointer type, and `I` shall denote an accessible member of the type to which `P` points. -A pointer member access of the form `P->I` is evaluated exactly as `(*P).I`. For a description of the pointer indirection operator (`*`), see [§22.6.2](unsafe-code.md#2262-pointer-indirection). For a description of the member access operator (`.`), see [§11.7.6](expressions.md#1176-member-access). +A pointer member access of the form `P->I` is evaluated exactly as `(*P).I`. For a description of the pointer indirection operator (`*`), see [§23.6.2](unsafe-code.md#2362-pointer-indirection). For a description of the member access operator (`.`), see [§12.8.7](expressions.md#1287-member-access). > *Example*: In the following code > @@ -466,7 +474,7 @@ A pointer member access of the form `P->I` is evaluated exactly as `(*P).I`. For > > *end example* -### 22.6.4 Pointer element access +### 23.6.4 Pointer element access A *pointer_element_access* consists of a *primary_no_array_creation_expression* followed by an expression enclosed in “`[`” and “`]`”. @@ -478,7 +486,7 @@ pointer_element_access In a pointer element access of the form `P[E]`, `P` shall be an expression of a pointer type other than `void*`, and `E` shall be an expression that can be implicitly converted to `int`, `uint`, `long`, or `ulong`. -A pointer element access of the form `P[E]` is evaluated exactly as `*(P + E)`. For a description of the pointer indirection operator (`*`), see [§22.6.2](unsafe-code.md#2262-pointer-indirection). For a description of the pointer addition operator (`+`), see [§22.6.7](unsafe-code.md#2267-pointer-arithmetic). +A pointer element access of the form `P[E]` is evaluated exactly as `*(P + E)`. For a description of the pointer indirection operator (`*`), see [§23.6.2](unsafe-code.md#2362-pointer-indirection). For a description of the pointer addition operator (`+`), see [§23.6.7](unsafe-code.md#2367-pointer-arithmetic). > *Example*: In the following code > @@ -526,7 +534,7 @@ The pointer element access operator does not check for out-of-bounds errors and > *Note*: This is the same as C and C++. *end note* -### 22.6.5 The address-of operator +### 23.6.5 The address-of operator An *addressof_expression* consists of an ampersand (`&`) followed by a *unary_expression*. @@ -536,9 +544,9 @@ addressof_expression ; ``` -Given an expression `E` which is of a type `T` and is classified as a fixed variable ([§22.4](unsafe-code.md#224-fixed-and-moveable-variables)), the construct `&E` computes the address of the variable given by `E`. The type of the result is `T*` and is classified as a value. A compile-time error occurs if `E` is not classified as a variable, if `E` is classified as a read-only local variable, or if `E` denotes a moveable variable. In the last case, a fixed statement ([§22.7](unsafe-code.md#227-the-fixed-statement)) can be used to temporarily “fix” the variable before obtaining its address. +Given an expression `E` which is of a type `T` and is classified as a fixed variable ([§23.4](unsafe-code.md#234-fixed-and-moveable-variables)), the construct `&E` computes the address of the variable given by `E`. The type of the result is `T*` and is classified as a value. A compile-time error occurs if `E` is not classified as a variable, if `E` is classified as a read-only local variable, or if `E` denotes a moveable variable. In the last case, a fixed statement ([§23.7](unsafe-code.md#237-the-fixed-statement)) can be used to temporarily “fix” the variable before obtaining its address. -> *Note*: As stated in [§11.7.6](expressions.md#1176-member-access), outside an instance constructor or static constructor for a struct or class that defines a `readonly` field, that field is considered a value, not a variable. As such, its address cannot be taken. Similarly, the address of a constant cannot be taken. +> *Note*: As stated in [§12.8.7](expressions.md#1287-member-access), outside an instance constructor or static constructor for a struct or class that defines a `readonly` field, that field is considered a value, not a variable. As such, its address cannot be taken. Similarly, the address of a constant cannot be taken. The `&` operator does not require its argument to be definitely assigned, but following an `&` operation, the variable to which the operator is applied is considered definitely assigned in the execution path in which the operation occurs. It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation. @@ -571,24 +579,24 @@ The `&` operator does not require its argument to be definitely assigned, but fo -> *Note*: When a local variable, value parameter, or parameter array is captured by an anonymous function ([§11.7.21](expressions.md#11721-anonymous-method-expressions)), that local variable, parameter, or parameter array is no longer considered to be a fixed variable ([§22.7](unsafe-code.md#227-the-fixed-statement)), but is instead considered to be a moveable variable. Thus it is an error for any unsafe code to take the address of a local variable, value parameter, or parameter array that has been captured by an anonymous function. *end note* +> *Note*: When a local variable, value parameter, or parameter array is captured by an anonymous function ([§12.8.23](expressions.md#12823-anonymous-method-expressions)), that local variable, parameter, or parameter array is no longer considered to be a fixed variable ([§23.7](unsafe-code.md#237-the-fixed-statement)), but is instead considered to be a moveable variable. Thus it is an error for any unsafe code to take the address of a local variable, value parameter, or parameter array that has been captured by an anonymous function. *end note* -### 22.6.6 Pointer increment and decrement +### 23.6.6 Pointer increment and decrement -In an unsafe context, the `++` and `--` operators ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators) and [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)) can be applied to pointer variables of all types except `void*`. Thus, for every pointer type `T*`, the following operators are implicitly defined: +In an unsafe context, the `++` and `--` operators ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators) and [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)) can be applied to pointer variables of all types except `void*`. Thus, for every pointer type `T*`, the following operators are implicitly defined: ```csharp T* operator ++(T* x); T* operator --(T* x); ``` -The operators produce the same results as `x+1` and `x-1`, respectively ([§22.6.7](unsafe-code.md#2267-pointer-arithmetic)). In other words, for a pointer variable of type `T*`, the `++` operator adds `sizeof(T)` to the address contained in the variable, and the `--` operator subtracts `sizeof(T)` from the address contained in the variable. +The operators produce the same results as `x+1` and `x-1`, respectively ([§23.6.7](unsafe-code.md#2367-pointer-arithmetic)). In other words, for a pointer variable of type `T*`, the `++` operator adds `sizeof(T)` to the address contained in the variable, and the `--` operator subtracts `sizeof(T)` from the address contained in the variable. If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced. -### 22.6.7 Pointer arithmetic +### 23.6.7 Pointer arithmetic -In an unsafe context, the `+` operator ([§11.9.5](expressions.md#1195-addition-operator)) and `–` operator ([§11.9.6](expressions.md#1196-subtraction-operator)) can be applied to values of all pointer types except `void*`. Thus, for every pointer type `T*`, the following operators are implicitly defined: +In an unsafe context, the `+` operator ([§12.10.5](expressions.md#12105-addition-operator)) and `–` operator ([§12.10.6](expressions.md#12106-subtraction-operator)) can be applied to values of all pointer types except `void*`. Thus, for every pointer type `T*`, the following operators are implicitly defined: ```csharp T* operator +(T* x, int y); @@ -641,9 +649,9 @@ Given two expressions, `P` and `Q`, of a pointer type `T*`, the expression `P If a pointer arithmetic operation overflows the domain of the pointer type, the result is truncated in an implementation-defined fashion, but no exceptions are produced. -### 22.6.8 Pointer comparison +### 23.6.8 Pointer comparison -In an unsafe context, the `==`, `!=`, `<`, `>`, `<=`, and `>=` operators ([§11.11](expressions.md#1111-relational-and-type-testing-operators)) can be applied to values of all pointer types. The pointer comparison operators are: +In an unsafe context, the `==`, `!=`, `<`, `>`, `<=`, and `>=` operators ([§12.12](expressions.md#1212-relational-and-type-testing-operators)) can be applied to values of all pointer types. The pointer comparison operators are: ```csharp bool operator ==(void* x, void* y); @@ -656,9 +664,9 @@ bool operator >=(void* x, void* y); Because an implicit conversion exists from any pointer type to the `void*` type, operands of any pointer type can be compared using these operators. The comparison operators compare the addresses given by the two operands as if they were unsigned integers. -### 22.6.9 The sizeof operator +### 23.6.9 The sizeof operator -For certain predefined types ([§11.7.17](expressions.md#11717-the-sizeof-operator)), the `sizeof` operator yields a constant `int` value. For all other types, the result of the `sizeof` operator is implementation-defined and is classified as a value, not a constant. +For certain predefined types ([§12.8.18](expressions.md#12818-the-sizeof-operator)), the `sizeof` operator yields a constant `int` value. For all other types, the result of the `sizeof` operator is implementation-defined and is classified as a value, not a constant. The order in which members are packed into a struct is unspecified. @@ -666,9 +674,9 @@ For alignment purposes, there may be unnamed padding at the beginning of a struc When applied to an operand that has struct type, the result is the total number of bytes in a variable of that type, including any padding. -## 22.7 The fixed statement +## 23.7 The fixed statement -In an unsafe context, the *embedded_statement* ([§12.1](statements.md#121-general)) production permits an additional construct, the fixed statement, which is used to “fix” a moveable variable such that its address remains constant for the duration of the statement. +In an unsafe context, the *embedded_statement* ([§13.1](statements.md#131-general)) production permits an additional construct, the fixed statement, which is used to “fix” a moveable variable such that its address remains constant for the duration of the statement. ```ANTLR fixed_statement @@ -691,12 +699,13 @@ fixed_pointer_initializer Each *fixed_pointer_declarator* declares a local variable of the given *pointer_type* and initializes that local variable with the address computed by the corresponding *fixed_pointer_initializer*. A local variable declared in a fixed statement is accessible in any *fixed_pointer_initializer*s occurring to the right of that variable’s declaration, and in the *embedded_statement* of the fixed statement. A local variable declared by a fixed statement is considered read-only. A compile-time error occurs if the embedded statement attempts to modify this local variable (via assignment or the `++` and `--` operators) or pass it as a `ref` or `out` parameter. -It is an error to use a captured local variable ([§11.17.6.2](expressions.md#111762-captured-outer-variables)), value parameter, or parameter array in a *fixed_pointer_initializer*. A *fixed_pointer_initializer* can be one of the following: +It is an error to use a captured local variable ([§12.19.6.2](expressions.md#121962-captured-outer-variables)), value parameter, or parameter array in a *fixed_pointer_initializer*. A *fixed_pointer_initializer* can be one of the following: -- The token “`&`” followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) to a moveable variable ([§22.4](unsafe-code.md#224-fixed-and-moveable-variables)) of an unmanaged type `T`, provided the type `T*` is implicitly convertible to the pointer type given in the `fixed` statement. In this case, the initializer computes the address of the given variable, and the variable is guaranteed to remain at a fixed address for the duration of the fixed statement. +- The token “`&`” followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)) to a moveable variable ([§23.4](unsafe-code.md#234-fixed-and-moveable-variables)) of an unmanaged type `T`, provided the type `T*` is implicitly convertible to the pointer type given in the `fixed` statement. In this case, the initializer computes the address of the given variable, and the variable is guaranteed to remain at a fixed address for the duration of the fixed statement. - An expression of an *array_type* with elements of an unmanaged type `T`, provided the type `T*` is implicitly convertible to the pointer type given in the fixed statement. In this case, the initializer computes the address of the first element in the array, and the entire array is guaranteed to remain at a fixed address for the duration of the `fixed` statement. If the array expression is `null` or if the array has zero elements, the initializer computes an address equal to zero. - An expression of type `string`, provided the type `char*` is implicitly convertible to the pointer type given in the `fixed` statement. In this case, the initializer computes the address of the first character in the string, and the entire string is guaranteed to remain at a fixed address for the duration of the `fixed` statement. The behavior of the `fixed` statement is implementation-defined if the string expression is `null`. -- A *simple_name* or *member_access* that references a fixed-size buffer member of a moveable variable, provided the type of the fixed-size buffer member is implicitly convertible to the pointer type given in the `fixed` statement. In this case, the initializer computes a pointer to the first element of the fixed-size buffer ([§22.8.3](unsafe-code.md#2283-fixed-size-buffers-in-expressions)), and the fixed-size buffer is guaranteed to remain at a fixed address for the duration of the `fixed` statement. +- An expression of type other than *array_type* or `string`, provided there exists an accessible method or accessible extension method matching the signature `ref [readonly] T GetPinnableReference()`, where `T` is an *unmanaged_type*, and `T*` is implicitly convertible to the pointer type given in the `fixed` statement. In this case, the initializer computes the address of the returned variable, and that variable is guaranteed to remain at a fixed address for the duration of the `fixed` statement. A `GetPinnableReference()` method can be used by the `fixed` statement when overload resolution ([§12.6.4](expressions.md#1264-overload-resolution)) produces exactly one function member and that function member satisfies the preceding conditions. The `GetPinnableReference` method should return a reference to an address equal to zero, such as that returned from `System.Runtime.CompilerServices.Unsafe.NullRef()` when there is no data to pin. +- A *simple_name* or *member_access* that references a fixed-size buffer member of a moveable variable, provided the type of the fixed-size buffer member is implicitly convertible to the pointer type given in the `fixed` statement. In this case, the initializer computes a pointer to the first element of the fixed-size buffer ([§23.8.3](unsafe-code.md#2383-fixed-size-buffers-in-expressions)), and the fixed-size buffer is guaranteed to remain at a fixed address for the duration of the `fixed` statement. For each address computed by a *fixed_pointer_initializer* the `fixed` statement ensures that the variable referenced by the address is not subject to relocation or disposal by the garbage collector for the duration of the `fixed` statement. @@ -745,7 +754,7 @@ Fixed objects can cause fragmentation of the heap (because they can’t be moved In an unsafe context, array elements of single-dimensional arrays are stored in increasing index order, starting with index `0` and ending with index `Length – 1`. For multi-dimensional arrays, array elements are stored such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left. -Within a `fixed` statement that obtains a pointer `p` to an array instance `a`, the pointer values ranging from `p` to `p + a.Length - 1` represent addresses of the elements in the array. Likewise, the variables ranging from `p[0]` to `p[a.Length - 1]` represent the actual array elements. Given the way in which arrays are stored, we can treat an array of any dimension as though it were linear. +Within a `fixed` statement that obtains a pointer `p` to an array instance `a`, the pointer values ranging from `p` to `p + a.Length - 1` represent addresses of the elements in the array. Likewise, the variables ranging from `p[0]` to `p[a.Length - 1]` represent the actual array elements. Given the way in which arrays are stored, an array of any dimension can be treated as though it were linear. > *Example*: > @@ -856,6 +865,34 @@ A `char*` value produced by fixing a string instance always points to a null-ter > ``` > > *end example* + + + +> *Example*: The following code shows a *fixed_pointer_initializer* with an expression of type other than *array_type* or `string`: +> +> +> ```csharp +> public class C +> { +> private int _value; +> public C(int value) => _value = value; +> public ref int GetPinnableReference() => ref _value; +> } +> +> public class Test +> { +> unsafe private static void Main() +> { +> C c = new C(10); +> fixed (int* p = c) +> { +> // ... +> } +> } +> } +> ``` +> +> Type `C` has an accessible `GetPinnableReference` method with the correct signature. In the `fixed` statement, the `ref int` returned from that method when it is called on `c` is used to initialize the `int*` pointer `p`. *end example* Modifying objects of managed type through fixed pointers can result in undefined behavior. @@ -865,19 +902,19 @@ Modifying objects of managed type through fixed pointers can result in undefined > *Note*: The automatic null-termination of strings is particularly convenient when calling external APIs that expect “C-style” strings. Note, however, that a string instance is permitted to contain null characters. If such null characters are present, the string will appear truncated when treated as a null-terminated `char*`. *end note* -## 22.8 Fixed-size buffers +## 23.8 Fixed-size buffers -### 22.8.1 General +### 23.8.1 General Fixed-size buffers are used to declare “C-style” in-line arrays as members of structs, and are primarily useful for interfacing with unmanaged APIs. -### 22.8.2 Fixed-size buffer declarations +### 23.8.2 Fixed-size buffer declarations A ***fixed-size buffer*** is a member that represents storage for a fixed-length buffer of variables of a given type. A fixed-size buffer declaration introduces one or more fixed-size buffers of a given element type. -> *Note*: Like an array, a fixed-size buffer can be thought of as containing elements. As such, the term *element type* as defined for an array is also used with a fixed-size buffer. *end note* +> *Note*: Like an array, a fixed-size buffer can be thought of as containing elements. As such, the term *element type* as defined for an array is also used with a fixed-size buffer. *end note* -Fixed-size buffers are only permitted in struct declarations and may only occur in unsafe contexts ([§22.2](unsafe-code.md#222-unsafe-contexts)). +Fixed-size buffers are only permitted in struct declarations and may only occur in unsafe contexts ([§23.2](unsafe-code.md#232-unsafe-contexts)). ```ANTLR fixed_size_buffer_declaration @@ -906,7 +943,7 @@ fixed_size_buffer_declarator ; ``` -A fixed-size buffer declaration may include a set of attributes ([§21](attributes.md#21-attributes)), a `new` modifier ([§14.3.5](classes.md#1435-the-new-modifier)), accessibility modifiers corresponding to any of the declared accessibilities permitted for struct members ([§15.4.3](structs.md#1543-inheritance)) and an `unsafe` modifier ([§22.2](unsafe-code.md#222-unsafe-contexts)). The attributes and modifiers apply to all of the members declared by the fixed-size buffer declaration. It is an error for the same modifier to appear multiple times in a fixed-size buffer declaration. +A fixed-size buffer declaration may include a set of attributes ([§22](attributes.md#22-attributes)), a `new` modifier ([§15.3.5](classes.md#1535-the-new-modifier)), accessibility modifiers corresponding to any of the declared accessibilities permitted for struct members ([§16.4.3](structs.md#1643-inheritance)) and an `unsafe` modifier ([§23.2](unsafe-code.md#232-unsafe-contexts)). The attributes and modifiers apply to all of the members declared by the fixed-size buffer declaration. It is an error for the same modifier to appear multiple times in a fixed-size buffer declaration. A fixed-size buffer declaration is not permitted to include the `static` modifier. @@ -942,19 +979,22 @@ A fixed-size buffer declaration that declares multiple fixed-size buffers is equ > > *end example* -### 22.8.3 Fixed-size buffers in expressions +### 23.8.3 Fixed-size buffers in expressions -Member lookup ([§11.5](expressions.md#115-member-lookup)) of a fixed-size buffer member proceeds exactly like member lookup of a field. +Member lookup ([§12.5](expressions.md#125-member-lookup)) of a fixed-size buffer member proceeds exactly like member lookup of a field. -A fixed-size buffer can be referenced in an expression using a *simple_name* ([§11.6.3](expressions.md#1163-type-inference)) or a *member_access* ([§11.6.5](expressions.md#1165-compile-time-checking-of-dynamic-member-invocation)). +A fixed-size buffer can be referenced in an expression using a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)), a *member_access* ([§12.8.7](expressions.md#1287-member-access)), or an *element_access* ([§12.8.11](expressions.md#12811-element-access)). When a fixed-size buffer member is referenced as a simple name, the effect is the same as a member access of the form `this.I`, where `I` is the fixed-size buffer member. -In a member access of the form `E.I`, if `E` is of a struct type and a member lookup of `I` in that struct type identifies a fixed-size member, then `E.I` is evaluated an classified as follows: +In a member access of the form `E.I` where `E.` may be the implicit `this.`, if `E` is of a struct type and a member lookup of `I` in that struct type identifies a fixed-size member, then `E.I` is evaluated and classified as follows: - If the expression `E.I` does not occur in an unsafe context, a compile-time error occurs. - If `E` is classified as a value, a compile-time error occurs. -- Otherwise, if `E` is a moveable variable ([§22.4](unsafe-code.md#224-fixed-and-moveable-variables)) and the expression `E.I` is not a *fixed_pointer_initializer* ([§22.7](unsafe-code.md#227-the-fixed-statement)), a compile-time error occurs. +- Otherwise, if `E` is a moveable variable ([§23.4](unsafe-code.md#234-fixed-and-moveable-variables)) then: + - If the expression `E.I` is a *fixed_pointer_initializer* ([§23.7](unsafe-code.md#237-the-fixed-statement)), then the result of the expression is a pointer to the first element of the fixed size buffer member `I` in `E`. + - Otherwise if the expression `E.I` is a *primary_no_array_creation_expression* ([§12.8.11.1](expressions.md#128111-general)) within an *element_access* ([§12.8.11](expressions.md#12811-element-access)) of the form `E.I[J]`, then the result of `E.I` is a pointer, `P`, to the first element of the fixed size buffer member `I` in `E`, and the enclosing *element_access* is then evaluated as the *pointer_element_access* ([§23.6.4](unsafe-code.md#2364-pointer-element-access)) `P[J]`. + - Otherwise a compile-time error occurs. - Otherwise, `E` references a fixed variable and the result of the expression is a pointer to the first element of the fixed-size buffer member `I` in `E`. The result is of type `S*`, where S is the element type of `I`, and is classified as a value. The subsequent elements of the fixed-size buffer can be accessed using pointer operations from the first element. Unlike access to arrays, access to the elements of a fixed-size buffer is an unsafe operation and is not range checked. @@ -999,41 +1039,50 @@ The subsequent elements of the fixed-size buffer can be accessed using pointer o > > *end example* -### 22.8.4 Definite assignment checking +### 23.8.4 Definite assignment checking Fixed-size buffers are not subject to definite assignment-checking ([§9.4](variables.md#94-definite-assignment)), and fixed-size buffer members are ignored for purposes of definite-assignment checking of struct type variables. When the outermost containing struct variable of a fixed-size buffer member is a static variable, an instance variable of a class instance, or an array element, the elements of the fixed-size buffer are automatically initialized to their default values ([§9.3](variables.md#93-default-values)). In all other cases, the initial content of a fixed-size buffer is undefined. -## 22.9 Stack allocation - -In an unsafe context, a local variable declaration ([§12.6.2](statements.md#1262-local-variable-declarations)) may include a stack allocation initializer, which allocates memory from the call stack. +## 23.9 Stack allocation -```ANTLR -stackalloc_initializer - : 'stackalloc' unmanaged_type '[' expression ']' - ; -``` - -The *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)) indicates the type of the items that will be stored in the newly allocated location, and the *expression* indicates the number of these items. Taken together, these specify the required allocation size. Since the size of a stack allocation cannot be negative, it is a compile-time error to specify the number of items as a *constant_expression* that evaluates to a negative value. +See [§12.8.21](expressions.md#12821-stack-allocation) for general information about the operator `stackalloc`. Here, the ability of that operator to result in a pointer is discussed. -A stack allocation initializer of the form stackalloc `T[E]` requires `T` to be an unmanaged type ([§22.3](unsafe-code.md#223-pointer-types)) and `E` to be an expression implicitly convertible to type `int`. The construct allocates `E * sizeof(T)` bytes from the call stack and returns a pointer, of type `T*`, to the newly allocated block. If `E` is a negative value, then the behavior is undefined. If `E` is zero, then no allocation is made, and the pointer returned is implementation-defined. If there is not enough memory available to allocate a block of the given size, a `System.StackOverflowException` is thrown. +In an unsafe context if a *stackalloc_expression* ([§12.8.21](expressions.md#12821-stack-allocation)) occurs as the initializing expression of a *local_variable_declaration* ([§13.6.2](statements.md#1362-local-variable-declarations)), where the *local_variable_type* is either a pointer type ([§23.3](unsafe-code.md#233-pointer-types)) or inferred (`var`), then the result of the *stackalloc_expression* is a pointer of type `T *` to be beginning of the allocated block, where `T` is the *unmanaged_type* of the *stackalloc_expression*. -The content of the newly allocated memory is undefined. +In all other respects the semantics of *local_variable_declaration*s ([§13.6.2](statements.md#1362-local-variable-declarations)) and *stackalloc_expression*s ([§12.8.21](expressions.md#12821-stack-allocation)) in unsafe contexts follow those defined for safe contexts. -Stack allocation initializers are not permitted in `catch` or `finally` blocks ([§12.11](statements.md#1211-the-try-statement)). - -> *Note*: There is no way to explicitly free memory allocated using stackalloc. *end note* - -All stack-allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. +> *Example*: +> +> +> ```csharp +> unsafe +> { +> // Memory uninitialized +> int* p1 = stackalloc int[3]; +> // Memory initialized +> int* p2 = stackalloc int[3] { -10, -15, -30 }; +> // Type int is inferred +> int* p3 = stackalloc[] { 11, 12, 13 }; +> // Can't infer context, so pointer result assumed +> var p4 = stackalloc[] { 11, 12, 13 }; +> // Error; no conversion exists +> long* p5 = stackalloc[] { 11, 12, 13 }; +> // Converts 11 and 13, and returns long* +> long* p6 = stackalloc[] { 11, 12L, 13 }; +> // Converts all and returns long* +> long* p7 = stackalloc long[] { 11, 12, 13 }; +> } +> ``` +> +> *end example* -> *Note*: This corresponds to the `alloca` function, an extension commonly found in C and C++ implementations. *end note* - +Unlike access to arrays or `stackalloc`’ed blocks of `Span` type, access to the elements of a `stackalloc`’ed block of pointer type is an unsafe operation and is not range checked. - > *Example*: In the following code > -> +> > ```csharp > class Test > { @@ -1069,10 +1118,37 @@ All stack-allocated memory blocks created during the execution of a function mem > } > ``` > -> a `stackalloc` initializer is used in the `IntToString` method to allocate a buffer of 16 characters on the stack. The buffer is automatically discarded when the method returns. +> a `stackalloc` expression is used in the `IntToString` method to allocate a buffer of 16 characters on the stack. The buffer is automatically discarded when the method returns. +> +> Note, however, that `IntToString` can be rewritten in safe mode; that is, without using pointers, as follows: +> +> +> ```csharp +> class Test +> { +> static string IntToString(int value) +> { +> if (value == int.MinValue) +> { +> return "-2147483648"; +> } +> int n = value >= 0 ? value : -value; +> Span buffer = stackalloc char[16]; +> int idx = 16; +> do +> { +> buffer[--idx] = (char)(n % 10 + '0'); +> n /= 10; +> } while (n != 0); +> if (value < 0) +> { +> buffer[--idx] = '-'; +> } +> return buffer.Slice(idx).ToString(); +> } +> } +> ``` > > *end example* -Except for the `stackalloc` operator, C# provides no predefined constructs for managing non-garbage collected memory. Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. - **End of conditionally normative text.** diff --git a/standard/variables.md b/standard/variables.md index d9df523dd..f088735e5 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -12,7 +12,7 @@ As described in the following subclauses, variables are either ***initially assi ### 9.2.1 General -C# defines seven categories of variables: static variables, instance variables, array elements, value parameters, reference parameters, output parameters, and local variables. The subclauses that follow describe each of these categories. +C# defines eight categories of variables: static variables, instance variables, array elements, value parameters, input parameters, reference parameters, output parameters, and local variables. The subclauses that follow describe each of these categories. > *Example*: In the following code > @@ -23,21 +23,19 @@ C# defines seven categories of variables: static variables, instance variables, > public static int x; > int y; > -> void F(int[] v, int a, ref int b, out int c) +> void F(int[] v, int a, ref int b, out int c, in int d) > { > int i = 1; -> c = a + b++; +> c = a + b++ + d; > } > } > ``` > -> `x` is a static variable, `y` is an instance variable, `v[0]` is an array element, `a` is a value parameter, `b` is a reference parameter, `c` is an output parameter, and `i` is a local variable. -> -> *end example* +> `x` is a static variable, `y` is an instance variable, `v[0]` is an array element, `a` is a value parameter, `b` is a reference parameter, `c` is an output parameter, `d` is an input parameter, and `i` is a local variable. *end example* ### 9.2.2 Static variables -A field declared with the `static` modifier is a static variable. A static variable comes into existence before execution of the `static` constructor ([§14.12](classes.md#1412-static-constructors)) for its containing type, and ceases to exist when the associated application domain ceases to exist. +A field declared with the `static` modifier is a static variable. A static variable comes into existence before execution of the `static` constructor ([§15.12](classes.md#1512-static-constructors)) for its containing type, and ceases to exist when the associated application domain ceases to exist. The initial value of a static variable is the default value ([§9.3](variables.md#93-default-values)) of the variable’s type. @@ -73,9 +71,9 @@ For the purpose of definite-assignment checking, an array element is considered ### 9.2.5 Value parameters -A parameter declared without a `ref` or `out` modifier is a ***value parameter***. +A parameter declared without an `in`, `out`, or `ref` modifier is a ***value parameter***. -A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist when execution of the function body completes. However, if the value parameter is captured by an anonymous function ([§11.17.6.2](expressions.md#111762-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection. +A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist when execution of the function body completes. However, if the value parameter is captured by an anonymous function ([§12.19.6.2](expressions.md#121962-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection. For the purpose of definite-assignment checking, a value parameter is considered initially assigned. @@ -83,7 +81,7 @@ For the purpose of definite-assignment checking, a value parameter is considered A parameter declared with a `ref` modifier is a ***reference parameter***. -A reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the function member, anonymous function, or local function invocation. Thus, the value of a reference parameter is always the same as the underlying variable. +A reference parameter is a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)) which comes into existence upon invocation of the function member, delegate, anonymous function, or local function and its referent is initialized to the variable given as the argument in that invocation. A reference parameter ceases to exist when execution of the function body completes. Unlike value parameters a reference parameter may not be captured ([§9.7.2.9](variables.md#9729-limitations-on-reference-variables)). The following definite-assignment rules apply to reference parameters. @@ -92,13 +90,13 @@ The following definite-assignment rules apply to reference parameters. - A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as a reference parameter in a function member or delegate invocation. - Within a function member or anonymous function, a reference parameter is considered initially assigned. -For a `struct` type, within an instance method or instance accessor ([§11.2.1](expressions.md#1121-general)) or instance constructor with a constructor initializer, the `this` keyword behaves exactly as a reference parameter of the struct type ([§11.7.12](expressions.md#11712-this-access)). +For a `struct` type, within an instance method or instance accessor ([§12.2.1](expressions.md#1221-general)) or instance constructor with a constructor initializer, the `this` keyword behaves exactly as a reference parameter of the struct type ([§12.8.13](expressions.md#12813-this-access)). ### 9.2.7 Output parameters A parameter declared with an `out` modifier is an ***output parameter***. -An output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the function member or delegate invocation. Thus, the value of an output parameter is always the same as the underlying variable. +An output parameter is a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)) which comes into existence upon invocation of the function member, delegate, anonymous function, or local function and its referent is initialized to the variable given as the argument in that invocation. An output parameter ceases to exist when execution of the function body completes. Unlike value parameters an output parameter may not be captured ([§9.7.2.9](variables.md#9729-limitations-on-reference-variables)). The following definite-assignment rules apply to output parameters. @@ -109,21 +107,30 @@ The following definite-assignment rules apply to output parameters. - Within a function member or anonymous function, an output parameter is considered initially unassigned. - Every output parameter of a function member, anonymous function, or local function shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before the function member, anonymous function, or local function returns normally. -Within an instance constructor of a struct type, the `this` keyword behaves exactly as an output or reference parameter of the struct type, depending on whether the constructor declaration includes a constructor initializer ([§11.7.12](expressions.md#11712-this-access)). +### 9.2.8 Input parameters + +A parameter declared with an `in` modifier is an ***input parameter***. + +An input parameter is a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)) which comes into existence upon invocation of the function member, delegate, anonymous function, or local function and its referent is initialized to the *variable_reference* given as the argument in that invocation. An input parameter ceases to exist when execution of the function body completes. Unlike value parameters an input parameter may not be captured ([§9.7.2.9](variables.md#9729-limitations-on-reference-variables)). + +The following definite assignment rules apply to input parameters. -### 9.2.8 Local variables +- A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter in a function member or delegate invocation. +- Within a function member, anonymous function, or local function an input parameter is considered initially assigned. -A ***local variable*** is declared by a *local_variable_declaration*, *foreach_statement*, or *specific_catch_clause* of a *try_statement*. For a *foreach_statement*, the local variable is an iteration variable ([§12.9.5](statements.md#1295-the-foreach-statement)). For a *specific_catch_clause*, the local variable is an exception variable ([§12.11](statements.md#1211-the-try-statement)). A local variable declared by a *foreach_statement* or *specific_catch_clause* is considered initially assigned. +### 9.2.9 Local variables -A *local_variable_declaration* can occur in a *block*, a *for_statement*, a *switch_block*, or a *using_statement*. +A ***local variable*** is declared by a *local_variable_declaration*, *declaration_expression*, *foreach_statement*, or *specific_catch_clause* of a *try_statement*. A local variable can also be declared by certain kinds of *pattern*s ([§11](patterns.md#11-patterns-and-pattern-matching)). For a *foreach_statement*, the local variable is an iteration variable ([§13.9.5](statements.md#1395-the-foreach-statement)). For a *specific_catch_clause*, the local variable is an exception variable ([§13.11](statements.md#1311-the-try-statement)). A local variable declared by a *foreach_statement* or *specific_catch_clause* is considered initially assigned. -The lifetime of a local variable is the portion of program execution during which storage is guaranteed to be reserved for it. This lifetime extends from entry into the scope with which it is associated, at least until execution of that scope ends in some way. (Entering an enclosed *block*, calling a method, or yielding a value from an iterator block suspends, but does not end, execution of the current scope.) If the local variable is captured by an anonymous function ([§11.17.6.2](expressions.md#111762-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from the anonymous function, along with any other objects that come to reference the captured variable, are eligible for garbage collection. If the parent scope is entered recursively or iteratively, a new instance of the local variable is created each time, and its *local_variable_initializer*, if any, is evaluated each time. +A *local_variable_declaration* can occur in a *block*, a *for_statement*, a *switch_block*, or a *using_statement*. A *declaration_expression* can occur as an `out` *argument_value*, and as a *tuple_element* that is the target of a deconstructing assignment ([§12.21.2](expressions.md#12212-simple-assignment)). + +The lifetime of a local variable is the portion of program execution during which storage is guaranteed to be reserved for it. This lifetime extends from entry into the scope with which it is associated, at least until execution of that scope ends in some way. (Entering an enclosed *block*, calling a method, or yielding a value from an iterator block suspends, but does not end, execution of the current scope.) If the local variable is captured by an anonymous function ([§12.19.6.2](expressions.md#121962-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from the anonymous function, along with any other objects that come to reference the captured variable, are eligible for garbage collection. If the parent scope is entered recursively or iteratively, a new instance of the local variable is created each time, and its initializer, if any, is evaluated each time. > *Note*: A local variable is instantiated each time its scope is entered. This behavior is visible to user code containing anonymous methods. *end note* -> *Note*: The lifetime of an *iteration variable* ([§12.9.5](statements.md#1295-the-foreach-statement)) declared by a *foreach_statement* is a single iteration of that statement. Each iteration creates a new variable. *end note* +> *Note*: The lifetime of an *iteration variable* ([§13.9.5](statements.md#1395-the-foreach-statement)) declared by a *foreach_statement* is a single iteration of that statement. Each iteration creates a new variable. *end note* @@ -133,9 +140,9 @@ The lifetime of a local variable is the portion of program execution during whic > > *end note* -A local variable introduced by a *local_variable_declaration* is not automatically initialized and thus has no default value. Such a local variable is considered initially unassigned. +A local variable introduced by a *local_variable_declaration* or *declaration_expression* is not automatically initialized and thus has no default value. Such a local variable is considered initially unassigned. -> *Note*: A *local_variable_declaration* that includes a *local_variable_initializer* is still initially unassigned. Execution of the declaration behaves exactly like an assignment to the variable ([§9.4.4.5](variables.md#9445-declaration-statements)). It is possible to use a variable without executing its *local_variable_initializer*; e.g., within the initializer expression itself or by using a *goto_statement* to bypass the initialization: +> *Note*: A *local_variable_declaration* that includes an initializer is still initially unassigned. Execution of the declaration behaves exactly like an assignment to the variable ([§9.4.4.5](variables.md#9445-declaration-statements)). Using a variable before its initializer has been executed; e.g., within the initializer expression itself or by using a *goto_statement* which bypasses the initializer; is a compile-time error: > > > ```csharp @@ -146,10 +153,38 @@ A local variable introduced by a *local_variable_declaration* is not automatical > L: x += 1; // error: x not definitely assigned > ``` > -> Within the scope of a local variable, it is a compile-time error to refer to that local variable in a textual position that precedes its *local_variable_declarator*. +> Within the scope of a local variable, it is a compile-time error to refer to that local variable in a textual position that precedes its declarator. > > *end note* +#### 9.2.9.1 Discards + +A ***discard*** is a local variable that has no name. A discard is introduced by a declaration expression ([§12.17](expressions.md#1217-declaration-expressions)) with the identifier `_`; and is either implicitly typed (`_` or `var _`) or explicitly typed (`T _`). + +> *Note*: `_` is a valid identifier in many forms of declarations. *end note* + +Because a discard has no name, the only reference to the variable it represents is the expression that introduces it. + +> *Note*: A discard can however be passed as an out argument, allowing the out parameter to denote its associated storage location. *end note* + +A discard is not initially assigned, so it is always an error to access its value. + +> *Example*: +> +> +> ```csharp +> _ = "Hello".Length; +> (int, int, int) M(out int i1, out int i2, out int i3) { ... } +> (int _, var _, _) = M(out int _, out var _, out _); +> ``` +> +> The example assumes that there is no declaration of the name `_` in scope. +> +> The assignment to `_` shows a simple pattern for ignoring the result of an expression. +> The call of `M` shows the different forms of discards available in tuples and as out parameters. +> +> *end example* + ## 9.3 Default values The following categories of variables are automatically initialized to their default values: @@ -162,7 +197,6 @@ The default value of a variable depends on the type of the variable and is deter - For a variable of a *value_type*, the default value is the same as the value computed by the *value_type*’s default constructor ([§8.3.3](types.md#833-default-constructors)). - For a variable of a *reference_type*, the default value is `null`. -- In an unsafe context, for a variable of a *pointer_type*, the default value is `null`. > *Note*: Initialization to default values is typically done by having the memory manager or garbage collector initialize memory to all-bits-zero before it is allocated for use. For this reason, it is convenient to use all-bits-zero to represent the null reference. *end note* @@ -176,9 +210,9 @@ At a given location in the executable code of a function member or an anonymous > > - An initially assigned variable ([§9.4.2](variables.md#942-initially-assigned-variables)) is always considered definitely assigned. > - An initially unassigned variable ([§9.4.3](variables.md#943-initially-unassigned-variables)) is considered definitely assigned at a given location if all possible execution paths leading to that location contain at least one of the following: -> - A simple assignment ([§11.19.2](expressions.md#11192-simple-assignment)) in which the variable is the left operand. -> - An invocation expression ([§11.7.8](expressions.md#1178-invocation-expressions)) or object creation expression ([§11.7.15.2](expressions.md#117152-object-creation-expressions) that passes the variable as an output parameter. -> - For a local variable, a local variable declaration for the variable ([§12.6.2](statements.md#1262-local-variable-declarations)) that includes a variable initializer. +> - A simple assignment ([§12.21.2](expressions.md#12212-simple-assignment)) in which the variable is the left operand. +> - An invocation expression ([§12.8.9](expressions.md#1289-invocation-expressions)) or object creation expression ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) that passes the variable as an output parameter. +> - For a local variable, a local variable declaration for the variable ([§13.6.2](statements.md#1362-local-variable-declarations)) that includes a variable initializer. > > The formal specification underlying the above informal rules is described in [§9.4.2](variables.md#942-initially-assigned-variables), [§9.4.3](variables.md#943-initially-unassigned-variables), and [§9.4.4](variables.md#944-precise-rules-for-determining-definite-assignment). > @@ -199,6 +233,8 @@ Definite assignment is a requirement in the following contexts: - the variable is a *struct_type* variable and occurs as the left operand of a member access. - A variable shall be definitely assigned at each location where it is passed as a reference parameter. > *Note*: This ensures that the function member being invoked can consider the reference parameter initially assigned. *end note* +- A variable shall be definitely assigned at each location where it is passed as an input parameter. + > *Note*: This ensures that the function member being invoked can consider the input parameter initially assigned. *end note* - All output parameters of a function member shall be definitely assigned at each location where the function member returns (through a return statement or through execution reaching the end of the function member body). > *Note*: This ensures that function members do not return undefined values in output parameters, thus enabling the compiler to consider a function member invocation that takes a variable as an output parameter equivalent to an assignment to the variable. *end note* - The `this` variable of a *struct_type* instance constructor shall be definitely assigned at each location where that instance constructor returns. @@ -213,6 +249,7 @@ The following categories of variables are classified as initially assigned: - Array elements. - Value parameters. - Reference parameters. +- Input parameters. - Variables declared in a `catch` clause or a `foreach` statement. ### 9.4.3 Initially unassigned variables @@ -232,7 +269,7 @@ In order to determine that each used variable is definitely assigned, the compil The compiler processes the body of each function member that has one or more initially unassigned variables. For each initially unassigned variable *v*, the compiler determines a ***definite-assignment state*** for *v* at each of the following points in the function member: - At the beginning of each statement -- At the end point ([§12.2](statements.md#122-end-points-and-reachability)) of each statement +- At the end point ([§13.2](statements.md#132-end-points-and-reachability)) of each statement - On each arc which transfers control to another statement or to the end point of a statement - At the beginning of each expression - At the end of each expression @@ -249,8 +286,8 @@ The following rules govern how the state of a variable *v* is determined at each #### 9.4.4.2 General rules for statements - *v* is not definitely assigned at the beginning of a function member body. -- The definite-assignment state of *v* at the beginning of any other statement is determined by checking the definite-assignment state of *v* on all control flow transfers that target the beginning of that statement. If (and only if) *v* is definitely assigned on all such control flow transfers, then *v* is definitely assigned at the beginning of the statement. The set of possible control flow transfers is determined in the same way as for checking statement reachability ([§12.2](statements.md#122-end-points-and-reachability)). -- The definite-assignment state of *v* at the end point of a `block`, `checked`, `unchecked`, `if`, `while`, `do`, `for`, `foreach`, `lock`, `using`, or `switch` statement is determined by checking the definite-assignment state of *v* on all control flow transfers that target the end point of that statement. If *v* is definitely assigned on all such control flow transfers, then *v* is definitely assigned at the end point of the statement. Otherwise, *v* is not definitely assigned at the end point of the statement. The set of possible control flow transfers is determined in the same way as for checking statement reachability ([§12.2](statements.md#122-end-points-and-reachability)). +- The definite-assignment state of *v* at the beginning of any other statement is determined by checking the definite-assignment state of *v* on all control flow transfers that target the beginning of that statement. If (and only if) *v* is definitely assigned on all such control flow transfers, then *v* is definitely assigned at the beginning of the statement. The set of possible control flow transfers is determined in the same way as for checking statement reachability ([§13.2](statements.md#132-end-points-and-reachability)). +- The definite-assignment state of *v* at the end point of a `block`, `checked`, `unchecked`, `if`, `while`, `do`, `for`, `foreach`, `lock`, `using`, or `switch` statement is determined by checking the definite-assignment state of *v* on all control flow transfers that target the end point of that statement. If *v* is definitely assigned on all such control flow transfers, then *v* is definitely assigned at the end point of the statement. Otherwise, *v* is not definitely assigned at the end point of the statement. The set of possible control flow transfers is determined in the same way as for checking statement reachability ([§13.2](statements.md#132-end-points-and-reachability)). > *Note*: Because there are no control paths to an unreachable statement, *v* is definitely assigned at the beginning of any unreachable statement. *end note* @@ -288,8 +325,51 @@ if ( «expr» ) «then_stmt» else «else_stmt» For a `switch` statement *stmt* with a controlling expression *expr*: -- The definite-assignment state of *v* at the beginning of *expr* is the same as the state of *v* at the beginning of *stmt*. -- The definite-assignment state of *v* on the control flow transfer to a reachable switch block statement list is the same as the definite-assignment state of *v* at the end of *expr*. +The definite-assignment state of *v* at the beginning of *expr* is the same as the state of *v* at the beginning of *stmt*. + +The definite-assignment state of *v* at the beginning of a case’s guard clause is + +- If *v* is a pattern variable declared in the *switch_label*: “definitely assigned”. +- If the switch label containing that guard clause ([§13.8.3](statements.md#1383-the-switch-statement)) is not reachable: “definitely assigned”. +- Otherwise, the state of *v* is the same as the state of *v* after *expr*. + +The definite-assignment state of *v* on the control flow transfer to a reachable switch block statement list is + +- If the control transfer was due to a ‘goto case’ or ‘goto default’ statement, then the state of *v* is the same as the state at the beginning of that ‘goto’ statement. +- If the control transfer was due to the `default` label of the switch, then the state of *v* is the same as the state of *v* after *expr*. +- If the control transfer was due to an unreachable switch label, then the state of *v* is “definitely assigned”. +- If the control transfer was due to a reachable switch label with a guard clause, then the state of *v* is the same as the state of *v* after the guard clause. +- If the control transfer was due to a reachable switch label without a guard clause, then the state of *v* is + - If *v* is a pattern variable declared in the *switch_label*: “definitely assigned”. + - Otherwise, the state of *v* is the same as the stat of *v* after *expr*. + +A consequence of these rules is that a pattern variable declared in a *switch_label* will be “not definitely assigned” in the statements of its switch section if it is not the only reachable switch label in its section. + +> *Example*: +> +> ```csharp +> public static double ComputeArea(object shape) +> { +> switch (shape) +> { +> case Square s when s.Side == 0: +> case Circle c when c.Radius == 0: +> case Triangle t when t.Base == 0 || t.Height == 0: +> case Rectangle r when r.Length == 0 || r.Height == 0: +> // none of s, c, t, or r is definitely assigned +> return 0; +> case Square s: +> // s is definitely assigned +> return s.Side * s.Side; +> case Circle c: +> // c is definitely assigned +> return c.Radius * c.Radius * Math.PI; +> … +> } +> } +> ``` +> +> *end example* #### 9.4.4.8 While statements @@ -322,7 +402,8 @@ do «do_body» while ( «expr» ) ; For a statement of the form: ```csharp -for ( «for_initializer» ; «for_condition» ; «for_iterator» ) «embedded_statement» +for ( «for_initializer» ; «for_condition» ; «for_iterator» ) + «embedded_statement» ``` definite-assignment checking is done as if the statement were written: @@ -433,7 +514,7 @@ try finally «finally_block» ``` -> *Example*: The following example demonstrates how the different blocks of a `try` statement ([§12.11](statements.md#1211-the-try-statement)) affect definite assignment. +> *Example*: The following example demonstrates how the different blocks of a `try` statement ([§13.11](statements.md#1311-the-try-statement)) affect definite assignment. > > > ```csharp @@ -561,13 +642,13 @@ For all other constant expressions, the definite-assignment state of *v* after t #### 9.4.4.22 General rules for simple expressions -The following rule applies to these kinds of expressions: literals ([§11.7.2](expressions.md#1172-literals)), simple names ([§11.7.4](expressions.md#1174-simple-names)), member access expressions ([§11.7.6](expressions.md#1176-member-access)), non-indexed base access expressions ([§11.7.13](expressions.md#11713-base-access)), `typeof` expressions ([§11.7.16](expressions.md#11716-the-typeof-operator)), default value expressions ([§11.7.19](expressions.md#11719-default-value-expressions)), and `nameof` expressions ([§11.7.20](expressions.md#11720-nameof-expressions)). +The following rule applies to these kinds of expressions: literals ([§12.8.2](expressions.md#1282-literals)), simple names ([§12.8.4](expressions.md#1284-simple-names)), member access expressions ([§12.8.7](expressions.md#1287-member-access)), non-indexed base access expressions ([§12.8.14](expressions.md#12814-base-access)), `typeof` expressions ([§12.8.17](expressions.md#12817-the-typeof-operator)), default value expressions ([§12.8.20](expressions.md#12820-default-value-expressions)), `nameof` expressions ([§12.8.22](expressions.md#12822-nameof-expressions)), and declaration expressions ([§12.17](expressions.md#1217-declaration-expressions)). - The definite-assignment state of *v* at the end of such an expression is the same as the definite-assignment state of *v* at the beginning of the expression. #### 9.4.4.23 General rules for expressions with embedded expressions -The following rules apply to these kinds of expressions: parenthesized expressions ([§11.7.5](expressions.md#1175-parenthesized-expressions)), element access expressions ([§11.7.10](expressions.md#11710-element-access)), base access expressions with indexing ([§11.7.13](expressions.md#11713-base-access)), increment and decrement expressions ([§11.7.14](expressions.md#11714-postfix-increment-and-decrement-operators), [§11.8.6](expressions.md#1186-prefix-increment-and-decrement-operators)), cast expressions ([§11.8.7](expressions.md#1187-cast-expressions)), unary `+`, `-`, `~`, `*` expressions, binary `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `is`, `as`, `&`, `|`, `^` expressions ([§11.9](expressions.md#119-arithmetic-operators), [§11.10](expressions.md#1110-shift-operators), [§11.11](expressions.md#1111-relational-and-type-testing-operators), [§11.12](expressions.md#1112-logical-operators)), compound assignment expressions ([§11.19.3](expressions.md#11193-compound-assignment)), `checked` and `unchecked` expressions ([§11.7.18](expressions.md#11718-the-checked-and-unchecked-operators)), array and delegate creation expressions ([§11.7.15](expressions.md#11715-the-new-operator)) , and `await` expressions ([§11.8.8](expressions.md#1188-await-expressions)). +The following rules apply to these kinds of expressions: parenthesized expressions ([§12.8.5](expressions.md#1285-parenthesized-expressions)), tuple expressions ([§12.8.6](expressions.md#1286-tuple-expressions)), element access expressions ([§12.8.11](expressions.md#12811-element-access)), base access expressions with indexing ([§12.8.14](expressions.md#12814-base-access)), increment and decrement expressions ([§12.8.15](expressions.md#12815-postfix-increment-and-decrement-operators), [§12.9.6](expressions.md#1296-prefix-increment-and-decrement-operators)), cast expressions ([§12.9.7](expressions.md#1297-cast-expressions)), unary `+`, `-`, `~`, `*` expressions, binary `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `is`, `as`, `&`, `|`, `^` expressions ([§12.10](expressions.md#1210-arithmetic-operators), [§12.11](expressions.md#1211-shift-operators), [§12.12](expressions.md#1212-relational-and-type-testing-operators), [§12.13](expressions.md#1213-logical-operators)), compound assignment expressions ([§12.21.4](expressions.md#12214-compound-assignment)), `checked` and `unchecked` expressions ([§12.8.19](expressions.md#12819-the-checked-and-unchecked-operators)), array and delegate creation expressions ([§12.8.16](expressions.md#12816-the-new-operator)) , and `await` expressions ([§12.9.8](expressions.md#1298-await-expressions)). Each of these expressions has one or more subexpressions that are unconditionally evaluated in a fixed order. @@ -581,7 +662,7 @@ For an expression *expr*, which has subexpressions *expr₁*, *expr₂*, …, *e #### 9.4.4.24 Invocation expressions and object creation expressions -If the method to be invoked is a partial method that has no implementing partial method declaration, or is a conditional method for which the call is omitted ([§21.5.3.2](attributes.md#21532-conditional-methods)), then the definite-assignment state of *v* after the invocation is the same as the definite-assignment state of *v* before the invocation. Otherwise the following rules apply: +If the method to be invoked is a partial method that has no implementing partial method declaration, or is a conditional method for which the call is omitted ([§22.5.3.2](attributes.md#22532-conditional-methods)), then the definite-assignment state of *v* after the invocation is the same as the definite-assignment state of *v* before the invocation. Otherwise the following rules apply: For an invocation expression *expr* of the form: @@ -595,25 +676,30 @@ or an object-creation expression *expr* of the form: new «type» ( «arg₁», «arg₂», … , «argₓ» ) ``` -- For an invocation expression, the definite-assignment state of *v* before *primary_expression* is the same as the state of *v* before *expr*. -- For an invocation expression, the definite-assignment state of *v* before *arg₁* is the same as the state of *v* after *primary_expression*. -- For an object-creation expression, the definite-assignment state of *v* before *arg₁* is the same as the state of *v* before *expr*. -- For each argument *argᵢ*, the definite-assignment state of *v* after *argᵢ* is determined by the normal expression rules, ignoring any `ref` or `out` modifiers. -- For each argument *argᵢ* for any *i* greater than one, the definite-assignment state of *v* before *argᵢ* is the same as the state of *v* after *argᵢ₋₁*. +- For an invocation expression, the definite assignment state of *v* before *primary_expression* is the same as the state of *v* before *expr*. +- For an invocation expression, the definite assignment state of *v* before *arg₁* is the same as the state of *v* after *primary_expression*. +- For an object creation expression, the definite assignment state of *v* before *arg₁* is the same as the state of *v* before *expr*. +- For each argument *argᵢ*, the definite assignment state of *v* after *argᵢ* is determined by the normal expression rules, ignoring any `in`, `out`, or `ref` modifiers. +- For each argument *argᵢ* for any *i* greater than one, the definite assignment state of *v* before *argᵢ* is the same as the state of *v* after *argᵢ₋₁*. - If the variable *v* is passed as an `out` argument (i.e., an argument of the form “out *v*”) in any of the arguments, then the state of *v* after *expr* is definitely assigned. Otherwise, the state of *v* after *expr* is the same as the state of *v* after *argₓ*. -- For array initializers ([§11.7.15.5](expressions.md#117155-array-creation-expressions)), object initializers ([§11.7.15.3](expressions.md#117153-object-initializers)), collection initializers ([§11.7.15.4](expressions.md#117154-collection-initializers)) and anonymous object initializers ([§11.7.15.7](expressions.md#117157-anonymous-object-creation-expressions)), the definite-assignment state is determined by the expansion that these constructs are defined in terms of. +- For array initializers ([§12.8.16.5](expressions.md#128165-array-creation-expressions)), object initializers ([§12.8.16.3](expressions.md#128163-object-initializers)), collection initializers ([§12.8.16.4](expressions.md#128164-collection-initializers)) and anonymous object initializers ([§12.8.16.7](expressions.md#128167-anonymous-object-creation-expressions)), the definite-assignment state is determined by the expansion that these constructs are defined in terms of. #### 9.4.4.25 Simple assignment expressions +Let the set of *assignment targets* in an expression *e* be defined as follows: + +- If *e* is a tuple expression, then the assignment targets in *e* are the union of the assignment targets of the elements of *e*. +- Otherwise, the assignment targets in *e* are *e*. + For an expression *expr* of the form: ```csharp -«w» = «expr_rhs» +«expr_lhs» = «expr_rhs» ``` -- The definite-assignment state of *v* before *w* is the same as the definite-assignment state of *v* before *expr*. -- The definite-assignment state of *v* before *expr_rhs* is the same as the definite-assignment state of *v* after *w*. -- If *w* is the same variable as *v*, then the definite-assignment state of *v* after *expr* is definitely assigned. Otherwise, if the assignment occurs within the instance constructor of a struct type, and *w* is a property access designating an automatically implemented property *P* on the instance being constructed and *v* is the hidden backing field of *P*, then the definite-assignment state of *v* after *expr* is definitely assigned. Otherwise, the definite-assignment state of *v* after *expr* is the same as the definite-assignment state of *v* after *expr_rhs*. +- The definite-assignment state of *v* before *expr_lhs* is the same as the definite-assignment state of *v* before *expr*. +- The definite-assignment state of *v* before *expr_rhs* is the same as the definite-assignment state of *v* after *expr_lhs*. +- If *v* is an assignment target of *expr_lhs*, then the definite-assignment state of *v* after *expr* is definitely assigned. Otherwise, if the assignment occurs within the instance constructor of a struct type, and *v* is the hidden backing field of an automatically implemented property *P* on the instance being constructed, and a property access designating *P* is an assigment target of *expr_lhs*, then the definite-assignment state of *v* after *expr* is definitely assigned. Otherwise, the definite-assignment state of *v* after *expr* is the same as the definite-assignment state of *v* after *expr_rhs*. > *Example*: In the following code > @@ -745,7 +831,7 @@ For an expression *expr* of the form: - The definite-assignment state of *v* before *expr_first* is the same as the definite-assignment state of *v* before *expr*. - The definite-assignment state of *v* before *expr_second* is the same as the definite-assignment state of *v* after *expr_first*. - The definite-assignment statement of *v* after *expr* is determined by: - - If *expr_first* is a constant expression ([§11.21](expressions.md#1121-constant-expressions)) with value `null`, then the state of *v* after *expr* is the same as the state of *v* after *expr_second*. + - If *expr_first* is a constant expression ([§12.23](expressions.md#1223-constant-expressions)) with value `null`, then the state of *v* after *expr* is the same as the state of *v* after *expr_second*. - Otherwise, the state of *v* after *expr* is the same as the definite-assignment state of *v* after *expr_first*. #### 9.4.4.30 ?: expressions @@ -760,8 +846,8 @@ For an expression *expr* of the form: - The definite-assignment state of *v* before *expr_true* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after true expression”. - The definite-assignment state of *v* before *expr_false* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after false expression”. - The definite-assignment state of *v* after *expr* is determined by: - - If *expr_cond* is a constant expression ([§11.21](expressions.md#1121-constant-expressions)) with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*. - - Otherwise, if *expr_cond* is a constant expression ([§11.21](expressions.md#1121-constant-expressions)) with value `false` then the state of *v* after *expr* is the same as the state of *v* after *expr_false*. + - If *expr_cond* is a constant expression ([§12.23](expressions.md#1223-constant-expressions)) with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*. + - Otherwise, if *expr_cond* is a constant expression ([§12.23](expressions.md#1223-constant-expressions)) with value `false` then the state of *v* after *expr* is the same as the state of *v* after *expr_false*. - Otherwise, if the state of *v* after *expr_true* is definitely assigned and the state of *v* after *expr_false* is definitely assigned, then the state of *v* after *expr* is definitely assigned. - Otherwise, the state of *v* after *expr* is not definitely assigned. @@ -769,9 +855,9 @@ For an expression *expr* of the form: For a *lambda_expression* or *anonymous_method_expression* *expr* with a body (either *block* or *expression*) *body*: -- The definite-assignment state of a parameter is the same as for a parameter of a named method ([§9.2.6](variables.md#926-reference-parameters), [§9.2.7](variables.md#927-output-parameters)). -- The definite-assignment state of an outer variable *v* before *body* is the same as the state of *v* before *expr*. That is, definite-assignment state of outer variables is inherited from the context of the anonymous function. -- The definite-assignment state of an outer variable *v* after *expr* is the same as the state of *v* before *expr*. +- The definite assignment state of a parameter is the same as for a parameter of a named method ([§9.2.6](variables.md#926-reference-parameters), [§9.2.7](variables.md#927-output-parameters), [§9.2.8](variables.md#928-input-parameters)). +- The definite assignment state of an outer variable *v* before *body* is the same as the state of *v* before *expr*. That is, definite assignment state of outer variables is inherited from the context of the anonymous function. +- The definite assignment state of an outer variable *v* after *expr* is the same as the state of *v* before *expr*. > *Example*: The example > @@ -837,7 +923,7 @@ Definite assignment for the body of each local function is defined separately fo Delegate conversions have a control flow path to the local function body. Captured variables are definitely assigned for the body if they are definitely assigned before the conversion. Variables assigned by the local function are not considered assigned after the conversion. -> *Note:* the above implies that bodies are re-analyzed for definite assignment at every local function invocation or delegate conversion. Compilers are not required to re-analyze the body of a local function at each invocation or delegate conversion. The implementation must produce results equivalent to that description. +> *Note*: the above implies that bodies are re-analyzed for definite assignment at every local function invocation or delegate conversion. Compilers are not required to re-analyze the body of a local function at each invocation or delegate conversion. The implementation must produce results equivalent to that description. *end note* @@ -862,7 +948,7 @@ Delegate conversions have a control flow path to the local function body. Captur > // OK. s is now definitely assigned. > F1(); > -> // OK, F3 reads s2, which is definitely assigned in F2. +> // OK, F3 reads s2, which is definitely assigned in F2. > F3(); > > void F1() @@ -887,6 +973,16 @@ Delegate conversions have a control flow path to the local function body. Captur > > *end example* +#### 9.4.4.34 is-pattern expressions + +For an expression *expr* of the form: + +*expr_operand* is *pattern* + +- The definite-assignment state of *v* before *expr_operand* is the same as the definite-assignment state of *v* before *expr*. +- If the variable ‘v’ is declared in *pattern*, then the definite-assignment state of ‘v’ after *expr* is “definitely assigned when true”. +- Otherwise the definite assignment state of ‘v’ after *expr* is the same as the definite assignment state of ‘v’ after *expr_operand*. + ## 9.5 Variable references A *variable_reference* is an *expression* that is classified as a variable. A *variable_reference* denotes a storage location that can be accessed both to fetch the current value and to store a new value. @@ -902,3 +998,239 @@ variable_reference ## 9.6 Atomicity of variable references Reads and writes of the following data types shall be atomic: `bool`, `char`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `int`, `float`, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including `long`, `ulong`, `double`, and `decimal`, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement. + +## 9.7 Reference variables and returns + +### 9.7.1 General + +A ***reference variable*** is a variable that refers to another variable, called the referent ([§9.2.6](variables.md#926-reference-parameters)). A reference variable is a local variable declared with the `ref` modifier. + +A reference variable stores a *variable_reference* ([§9.5](variables.md#95-variable-references)) to its referent and not the value of its referent. When a reference variable is used where a value is required its referent’s value is returned; similarly when a reference variable is the target of an assignment it is the referent which is assigned to. The variable to which a reference variable refers, i.e. the stored *variable_reference* for its referent, can be changed using a ref assignment (`= ref`). + +> *Example:* The following example demonstrates a local reference variable whose referent is an element of an array: +> +> +> ```csharp +> public class C +> { +> public void M() +> { +> int[] arr = new int[10]; +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> element += 5; // arr[5] has been incremented by 5 +> } +> } +> ``` +> +> *end example* + +A ***reference return*** is the *variable_reference* returned from a returns-by-ref method ([§15.6.1](classes.md#1561-general)). This *variable_reference* is the referent of the reference return. + +> *Example:* The following example demonstrates a reference return whose referent is an element of an array field: +> +> +> ```csharp +> public class C +> { +> private int[] arr = new int[10]; +> +> public ref readonly int M() +> { +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> return ref element; // return reference to arr[5]; +> } +> } +> ``` +> +> *end example* + +### 9.7.2 Ref safe contexts + +#### 9.7.2.1 General + +All reference variables obey safety rules that ensure the ref-safe-context of the reference variable is not greater than the ref-safe-context of its referent. + +> *Note*: The related notion of a *safe-context* is defined in ([§16.4.12](structs.md#16412-safe-context-constraint)), along with associated constraints. *end note* + +For any variable, the ***ref-safe-context*** of that variable is the context where a *variable_reference* ([§9.5](variables.md#95-variable-references)) to that variable is valid. The referent of a reference variable must have a ref-safe-context that is at least as wide as the ref-safe-context of the reference variable itself. + +> *Note*: The compiler determines the ref-safe-context through a static analysis of the program text. The ref-safe-context reflects the lifetime of a variable at runtime. *end note* + +There are three ref-safe-contexts: + +- ***declaration-block***: The ref-safe-context of a *variable_reference* to a local variable ([§9.2.9](variables.md#929-local-variables)) is that local variable’s scope ([§13.6.2](statements.md#1362-local-variable-declarations)), including any nested *embedded-statement*s in that scope. + + A *variable_reference* to a local variable is a valid referent for a reference variable only if the reference variable is declared within the ref-safe-context of that variable. + +- ***function-member***: Within a function a *variable_reference* to any of the following has a ref-safe-context of function-member: + + - Value parameters ([§9.2.5](variables.md#925-value-parameters)) on a function member declaration, including the implicit `this` of class member functions; and + - The implicit reference (`ref`) parameter ([§9.2.6](variables.md#926-reference-parameters)) `this` of a struct member function, along with its fields. + + A *variable_reference* with ref-safe-context of function-member is a valid referent only if the reference variable is declared in the same function member. + +- ***caller-context***: Within a function a *variable_reference* to any of the following has a ref-safe-context of caller-context: + - Reference (`ref`) parameters ([§9.2.6](variables.md#926-reference-parameters)) other than the implicit `this` of a struct member function; + - Member fields and elements of such parameters; + - Member fields of parameters of class type; and + - Elements of parameters of array type. + +A *variable_reference* with ref-safe-context of caller-context can be the referent of a reference return. + +These values form a nesting relationship from narrowest (declaration-block) to widest (caller-context). Each nested block represents a different context. + +> *Example*: The following code shows examples of the different ref-safe-contexts. The declarations show the ref-safe-context for a referent to be the initializing expression for a `ref` variable. The examples show the ref-safe-context for a reference return: +> +> +> ```csharp +> public class C +> { +> // ref safe context of arr is "caller-context". +> // ref safe context of arr[i] is "caller-context". +> private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +> +> // ref safe context is "caller-context" +> public ref int M1(ref int r1) +> { +> return ref r1; // r1 is safe to ref return +> } +> +> // ref safe context is "function-member" +> public ref int M2(int v1) +> { +> return ref v1; // error: v1 isn't safe to ref return +> } +> +> public ref int M3() +> { +> int v2 = 5; +> +> return ref arr[v2]; // arr[v2] is safe to ref return +> } +> +> public void M4(int p) +> { +> int v3 = 6; +> +> // context of r2 is declaration-block, +> // ref safe context of p is function-member +> ref int r2 = ref p; +> +> // context of r3 is declaration-block, +> // ref safe context of v3 is declaration-block +> ref int r3 = ref v3; +> +> // context of r4 is declaration-block, +> // ref safe context of arr[v3] is caller-context +> ref int r4 = ref arr[v3]; +> } +> } +> ``` +> +> *end example.* + + + +> *Example*: For `struct` types, the implicit `this` parameter is passed as a `ref` parameter. The ref-safe-context of the fields of a `struct` type as function-member prevents returning those fields by reference return. This rule prevents the following code: +> +> +> ```csharp +> public struct S +> { +> private int n; +> +> // Disallowed: returning ref of a field. +> public ref int GetN() => ref n; +> } +> +> class Test +> { +> public ref int M() +> { +> S s = new S(); +> ref int numRef = ref s.GetN(); +> return ref numRef; // reference to local variable 'numRef' returned +> } +> } +> ``` +> +> *end example.* + +#### 9.7.2.2 Local variable ref safe context + +For a local variable `v`: + +- If `v` is a reference variable, its ref-safe-context is the same as the ref-safe-context of its initializing expression. +- Otherwise its ref-safe-context is declaration-block. + +#### 9.7.2.3 Parameter ref safe context + +For a formal parameter `p`: + +- If `p` is a `ref`, or `in` parameter, its ref-safe-context is the caller-context. If `p` is an `in` parameter, it can’t be returned as a writable `ref` but can be returned as `ref readonly`. +- If `p` is an `out` parameter, its ref-safe-context is the caller-context. +- Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-context is the function-member. +- Otherwise, the parameter is a value parameter, and its ref-safe-context is the function-member. + +#### 9.7.2.4 Field ref safe context + +For a variable designating a reference to a field, `e.F`: + +- If `e` is of a reference type, its ref-safe-context is the caller-context. +- Otherwise, if `e` is of a value type, its ref-safe-context is the same as the ref-safe-context of `e`. + +#### 9.7.2.5 Operators + +The conditional operator ([§12.18](expressions.md#1218-conditional-operator)), `c ? ref e1 : ref e2`, and reference assignment operator, `= ref e` ([§12.21.1](expressions.md#12211-general)) have reference variables as operands and yield a reference variable. For those operators, the ref-safe-context of the result is the narrowest context among the ref-safe-contexts of all `ref` operands. + +#### 9.7.2.6 Function invocation + +For a variable `c` resulting from a ref-returning function invocation, its ref-safe-context is the narrowest of the following contexts: + +- The caller-context. +- The ref-safe-context of all `ref`, `out`, and `in` argument expressions (excluding the receiver). +- For each `in` parameter, if there is a corresponding expression that is a variable and there exists an identity conversion between the type of the variable and the type of the parameter, the variable’s ref-safe-context, otherwise the nearest enclosing context. +- The safe-context ([§16.4.12](structs.md#16412-safe-context-constraint)) of all argument expressions (including the receiver). + +> *Example*: the last bullet is necessary to handle code such as +> +> +> ```csharp +> ref int M2() +> { +> int v = 5; +> // Not valid. +> // ref safe context of "v" is block. +> // Therefore, ref safe context of the return value of M() is block. +> return ref M(ref v); +> } +> +> ref int M(ref int p) +> { +> return ref p; +> } +> ``` +> +> *end example* + +A property invocation and an indexer invocation (either `get` or `set`) is treated as a function invocation of the underlying accessor by the above rules. A local function invocation is a function invocation. + +#### 9.7.2.7 Values + +A value’s ref-safe-context is the nearest enclosing context. + +> *Note*: This occurs in an invocation such as `M(ref d.Length)` where `d` is of type `dynamic`. It is also consistent with arguments corresponding to `in` parameters. *end note* + +#### 9.7.2.8 Constructor invocations + +A `new` expression that invokes a constructor obeys the same rules as a method invocation ([§9.7.2.6](variables.md#9726-function-invocation)) that is considered to return the type being constructed. + +#### 9.7.2.9 Limitations on reference variables + +- Neither a reference parameter, nor an output parameter, nor an input parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type shall be captured by lambda expression or local function. +- Neither a reference parameter, nor an output parameter, nor an input parameter, nor a parameter of a `ref struct` type shall be an argument for an iterator method or an `async` method. +- Neither a `ref` local, nor a local of a `ref struct` type shall be in context at the point of a `yield return` statement or an `await` expression. +- For a ref reassignment `e1 = ref e2`, the ref-safe-context of `e2` must be at least as wide a context as the *ref-safe-context* of `e1`. +- For a ref return statement `return ref e1`, the ref-safe-context of `e1` must be the caller-context. diff --git a/tools/ExampleExtractor/ExampleMetadata.cs b/tools/ExampleExtractor/ExampleMetadata.cs index c9bfda877..a5b79fc56 100644 --- a/tools/ExampleExtractor/ExampleMetadata.cs +++ b/tools/ExampleExtractor/ExampleMetadata.cs @@ -19,6 +19,14 @@ public class ExampleMetadata [JsonProperty("replaceEllipsis")] public bool ReplaceEllipsis { get; set; } + /// + /// When null, the sample directory is expected to contain a single .csproj file. + /// Specify this as the project file (without extension) to disambiguate for + /// samples with multiple project files. + /// + [JsonProperty("project")] + public string Project { get; set; } + /// /// When set, and when is true, this list is used /// to provide the replacements, allowing for return statements etc. @@ -57,6 +65,9 @@ public class ExampleMetadata [JsonProperty("expectedException")] public string ExpectedException { get; set; } + [JsonProperty("executionArgs")] + public string[] ExecutionArgs { get; set; } + /// /// Additional files to copy from the special "additional-files" template directory. /// diff --git a/tools/ExampleExtractor/Template.cs b/tools/ExampleExtractor/Template.cs index 10cb3c0b0..12a008647 100644 --- a/tools/ExampleExtractor/Template.cs +++ b/tools/ExampleExtractor/Template.cs @@ -7,6 +7,7 @@ internal class Template private const string AdditionalFilesDirectory = "additional-files"; private const string ExampleCodeSubstitution = "$example-code"; private const string ExampleNameSubstitution = "$example-name"; + private const string FileCommentPrefix = "// File "; internal string Name { get; } private readonly Dictionary files; @@ -27,13 +28,16 @@ internal void Apply(Example example, string rootOutputDirectory, string rootTemp { var outputDirectory = Path.Combine(rootOutputDirectory, example.Name); Directory.CreateDirectory(outputDirectory); + + var code = ExtractExtraFiles(example.Code, outputDirectory); + foreach (var pair in files) { string file = Path.Combine(outputDirectory, pair.Key); - string code = pair.Value - .Replace(ExampleCodeSubstitution, example.Code) + string content = pair.Value + .Replace(ExampleCodeSubstitution, code) .Replace(ExampleNameSubstitution, example.Name); - File.WriteAllText(file, code); + File.WriteAllText(file, content); } if (example.Metadata.AdditionalFiles is List additionalFiles) { @@ -48,6 +52,43 @@ internal void Apply(Example example, string rootOutputDirectory, string rootTemp File.WriteAllText(Path.Combine(outputDirectory, ExampleMetadata.MetadataFile), metadataJson); } + /// + /// Returns all the code before the first "// File:" comment, extracting any additional + /// files into the given directory. + /// + private string ExtractExtraFiles(string code, string outputDirectory) + { + var lines = code.Split('\n').ToList(); + // The implementation is a lot simpler if we know we've got an end marker. + lines.Add(FileCommentPrefix + "IgnoreMe"); + string? currentFile = null; + List currentLines = new List(); + string? initialCode = null; + + foreach (var line in lines) + { + if (line.StartsWith(FileCommentPrefix)) + { + if (currentFile is null) + { + // Remember this for later. + initialCode = string.Join("\n", currentLines); + } + else + { + File.WriteAllLines(currentFile, currentLines); + } + currentLines.Clear(); + currentFile = Path.Combine(outputDirectory, line[FileCommentPrefix.Length..].TrimEnd(':')); + } + else + { + currentLines.Add(line); + } + } + return initialCode ?? throw new InvalidOperationException($"Never saw a file terminator - bug in {nameof(ExtractExtraFiles)}"); + } + internal static Dictionary LoadTemplates(string directory) => Directory.GetDirectories(directory) .Select(LoadTemplate) diff --git a/tools/ExampleTester/GeneratedExample.cs b/tools/ExampleTester/GeneratedExample.cs index 95e2866e4..3f73e29f6 100644 --- a/tools/ExampleTester/GeneratedExample.cs +++ b/tools/ExampleTester/GeneratedExample.cs @@ -35,12 +35,15 @@ private static GeneratedExample Load(string directory) internal async Task Test(TesterConfiguration configuration) { - var outputLines = new List(); - outputLines.Add($"Testing {Metadata.Name} from {Metadata.Source}"); + var outputLines = new List { $"Testing {Metadata.Name} from {Metadata.Source}" }; - using var workspace = MSBuildWorkspace.Create(); + // Explicitly do a release build, to avoid implicitly defining DEBUG. + var properties = new Dictionary { { "Configuration", "Release" } }; + using var workspace = MSBuildWorkspace.Create(properties); // TODO: Validate this more cleanly. - var projectFile = Directory.GetFiles(directory, "*.csproj").Single(); + var projectFile = Metadata.Project is string specifiedProject + ? Path.Combine(directory, $"{specifiedProject}.csproj") + : Directory.GetFiles(directory, "*.csproj").Single(); var project = await workspace.OpenProjectAsync(projectFile); var compilation = await project.GetCompilationAsync(); if (compilation is null) @@ -128,8 +131,9 @@ bool ValidateOutput() outputLines.Add($" Failed to find entry point method {typeName}.{methodName}"); return false; } - // TODO: Handle async entry points. (Is the entry point the synthesized one, or the user code?) - var arguments = method.GetParameters().Any() ? new object[] { new string[0] } : new object[0]; + var arguments = method.GetParameters().Any() + ? new object[] { Metadata.ExecutionArgs ?? new string[0] } + : new object[0]; var oldOut = Console.Out; List actualLines; @@ -140,7 +144,14 @@ bool ValidateOutput() Console.SetOut(new StringWriter(builder)); try { - method.Invoke(null, arguments); + var result = method.Invoke(null, arguments); + // For async Main methods, the compilation's entry point is still the Main + // method, so we explicitly wait for the returned task just like the synthesized + // entry point would. + if (result is Task task) + { + task.GetAwaiter().GetResult(); + } } catch (TargetInvocationException outer) { diff --git a/tools/GetGrammar/Program.cs b/tools/GetGrammar/Program.cs index d99ecd534..6d1c54478 100644 --- a/tools/GetGrammar/Program.cs +++ b/tools/GetGrammar/Program.cs @@ -24,6 +24,7 @@ using System; using System.IO; +using System.Text; using System.Threading.Tasks; namespace ExtractGrammar @@ -40,6 +41,8 @@ public static async Task Main(string[] args) using var inputFile = new StreamReader(args[0]); string section = ""; + Console.OutputEncoding = Encoding.UTF8; + while (await inputFile.ReadLineAsync() is string inputLine) { if (inputLine.StartsWith("#")) diff --git a/tools/MarkdownConverter/Converter/MarkdownSourceConverter.cs b/tools/MarkdownConverter/Converter/MarkdownSourceConverter.cs index 7ddc86675..43dcce77e 100644 --- a/tools/MarkdownConverter/Converter/MarkdownSourceConverter.cs +++ b/tools/MarkdownConverter/Converter/MarkdownSourceConverter.cs @@ -20,8 +20,9 @@ public class MarkdownSourceConverter { /// /// The maximum code line length that's allowed without generating a warning. + /// (80 would be normal, but 81 appears to be okay, and avoids a couple of difficult line breaks.) /// - public const int MaximumCodeLineLength = 80; + public const int MaximumCodeLineLength = 81; private const int InitialIndentation = 540; private const int ListLevelIndentation = 360; diff --git a/tools/MarkdownConverter/template.docx b/tools/MarkdownConverter/template.docx index 159b11783..54e89a8a7 100644 Binary files a/tools/MarkdownConverter/template.docx and b/tools/MarkdownConverter/template.docx differ diff --git a/tools/example-templates/additional-files/Customer.cs b/tools/example-templates/additional-files/Customer.cs new file mode 100644 index 000000000..920b38d87 --- /dev/null +++ b/tools/example-templates/additional-files/Customer.cs @@ -0,0 +1,18 @@ +partial class Customer +{ + string name; + + public string Name + { + get => name; + set + { + OnNameChanging(value); + name = value; + OnNameChanged(); + } + } + + partial void OnNameChanging(string newName); + partial void OnNameChanged(); +} \ No newline at end of file diff --git a/tools/example-templates/additional-files/Extensions.cs b/tools/example-templates/additional-files/Extensions.cs new file mode 100644 index 000000000..6535f1e3b --- /dev/null +++ b/tools/example-templates/additional-files/Extensions.cs @@ -0,0 +1,15 @@ +public static class Extensions +{ + public static int ToInt32(this string s) => Int32.Parse(s); + + public static T[] Slice(this T[] source, int index, int count) + { + if (index < 0 || count < 0 || source.Length - index < count) + { + throw new ArgumentException(); + } + T[] result = new T[count]; + Array.Copy(source, index, result, 0, count); + return result; + } +} \ No newline at end of file diff --git a/tools/example-templates/additional-files/MyTaskMethodBuilderT.cs b/tools/example-templates/additional-files/MyTaskMethodBuilderT.cs new file mode 100644 index 000000000..1dedb8364 --- /dev/null +++ b/tools/example-templates/additional-files/MyTaskMethodBuilderT.cs @@ -0,0 +1,42 @@ +using System.Runtime.CompilerServices; + +class MyTaskMethodBuilder +{ + public static MyTaskMethodBuilder Create() + { + return new MyTaskMethodBuilder(); + } + + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine + { + } + + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + } + + public void SetException(Exception exception) + { + } + + public void SetResult(T result) + { + } + + public void AwaitOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + } + + public void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + } + + public MyTask Task { get; } +} diff --git a/tools/example-templates/extern-lib/Example.cs b/tools/example-templates/extern-lib/Example.cs new file mode 100644 index 000000000..bb8d3f010 --- /dev/null +++ b/tools/example-templates/extern-lib/Example.cs @@ -0,0 +1 @@ +$example-code diff --git a/tools/example-templates/extern-lib/ExampleProject.csproj b/tools/example-templates/extern-lib/ExampleProject.csproj new file mode 100644 index 000000000..0dc90edad --- /dev/null +++ b/tools/example-templates/extern-lib/ExampleProject.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + false + CS0169 + + + + + + + + + + + diff --git a/tools/example-templates/extern-lib/ExternN2.cs b/tools/example-templates/extern-lib/ExternN2.cs new file mode 100644 index 000000000..9f4b93d84 --- /dev/null +++ b/tools/example-templates/extern-lib/ExternN2.cs @@ -0,0 +1 @@ +public class A {} diff --git a/tools/example-templates/extern-lib/ExternN2.csproj b/tools/example-templates/extern-lib/ExternN2.csproj new file mode 100644 index 000000000..61731db09 --- /dev/null +++ b/tools/example-templates/extern-lib/ExternN2.csproj @@ -0,0 +1,12 @@ + + + + net6.0 + false + + + + + + + diff --git a/tools/example-templates/extern-lib/ExternR1.cs b/tools/example-templates/extern-lib/ExternR1.cs new file mode 100644 index 000000000..26b4c8e5c --- /dev/null +++ b/tools/example-templates/extern-lib/ExternR1.cs @@ -0,0 +1,6 @@ +public class A {} + +namespace N1.N2 +{ + public interface I {} +} \ No newline at end of file diff --git a/tools/example-templates/extern-lib/ExternR1.csproj b/tools/example-templates/extern-lib/ExternR1.csproj new file mode 100644 index 000000000..3a19d71a2 --- /dev/null +++ b/tools/example-templates/extern-lib/ExternR1.csproj @@ -0,0 +1,12 @@ + + + + net6.0 + false + + + + + + + diff --git a/tools/example-templates/extern-lib/ExternX.cs b/tools/example-templates/extern-lib/ExternX.cs new file mode 100644 index 000000000..4661cd3b8 --- /dev/null +++ b/tools/example-templates/extern-lib/ExternX.cs @@ -0,0 +1,5 @@ +namespace N +{ + public class A {} + public class B {} +} \ No newline at end of file diff --git a/tools/example-templates/extern-lib/ExternX.csproj b/tools/example-templates/extern-lib/ExternX.csproj new file mode 100644 index 000000000..04b4e61e6 --- /dev/null +++ b/tools/example-templates/extern-lib/ExternX.csproj @@ -0,0 +1,12 @@ + + + + net6.0 + false + + + + + + + diff --git a/tools/example-templates/extern-lib/ExternY.cs b/tools/example-templates/extern-lib/ExternY.cs new file mode 100644 index 000000000..53fe292dc --- /dev/null +++ b/tools/example-templates/extern-lib/ExternY.cs @@ -0,0 +1,5 @@ +namespace N +{ + public class B {} + public class C {} +} \ No newline at end of file diff --git a/tools/example-templates/extern-lib/ExternY.csproj b/tools/example-templates/extern-lib/ExternY.csproj new file mode 100644 index 000000000..de0c8d76e --- /dev/null +++ b/tools/example-templates/extern-lib/ExternY.csproj @@ -0,0 +1,12 @@ + + + + net6.0 + false + + + + + + + diff --git a/tools/run-smarten.sh b/tools/run-smarten.sh index 5db56fd32..4aa33e5da 100644 --- a/tools/run-smarten.sh +++ b/tools/run-smarten.sh @@ -1,40 +1,8 @@ #!/bin/bash set -eu -o pipefail -declare -r SPEC_DIRECTORY=../standard -declare -r EXTENSION=md -declare -a SPEC_FILES=( - "foreword.md" - "introduction.md" - "scope.md" - "normative-references.md" - "terms-and-definitions.md" - "general-description.md" - "conformance.md" - "lexical-structure.md" - "basic-concepts.md" - "types.md" - "variables.md" - "conversions.md" - "expressions.md" - "statements.md" - "namespaces.md" - "classes.md" - "structs.md" - "arrays.md" - "interfaces.md" - "enums.md" - "delegates.md" - "exceptions.md" - "attributes.md" - "unsafe-code.md" - "grammar.md" - "portability-issues.md" - "standard-library.md" - "documentation-comments.md" - "bibliography.md" - ) +declare -a SPEC_FILES=($(ls ../standard/*.md)) # unpack the package to ./smarten tar -xvf ../.github/workflows/dependencies/EcmaTC49.Smarten.tar @@ -42,7 +10,7 @@ tar -xvf ../.github/workflows/dependencies/EcmaTC49.Smarten.tar for file in "${SPEC_FILES[@]}" do echo "$file" - ./smarten/Smarten_CL.exe $SPEC_DIRECTORY/$file $SPEC_DIRECTORY/$file + ./smarten/Smarten_CL.exe $file $file done # finally, remove the unpacked smarten executable: diff --git a/tools/update-grammar-annex.sh b/tools/update-grammar-annex.sh index 3e883ff56..17ddd41dd 100755 --- a/tools/update-grammar-annex.sh +++ b/tools/update-grammar-annex.sh @@ -12,6 +12,7 @@ declare -a SPEC_FILES=( "types.md" "variables.md" "conversions.md" + "patterns.md" "expressions.md" "statements.md" "namespaces.md" diff --git a/tools/validate-grammar.sh b/tools/validate-grammar.sh index 267cbc222..b5bc17f29 100755 --- a/tools/validate-grammar.sh +++ b/tools/validate-grammar.sh @@ -9,6 +9,7 @@ declare -a SPEC_FILES=( "types.md" "variables.md" "conversions.md" + "patterns.md" "expressions.md" "statements.md" "namespaces.md"