diff --git a/.markdownlint.json b/.markdownlint.json index 39b4280c6..6981b8233 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -11,7 +11,5 @@ "code_blocks": false, "names": [ ] - }, - - "MD031" : false + } } diff --git a/standard/arrays.md b/standard/arrays.md index 100483554..4de5fa489 100644 --- a/standard/arrays.md +++ b/standard/arrays.md @@ -9,6 +9,7 @@ An array has a rank that determines the number of indices associated with each a 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”. > *Example*: +> > ```csharp > int[][] pascals = > { @@ -18,6 +19,7 @@ The element type of an array can itself be an array type ([§16.2.1](arrays.md#1 > new int[] {1, 3, 3, 1} > }; > ``` +> > *end example* 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. @@ -60,6 +62,7 @@ A single-dimensional array `T[]` implements the interface System.Collections.Gen Similarly, a single-dimensional array `T[]` also implements the interface `System.Collections.Generic.IReadOnlyList` (`IReadOnlyList` for short) and its base interfaces. Accordingly, there is an implicit conversion from `T[]` to `IReadOnlyList` and its base interfaces. In addition, if there is an implicit reference conversion from `S` to `T` then `S[]` implements `IReadOnlyList` and there is an implicit reference conversion from `S[]` to `IReadOnlyList` 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 `IReadOnlyList` and its base interfaces ([§10.3.5](conversions.md#1035-explicit-reference-conversions)). > *Example*: For example: +> > ```csharp > using System.Collections.Generic; > @@ -88,6 +91,7 @@ Similarly, a single-dimensional array `T[]` also implements the interface `Syste > } > } > ``` +> > The assignment `lst2 = oa1` generates a compile-time error since the conversion from `object[]` to `IList` is an explicit conversion, not implicit. The cast `(IList)oa1` will cause an exception to be thrown at run-time since `oa1` references an `object[]` and not a `string[]`. However the cast (`IList)oa2` will not cause an exception to be thrown since `oa2` references a `string[]`. *end example* Whenever there is an implicit or explicit reference conversion from `S[]` to `IList`, there is also an explicit reference conversion from `IList` and its base interfaces to `S[]` ([§10.3.5](conversions.md#1035-explicit-reference-conversions)). @@ -121,6 +125,7 @@ For any two *reference_type*s `A` and `B`, if an implicit reference conversion 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.18.2](expressions.md#11182-simple-assignment)). > *Example*: +> > ```csharp > class Test > { @@ -141,6 +146,7 @@ Because of array covariance, assignments to elements of reference type arrays in > } > } > ``` +> > The assignment to `array[i]` in the `Fill` method implicitly includes a run-time check, which ensures that `value` is either a `null` reference or a reference to an object of a type that is compatible with the actual element type of `array`. In `Main`, the first two invocations of `Fill` succeed, but the third invocation causes a `System.ArrayTypeMismatchException` to be thrown upon executing the first assignment to `array[i]`. The exception occurs because a boxed `int` cannot be stored in a `string` array. *end example* 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[]`. @@ -164,6 +170,7 @@ variable_initializer | array_initializer ; ``` + An array initializer consists of a sequence of variable initializers, enclosed by “`{`” and “`}`” tokens and separated by “`,`” tokens. Each variable initializer is an expression or, in the case of a multi-dimensional array, a nested array initializer. The context in which an array initializer is used determines the type of the array being initialized. In an array creation expression, the array type immediately precedes the initializer, or is inferred from the expressions in the array initializer. In a field or variable declaration, the array type is the type of the field or variable being declared. When an array initializer is used in a field or variable declaration, @@ -171,30 +178,39 @@ The context in which an array initializer is used determines the type of the arr ```csharp int[] a = {0, 2, 4, 6, 8}; ``` + it is simply shorthand for an equivalent array creation expression: ```csharp int[] a = new int[] {0, 2, 4, 6, 8}; ``` + For a single-dimensional array, the array initializer shall consist of a sequence of expressions, each having an implicit conversion to the element type of the array ([§10.2](conversions.md#102-implicit-conversions)). The expressions initialize array elements in increasing order, starting with the element at index zero. The number of expressions in the array initializer determines the length of the array instance being created. > *Example*: The array initializer above creates an `int[]` instance of length 5 and then initializes the instance with the following values: +> > ```csharp > a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8; > ``` +> > *end example* For a multi-dimensional array, the array initializer shall have as many levels of nesting as there are dimensions in the array. The outermost nesting level corresponds to the leftmost dimension and the innermost nesting level corresponds to the rightmost dimension. The length of each dimension of the array is determined by the number of elements at the corresponding nesting level in the array initializer. For each nested array initializer, the number of elements shall be the same as the other array initializers at the same level. > *Example*: The example: +> > ```csharp > int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; > ``` +> > creates a two-dimensional array with a length of five for the leftmost dimension and a length of two for the rightmost dimension: +> > ```csharp > int[,] b = new int[5, 2]; > ``` +> > and then initializes the array instance with the following values: +> > ```csharp > b[0, 0] = 0; b[0, 1] = 1; > b[1, 0] = 2; b[1, 1] = 3; @@ -202,29 +218,36 @@ For a multi-dimensional array, the array initializer shall have as many levels o > b[3, 0] = 6; b[3, 1] = 7; > b[4, 0] = 8; b[4, 1] = 9; > ``` +> > *end example* If a dimension other than the rightmost is given with length zero, the subsequent dimensions are assumed to also have length zero. > *Example*: +> > ```csharp > int[,] c = {}; > ``` +> > creates a two-dimensional array with a length of zero for both the leftmost and the rightmost dimension: +> > ```csharp > int[,] c = new int[0, 0]; > ``` +> > *end example* When an array creation expression includes both explicit dimension lengths and an array initializer, the lengths shall be constant expressions and the number of elements at each nesting level shall match the corresponding dimension length. > *Example*: Here are some examples: +> > ```csharp > int i = 3; > int[] x = new int[3] {0, 1, 2}; // OK > int[] y = new int[i] {0, 1, 2}; // Error, i not a constant > int[] z = new int[3] {0, 1, 2, 3}; // Error, length/initializer mismatch > ``` +> > Here, the initializer for `y` results in a compile-time error because the dimension length expression is not a constant, and the initializer for `z` results in a compile-time error because the length and the number of elements in the initializer do not agree. *end example* diff --git a/standard/attributes.md b/standard/attributes.md index 26126a0ff..e3680542a 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -19,12 +19,14 @@ A class that derives from the abstract class `System.Attribute`, whether directl A generic class declaration shall not use `System.Attribute` as a direct or indirect base class. > *Example*: +> > ```csharp > using System; > > public class B : Attribute {} > public class C : B {} // Error – generic cannot be an attribute > ``` +> > *end example* ### 21.2.2 Attribute usage @@ -34,6 +36,7 @@ The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage `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. > *Example*: The example +> > ```csharp > using System; > @@ -43,21 +46,27 @@ The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage > ... > } > ``` +> > defines an attribute class named `SimpleAttribute` that can be placed on *class_declaration*s and *interface_declaration*s only. The example +> > ```csharp > [Simple] class Class1 {...} > [Simple] interface Interface1 {...} > ``` +> > shows several uses of the `Simple` attribute. Although this attribute is defined with the name `SimpleAttribute`, when this attribute is used, the `Attribute` suffix may be omitted, resulting in the short name `Simple`. Thus, the example above is semantically equivalent to the following +> > ```csharp > [SimpleAttribute] class Class1 {...} > [SimpleAttribute] interface Interface1 {...} > ``` +> > *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. > *Example*: The example +> > ```csharp > using System; > [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] @@ -74,6 +83,7 @@ The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage > } > } > ``` +> > defines a multi-use attribute class named `AuthorAttribute`. The example > > ```csharp @@ -83,6 +93,7 @@ The attribute `AttributeUsage` ([§21.5.2](attributes.md#2152-the-attributeusage > ... > } > ``` +> > shows a class declaration with two uses of the `Author` attribute. *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. @@ -94,6 +105,7 @@ using System; > class X : Attribute { ... } ``` + is equivalent to the following: ```csharp @@ -111,6 +123,7 @@ class X : Attribute { ... } 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. > *Example*: The example +> > ```csharp > using System; > [AttributeUsage(AttributeTargets.Class)] @@ -131,9 +144,11 @@ Attribute classes can have ***positional parameters*** and ***named parameters** > public string Url { get; } > } > ``` +> > defines an attribute class named `HelpAttribute` that has one positional parameter, `url`, and one named parameter, `Topic`. Although it is non-static and public, the property `Url` does not define a named parameter, since it is not read-write. > > This attribute class might be used as follows: +> > ```csharp > [Help("http://www.mycompany.com/.../Class1.htm")] > class Class1 @@ -145,6 +160,7 @@ Attribute classes can have ***positional parameters*** and ***named parameters** > { > } > ``` +> > *end example* ### 21.2.4 Attribute parameter types @@ -293,6 +309,7 @@ Certain contexts permit the specification of an attribute on more than one targe In all other contexts, inclusion of an *attribute_target_specifier* is permitted but unnecessary. > *Example*: a class declaration may either include or omit the specifier `type`: +> > ```csharp > [type: Author("Brian Kernighan")] > class Class1 {} @@ -300,6 +317,7 @@ In all other contexts, inclusion of an *attribute_target_specifier* is permitted > [Author("Dennis Ritchie")] > class Class2 {} > ``` +> > *end example*.] An implementation can accept other *attribute_target*s, the purposes of which are implementation defined. An implementation that does not recognize such an *attribute_target* shall issue a warning and ignore the containing *attribute_section*. @@ -313,7 +331,8 @@ By convention, attribute classes are named with a suffix of `Attribute`. An *att If exactly one of the two steps above results in a type derived from `System.Attribute`, then that type is the result of the *attribute_name*. Otherwise a compile-time error occurs. -*Example*: If an attribute class is found both with and without this suffix, an ambiguity is present, and a compile-time error results. If the *attribute_name* is spelled such that its right-most *identifier* is a verbatim identifier ([§6.4.3](lexical-structure.md#643-identifiers)), then only an attribute without a suffix is matched, thus enabling such an ambiguity to be resolved. The example +> *Example*: If an attribute class is found both with and without this suffix, an ambiguity is present, and a compile-time error results. If the *attribute_name* is spelled such that its right-most *identifier* is a verbatim identifier ([§6.4.3](lexical-structure.md#643-identifiers)), then only an attribute without a suffix is matched, thus enabling such an ambiguity to be resolved. The example +> > ```csharp > using System; > [AttributeUsage(AttributeTargets.All)] @@ -336,7 +355,9 @@ If exactly one of the two steps above results in a type derived from `System.Att > [@ExampleAttribute] // Refers to ExampleAttribute > class Class4 {} > ``` +> > shows two attribute classes named `Example` and `ExampleAttribute`. The attribute `[Example]` is ambiguous, since it could refer to either `Example` or `ExampleAttribute`. Using a verbatim identifier allows the exact intent to be specified in such rare cases. The attribute `[ExampleAttribute]` is not ambiguous (although it would be if there was an attribute class named `ExampleAttributeAttribute`!). If the declaration for class `Example` is removed, then both attributes refer to the attribute class named `ExampleAttribute`, as follows: +> > ```csharp > using System; > @@ -353,11 +374,13 @@ If exactly one of the two steps above results in a type derived from `System.Att > [@Example] // Error: no attribute named “Example” > class Class3 {} > ``` +> > *end example* It is a compile-time error to use a single-use attribute class more than once on the same entity. > *Example*: The example +> > ```csharp > using System; > @@ -375,6 +398,7 @@ It is a compile-time error to use a single-use attribute class more than once on > [HelpString("Another description of Class1")] > public class Class1 {} > ``` +> > results in a compile-time error because it attempts to use `HelpString`, which is a single-use attribute class, more than once on the declaration of `Class1`. *end example* An expression `E` is an *attribute_argument_expression* if all of the following statements are true: @@ -386,6 +410,7 @@ An expression `E` is an *attribute_argument_expression* if all of the following - A single-dimensional array of *attribute_argument_expression*s. > *Example*: +> > ```csharp > using System; > [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field)] @@ -415,11 +440,13 @@ An expression `E` is an *attribute_argument_expression* if all of the following > int x4; > } > ``` +> > *end example* The attributes of a type declared in multiple parts are determined by combining, in an unspecified order, the attributes of each of its parts. If the same attribute is placed on multiple parts, it is equivalent to specifying that attribute multiple times on the type. > *Example*: The two parts: +> > ```csharp > [Attr1, Attr2("hello")] > partial class A {} @@ -427,11 +454,14 @@ The attributes of a type declared in multiple parts are determined by combining, > [Attr3, Attr2("goodbye")] > partial class A {} > ``` +> > are equivalent to the following single declaration: +> > ```csharp > [Attr1, Attr2("hello"), Attr3, Attr2("goodbye")] > class A {} > ``` +> > *end example* Attributes on type parameters combine in the same way. @@ -474,6 +504,7 @@ The attribute instance represented by `T`, `C`, `P`, and `N`, and associated wi > *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: +> > ```csharp > using System; > using System.Reflection; @@ -497,6 +528,7 @@ The attribute instance represented by `T`, `C`, `P`, and `N`, and associated wi > } > } > ``` +> > *end example* ## 21.5 Reserved attributes @@ -531,6 +563,7 @@ The attribute `Conditional` enables the definition of ***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. > *Example*: +> > ```csharp > using System.Diagnostics; > @@ -544,6 +577,7 @@ A method decorated with the `Conditional` attribute is a conditional method. Eac > } > } > ``` +> > declares `Eg.M` as a conditional method associated with the two conditional compilation symbols `ALPHA` and `BETA`. *end example* A call to a conditional method is included if one or more of its associated conditional compilation symbols is defined at the point of call, otherwise the call is omitted. @@ -559,6 +593,7 @@ A conditional method is subject to the following restrictions: In addition, a compile-time error occurs if a delegate is created from a conditional method. > *Example*: The example +> > ```csharp > #define DEBUG > using System; @@ -581,11 +616,13 @@ In addition, a compile-time error occurs if a delegate is created from a conditi > } > } > ``` +> > declares `Class1.M` as a conditional method. `Class2`’s `Test` method calls this method. Since the conditional compilation symbol `DEBUG` is defined, if `Class2.Test` is called, it will call `M`. If the symbol `DEBUG` had not been defined, then `Class2.Test` would not call `Class1.M`. *end example* It is important to understand that the inclusion or exclusion of a call to a conditional method is controlled by the conditional compilation symbols at the point of the call. > *Example*: In the following code +> > ```csharp > // File `class1.cs`: > using System.Diagnostics; @@ -619,11 +656,13 @@ It is important to understand that the inclusion or exclusion of a call to a con > } > } > ``` +> > the classes `Class2` and `Class3` each contain calls to the conditional method `Class1.F`, which is conditional based on whether or not `DEBUG` is defined. Since this symbol is defined in the context of `Class2` but not `Class3`, the call to `F` in `Class2` is included, while the call to `F` in `Class3` is omitted. *end example* The use of conditional methods in an inheritance chain can be confusing. Calls made to a conditional method through `base`, of the form `base.M`, are subject to the normal conditional method call rules. > *Example*: In the following code +> > ```csharp > // File `class1.cs` > using System; @@ -659,6 +698,7 @@ The use of conditional methods in an inheritance chain can be confusing. Calls m > } > } > ``` +> > `Class2` includes a call to the `M` defined in its base class. This call is omitted because the base method is conditional based on the presence of the symbol `DEBUG`, which is undefined. Thus, the method writes to the console “`Class2.M executed`” only. Judicious use of *pp_declaration*s can eliminate such problems. *end example* #### 21.5.3.3 Conditional attribute classes @@ -666,6 +706,7 @@ The use of conditional methods in an inheritance chain can be confusing. Calls m 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. > *Example*: +> > ```csharp > using System; > using System.Diagnostics; @@ -674,6 +715,7 @@ An attribute class ([§21.2](attributes.md#212-attribute-classes)) decorated wit > [Conditional("BETA")] > public class TestAttribute : Attribute {} > ``` +> > declares `TestAttribute` as a conditional attribute class associated with the conditional compilations symbols `ALPHA` and `BETA`. *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. @@ -681,6 +723,7 @@ Attribute specifications ([§21.3](attributes.md#213-attribute-specification)) o 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`: > using System; @@ -699,6 +742,7 @@ It is important to note that the inclusion or exclusion of an attribute specific > [Test] // TestAttribute is not specified > class Class2 {} > ``` +> > the classes `Class1` and `Class2` are each decorated with attribute `Test`, which is conditional based on whether or not `DEBUG` is defined. Since this symbol is defined in the context of `Class1` but not `Class2`, the specification of the Test attribute on `Class1` is included, while the specification of the `Test` attribute on `Class2` is omitted. *end example* ### 21.5.4 The Obsolete attribute @@ -708,6 +752,7 @@ The attribute `Obsolete` is used to mark types and members of types that should If a program uses a type or member that is decorated with the `Obsolete` attribute, the compiler shall issue a warning or an error. Specifically, the compiler shall issue a warning if no error parameter is provided, or if the error parameter is provided and has the value `false`. The compiler shall issue an error if the error parameter is specified and has the value `true`. > *Example*: In the following code +> > ```csharp > [Obsolete("This class is obsolete; use class B instead")] > class A @@ -729,6 +774,7 @@ If a program uses a type or member that is decorated with the `Obsolete` attribu > } > } > ``` +> > the class `A` is decorated with the `Obsolete` attribute. Each use of `A` in `Main` results in a warning that includes the specified message, “This class is obsolete; use class `B` instead”. *end example* ### 21.5.5 Caller-info attributes @@ -740,6 +786,7 @@ For purposes such as logging and reporting, it is sometimes useful for a functio When an optional parameter is annotated with one of the caller-info attributes, omitting the corresponding argument in a call does not necessarily cause the default parameter value to be substituted. Instead, if the specified information about the calling context is available, that information will be passed as the argument value. > *Example*: +> > ```csharp > using System.Runtime.CompilerServices > @@ -756,6 +803,7 @@ When an optional parameter is annotated with one of the caller-info attributes, > Console.WriteLine((name == null) ? "No member name" : name); > } > ``` +> > A call to `Log()` with no arguments would print the line number and file path of the call, as well as the name of the member within which the call occurred. *end example* Caller-info attributes can occur on optional parameters anywhere, including in delegate declarations. However, the specific caller-info attributes have restrictions on the types of the parameters they can attribute, so that there will always be an implicit conversion from a substituted value to the parameter type. @@ -813,6 +861,7 @@ For invocations that occur within declarations of instance constructors, static 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. > *Example*: By default, an indexer’s name is `Item`. This can be overridden, as follows: +> > ```csharp > [System.Runtime.CompilerServices.IndexerName("TheItem")] > public int this[int index] @@ -820,5 +869,6 @@ For interoperation with other languages, an indexer may be implemented using ind > // get and set accessors > } > ``` +> > Now, the indexer’s name is `TheItem`. > *end example* diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 743ae86ee..3e6093323 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -75,6 +75,7 @@ The textual order in which names are declared is generally of no significance. I - Declaration order for enum member declarations ([§18.4](enums.md#184-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 +> > ```csharp > namespace Megacorp.Data > { @@ -92,11 +93,13 @@ The textual order in which names are declared is generally of no significance. I > } > } > ``` +> > The two namespace declarations above contribute to the same declaration space, in this case declaring two classes with the fully qualified names `Megacorp.Data.Customer` and `Megacorp.Data.Order`. Because the two declarations contribute to the same declaration space, it would have caused a compile-time error if each contained a declaration of a class with the same name. *end example* > *Note*: As specified above, the declaration space of a block includes any nested blocks. Thus, in the following example, the `F` and `G` methods result in a compile-time error because the name `i` is declared in the outer block and cannot be redeclared in the inner block. However, the `H` and `I` methods are valid since the two `i`’s are declared in separate non-nested blocks. +> > ```csharp > class A > { @@ -143,6 +146,7 @@ The textual order in which names are declared is generally of no significance. I > } > } > ``` +> > *end note* ## 7.4 Members @@ -268,6 +272,7 @@ The accessibility domain of a nested member `M` declared in a type `T` within > *Example*: In the following code +> > ```csharp > public class A > { @@ -297,6 +302,7 @@ The accessibility domain of a nested member `M` declared in a type `T` within > } > } > ``` +> > the classes and members have the following accessibility domains: > > - The accessibility domain of `A` and `A.X` is unlimited. @@ -311,6 +317,7 @@ The accessibility domain of a nested member `M` declared in a type `T` within As described in [§7.4](basic-concepts.md#74-members), all members of a base class, except for instance constructors, finalizers, and static constructors, are inherited by derived types. This includes even private members of a base class. However, the accessibility domain of a private member includes only the program text of the type in which the member is declared. > *Example*: In the following code +> > ```csharp > class A > { @@ -330,6 +337,7 @@ As described in [§7.4](basic-concepts.md#74-members), all members of a base cla > } > } > ``` +> > the `B` class inherits the private member `x` from the `A` class. Because the member is private, it is only accessible within the *class_body* of `A`. Thus, the access to `b.x` succeeds in the `A.F` method, but fails in the `B.F` method. *end example* ### 7.5.4 Protected access @@ -346,6 +354,7 @@ Let `B` be a base class that declares a protected instance member `M`, and let 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)). > *Example*: In the following code +> > ```csharp > public class A > { @@ -367,11 +376,13 @@ In addition to these forms of access, a derived class can access a protected ins > } > } > ``` +> > within `A`, it is possible to access `x` through instances of both `A` and `B`, since in either case the access takes place *through* an instance of `A` or a class derived from `A`. However, within `B`, it is not possible to access `x` through an instance of `A`, since `A` does not derive from `B`. *end example* > *Example*: +> > ```csharp > class C > { @@ -391,11 +402,13 @@ In addition to these forms of access, a derived class can access a protected ins > } > } > ``` +> > Here, the three assignments to `x` are permitted because they all take place through instances of class types constructed from the generic type. *end 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 > class C > { @@ -410,6 +423,7 @@ In addition to these forms of access, a derived class can access a protected ins > } > } > ``` +> > the reference to `protected` member `C.x` in `D` is valid even though the class `D` derives from `C`. *end note* ### 7.5.5 Accessibility constraints @@ -432,15 +446,18 @@ The following accessibility constraints exist: - An interface or class type constraint on a type parameter shall be at least as accessible as the member which declares the constraint. > *Example*: In the following code +> > ```csharp > class A {...} > public class B: A {...} > ``` +> > the `B` class results in a compile-time error because `A` is not at least as accessible as `B`. *end example* > *Example*: Likewise, in the following code +> > ```csharp > class A {...} > @@ -451,6 +468,7 @@ The following accessibility constraints exist: > public A H() {...} > } > ``` +> > the `H` method in `B` results in a compile-time error because the return type `A` is not at least as accessible as the method. *end example* ## 7.6 Signatures and overloading @@ -479,6 +497,7 @@ Although `out` and `ref` parameter modifiers are considered part of a signature, 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. > *Example*: The following example shows a set of overloaded method declarations along with their signatures. +> > ```csharp > interface ITest > { @@ -499,6 +518,7 @@ The types `object` and `dynamic` are not distinguished when comparing signatures > void F(S s); // F<0,1>(1) ok > } > ``` +> > 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* ## 7.7 Scopes @@ -537,6 +557,7 @@ The ***scope*** of a name is the region of program text within which it is possi 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. > *Example*: +> > ```csharp > class A > { @@ -548,11 +569,13 @@ Within the scope of a namespace, class, struct, or enumeration member it is poss > int i = 0; > } > ``` +> > Here, it is valid for `F` to refer to `i` before it is declared. *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. > *Example*: +> > ```csharp > class A > { @@ -576,6 +599,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*. *end example* @@ -583,6 +607,7 @@ Within the scope of a local variable, it is a compile-time error to refer to the > *Note*: The scoping rules for local variables and local constants are designed to guarantee that the meaning of a name used in an expression context is always the same within a block. If the scope of a local variable were to extend only from its declaration to the end of the block, then in the example above, the first assignment would assign to the instance variable and the second assignment would assign to the local variable, possibly leading to compile-time errors if the statements of the block were later to be rearranged.) > > The meaning of a name within a block may differ based on the context in which the name is used. In the example +> > ```csharp > using System; > @@ -600,6 +625,7 @@ Within the scope of a local variable, it is a compile-time error to refer to the > } > } > ``` +> > the name `A` is used in an expression context to refer to the local variable `A` and in a type context to refer to the class `A`. *end note* ### 7.7.2 Name hiding @@ -615,6 +641,7 @@ Name hiding occurs when scopes overlap through nesting and when scopes overlap t Name hiding through nesting can occur as a result of nesting namespaces or types within namespaces, as a result of nesting types within classes or structs, and as a result of parameter, local variable, and local constant declarations. > *Example*: In the following code +> > ```csharp > class A > { @@ -630,11 +657,13 @@ Name hiding through nesting can occur as a result of nesting namespaces or types > } > } > ``` +> > within the `F` method, the instance variable `i` is hidden by the local variable `i`, but within the `G` method, `i` still refers to the instance variable. *end example* When a name in an inner scope hides a name in an outer scope, it hides all overloaded occurrences of that name. > *Example*: In the following code +> > ```csharp > class Outer > { @@ -653,6 +682,7 @@ When a name in an inner scope hides a name in an outer scope, it hides all overl > } > } > ``` +> > the call `F(1)` invokes the `F` declared in `Inner` because all outer occurrences of `F` are hidden by the inner declaration. For the same reason, the call `F("Hello")` results in a compile-time error. *end example* #### 7.7.2.3 Hiding through inheritance @@ -668,6 +698,7 @@ The rules governing operator declarations ([§14.10](classes.md#1410-operators)) Contrary to hiding a name from an outer scope, hiding a visible name from an inherited scope causes a warning to be reported. > *Example*: In the following code +> > ```csharp > class Base > { @@ -679,11 +710,13 @@ Contrary to hiding a name from an outer scope, hiding a visible name from an inh > public void F() {} // Warning, hiding an inherited name > } > ``` +> > the declaration of `F` in `Derived` causes a warning to be reported. Hiding an inherited name is specifically not an error, since that would preclude separate evolution of base classes. For example, the above situation might have come about because a later version of `Base` introduced an `F` method that wasn’t present in an earlier version of the class. *end example* The warning caused by hiding an inherited name can be eliminated through use of the `new` modifier: > *Example*: +> > ```csharp > class Base > { @@ -695,11 +728,13 @@ The warning caused by hiding an inherited name can be eliminated through use of > public new void F() {} > } > ``` +> > The `new` modifier indicates that the `F` in `Derived` is “new”, and that it is indeed intended to hide the inherited member. *end example* A declaration of a new member hides an inherited member only within the scope of the new member. > *Example*: +> > ```csharp > class Base > { @@ -719,6 +754,7 @@ A declaration of a new member hides an inherited member only within the scope of > } > } > ``` +> > In the example above, the declaration of `F` in `Derived` hides the `F` that was inherited from `Base`, but since the new `F` in `Derived` has private access, its scope does not extend to `MoreDerived`. Thus, the call `F()` in `MoreDerived.G` is valid and will invoke `Base.F`. *end example* ## 7.8 Namespace and type names @@ -726,6 +762,7 @@ A declaration of a new member hides an inherited member only within the scope of ### 7.8.1 General Several contexts in a C# program require a *namespace_name* or a *type_name* to be specified. + ```ANTLR namespace_name : namespace_or_type_name @@ -741,6 +778,7 @@ namespace_or_type_name | qualified_alias_member ; ``` + A *namespace_name* is a *namespace_or_type_name* that refers to a namespace. Following resolution as described below, the *namespace_or_type_name* of a *namespace_name* shall refer to a namespace, or otherwise a compile-time error occurs. No type arguments ([§8.4.2](types.md#842-type-arguments)) can be present in a *namespace_name* (only types can have type arguments). @@ -811,6 +849,7 @@ In other words, the fully qualified name of `N` is the complete hierarchical pa - 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)). > *Example*: The example below shows several namespace and type declarations along with their associated fully qualified names. +> > ```csharp > class A {} // A > namespace X // X @@ -837,6 +876,7 @@ In other words, the fully qualified name of `N` is the complete hierarchical pa > } > } > ``` +> > *end example* ## 7.9 Automatic memory management @@ -858,6 +898,7 @@ Like other languages that assume the existence of a garbage collector, C# is des The behavior of the garbage collector can be controlled, to some degree, via static methods on the class `System.GC`. This class can be used to request a collection to occur, finalizers to be run (or not run), and so forth. > *Example*: Since the garbage collector is allowed wide latitude in deciding when to collect objects and run finalizers, a conforming implementation might produce output that differs from that shown by the following code. The program +> > ```csharp > using System; > class A @@ -893,19 +934,25 @@ The behavior of the garbage collector can be controlled, to some degree, via sta > } > } > ``` +> > creates an instance of class `A` and an instance of class `B`. These objects become eligible for garbage collection when the variable `b` is assigned the value `null`, since after this time it is impossible for any user-written code to access them. The output could be either +> > ```console > Finalize instance of `A` > Finalize instance of `B` > ``` +> > or +> > ```console > Finalize instance of `B` > Finalize instance of `A` > ``` +> > because the language imposes no constraints on the order in which objects are garbage collected. > > In subtle cases, the distinction between “eligible for finalization” and “eligible for collection” can be important. For example, +> > ```csharp > using System; > class A @@ -956,13 +1003,16 @@ The behavior of the garbage collector can be controlled, to some degree, via sta > } > } > ``` +> > In the above program, if the garbage collector chooses to run the finalizer of `A` before the finalizer of `B`, then the output of this program might be: +> > ```console > Finalize instance of A > Finalize instance of B > A.F > RefA is not null > ``` +> > Note that although the instance of `A` was not in use and `A`’s finalizer was run, it is still possible for methods of `A` (in this case, `F`) to be called from another finalizer. Also, note that running of a finalizer might cause an object to become usable from the mainline program again. In this case, the running of `B`’s finalizer caused an instance of `A` that was previously not in use, to become accessible from the live reference `Test.RefA`. After the call to `WaitForPendingFinalizers`, the instance of `B` is eligible for collection, but the instance of `A` is not, because of the reference `Test.RefA`. *end example* ## 7.10 Execution order diff --git a/standard/classes.md b/standard/classes.md index 12723314a..aa49ee1f3 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -66,6 +66,7 @@ The `abstract` modifier is used to indicate that a class is incomplete and that When a non-abstract class is derived from an abstract class, the non-abstract class shall include actual implementations of all inherited abstract members, thereby overriding those abstract members. > *Example*: In the following code +> > ```csharp > abstract class A > { @@ -85,6 +86,7 @@ When a non-abstract class is derived from an abstract class, the non-abstract cl > } > } > ``` +> > the abstract class `A` introduces an abstract method `F`. Class `B` introduces an additional method `G`, but since it doesn’t provide an implementation of `F`, `B` shall also be declared abstract. Class `C` overrides `F` and provides an actual implementation. Since there are no abstract members in `C`, `C` is permitted (but not required) to be non-abstract. *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. @@ -180,30 +182,36 @@ interface_type_list 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). > *Example*: In the following code +> > ```csharp > class A {} > class B : A {} > ``` +> > Class `A` is said to be the direct base class of `B`, and `B` is said to be derived from `A`. Since `A` does not explicitly specify a direct base class, its direct base class is implicitly `object`. *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. > *Example*: Given the generic class declarations +> > ```csharp > class B {...} > class G : B {...} > ``` +> > the base class of the constructed type `G` would be `B`. *end example* The base class specified in a class declaration can be a constructed class type ([§8.4](types.md#84-constructed-types)). A base class cannot be a type parameter on its own ([§8.5](types.md#85-type-parameters)), though it can involve the type parameters that are in scope. > *Example*: +> > ```csharp > class Base {} > class Extend : Base // Valid, non-constructed class with constructed base class > class Extend : V {} // Error, type parameter used as base class > class Extend : Base {} // Valid, type parameter used as type argument for base class > ``` +> > *end example* 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. @@ -213,6 +221,7 @@ The direct base class of a class type shall not be any of the following types: ` 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. > *Example*: The following +> > ```csharp > class X > { @@ -221,17 +230,20 @@ In determining the meaning of the direct base class specification `A` of a clas > > class Z : X {} > ``` +> > is in error since in the base class specification `X` the direct base class of `Z` is considered to be `object`, and hence (by the rules of [§7.8](basic-concepts.md#78-namespace-and-type-names)) `Z` is not considered to have a member `Y`. *end example* The base classes of a class are the direct base class and its base classes. In other words, the set of base classes is the transitive closure of the direct base class relationship. > *Example*: In the following: +> > ```csharp > class A {...} > class B : A {...} > class C : B> {...} > class D : C {...} > ``` +> > the base classes of `D` are `C`, `B>`, `A`, and `object`. *end example* Except for class `object`, every class has exactly one direct base class. The `object` class has no direct base class and is the ultimate base class of all other classes. @@ -239,16 +251,21 @@ Except for class `object`, every class has exactly one direct base class. The `o It is a compile-time error for a class to depend on itself. For the purpose of this rule, a class ***directly depends on*** its direct base class (if any) and *directly depends on* the nearest enclosing class within which it is nested (if any). Given this definition, the complete set of classes upon which a class depends is the transitive closure of the *directly depends on* relationship. > *Example*: The example +> > ```csharp > class A: A {} > ``` +> > is erroneous because the class depends on itself. Likewise, the example +> > ```csharp > class A : B {} > class B : C {} > class C : A {} > ``` +> > is in error because the classes circularly depend on themselves. Finally, the example +> > ```csharp > class A : B.C {} > class B : A @@ -256,25 +273,30 @@ It is a compile-time error for a class to depend on itself. For the purpose of t > public class C {} > } > ``` +> > results in a compile-time error because A depends on `B.C` (its direct base class), which depends on `B` (its immediately enclosing class), which circularly depends on `A`. *end example* A class does not depend on the classes that are nested within it. > *Example*: In the following code +> > ```csharp > class A > { > class B : A {} > } > ``` +> > `B` depends on `A` (because `A` is both its direct base class and its immediately enclosing class), but `A` does not depend on `B` (since `B` is neither a base class nor an enclosing class of `A`). Thus, the example is valid. *end example* It is not possible to derive from a sealed class. > *Example*: In the following code +> > ```csharp > sealed class A {} > class B : A {} // Error, cannot derive from a sealed class > ``` +> > Class `B` is in error because it attempts to derive from the sealed class `A`. *end example* #### 14.2.4.3 Interface implementations @@ -284,16 +306,19 @@ A *class_base* specification may include a list of interface types, in which cas 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. > *Example*: In the following: +> > ```csharp > partial class C : IA, IB {...} > partial class C : IC {...} > partial class C : IA, IB {...} > ``` +> > the set of base interfaces for class `C` is `IA`, `IB`, and `IC`. *end example* Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. A part can provide the implementation for an interface declared on a different part. > *Example*: +> > ```csharp > partial class X > { @@ -305,17 +330,20 @@ 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. > *Example*: The following code illustrates how a class can implement and extend constructed types: +> > ```csharp > class C {} > interface I1 {} > class D : C, I1 {} > class E : C, I1 {} > ``` +> > *end example* Interface implementations are discussed further in [§17.6](interfaces.md#176-interface-implementations). @@ -421,6 +449,7 @@ If the `where` clause for a type parameter includes a constructor constraint (wh It is a compile-time error for *type_parameter_constraints* having a *primary_constraint* of `struct` to also have a *constructor_constraint*. > *Example*: The following are examples of constraints: +> > ```csharp > interface IPrintable > { @@ -447,7 +476,9 @@ It is a compile-time error for *type_parameter_constraints* having a *primary_co > ... > } > ``` +> > The following example is in error because it causes a circularity in the dependency graph of the type parameters: +> > ```csharp > class Circular > where S: T @@ -456,7 +487,9 @@ It is a compile-time error for *type_parameter_constraints* having a *primary_co > ... > } > ``` +> > The following examples illustrate additional invalid situations: +> > ```csharp > class Sealed > where S : T @@ -483,6 +516,7 @@ It is a compile-time error for *type_parameter_constraints* having a *primary_co > ... > } > ``` +> > *end example* The ***dynamic erasure*** of a type `C` is type `Cₓ` constructed as follows: @@ -527,6 +561,7 @@ A type parameter is *known to be a reference type* if it has the reference type Values of a constrained type parameter type can be used to access the instance members implied by the constraints. > *Example*: In the following: +> > ```csharp > interface IPrintable > { @@ -538,11 +573,13 @@ Values of a constrained type parameter type can be used to access the instance m > void PrintOne(T x) => x.Print(); > } > ``` +> > the methods of `IPrintable` can be invoked directly on `x` because `T` is constrained to always implement `IPrintable`. *end example* When a partial generic type declaration includes constraints, the constraints shall agree with all other parts that include constraints. Specifically, each part that includes constraints shall have constraints for the same set of type parameters, and for each type parameter, the sets of primary, secondary, and constructor constraints shall be equivalent. Two sets of constraints are equivalent if they contain the same members. If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained. > *Example*: +> > ```csharp > partial class Map > where K : IComparable @@ -563,6 +600,7 @@ When a partial generic type declaration includes constraints, the constraints sh > ... > } > ``` +> > is correct because those parts that include constraints (the first two) effectively specify the same set of primary, secondary, and constructor constraints for the same set of type parameters, respectively. *end example* ### 14.2.6 Class body @@ -586,6 +624,7 @@ All parts of a partial type shall be compiled together such that the parts can b Nested types can be declared in multiple parts by using the `partial` modifier. Typically, the containing type is declared using `partial` as well, and each part of the nested type is declared in a different part of the containing type. > *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 > { @@ -607,7 +646,9 @@ Nested types can be declared in multiple parts by using the `partial` modifier. > public bool HasOutstandingOrders() => orders.Count > 0; > } > ``` +> > When the two parts above are compiled together, the resulting code behaves as if the class had been written as a single unit, as follows: +> > ```csharp > public class Customer > { @@ -626,6 +667,7 @@ Nested types can be declared in multiple parts by using the `partial` modifier. > public bool HasOutstandingOrders() => orders.Count > 0; > } > ``` +> > *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). @@ -693,6 +735,7 @@ The inherited members of a class ([§14.3.4](classes.md#1434-inheritance)) are n 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. > *Example*: +> > ```csharp > partial class A > { @@ -714,6 +757,7 @@ 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. @@ -723,6 +767,7 @@ Field initialization order can be significant within C# code, and some guarante 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. > *Example*: The following shows several class declarations along with their instance types: +> > ```csharp > class A // instance type: A > { @@ -731,6 +776,7 @@ Each class declaration has an associated ***instance type***. For a generic clas > } > class D {} // instance type: D > ``` +> > *end example* ### 14.3.3 Members of constructed types @@ -738,6 +784,7 @@ Each class declaration has an associated ***instance type***. For a generic clas 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. > *Example*: Given the generic class declaration +> > ```csharp > class Gen > { @@ -747,13 +794,16 @@ The non-inherited members of a constructed type are obtained by substituting, fo > public int H(double d) {...} > } > ``` +> > the constructed type `Gen>` has the following members: +> > ```csharp > public int[,][] a; > public void G(int i, int[] t, Gen,int[]> gt) {...} > public IComparable Prop { get {...} set {...} } > public int H(double d) {...} > ``` +> > The type of the member `a` in the generic class declaration `Gen` is “two-dimensional array of `T`”, so the type of the member `a` in the constructed type above is “two-dimensional array of single-dimensional array of `int`”, or `int[,][]`. *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. @@ -761,6 +811,7 @@ Within instance function members, the type of `this` is the instance type ([§14 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. > *Example*: +> > ```csharp > class C > { @@ -786,6 +837,7 @@ All members of a generic class can use type parameters from any enclosing class, > } > } > ``` +> > *end example* ### 14.3.4 Inheritance @@ -807,6 +859,7 @@ A class ***inherits*** the members of its direct base class. Inheritance means t 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*. > *Example*: +> > ```csharp > class B > { @@ -818,6 +871,7 @@ The inherited members of a constructed class type are the members of the immedia > public T G(string s) {...} > } > ``` +> > In the code above, the constructed type `D` has a non-inherited member public `int` `G(string s)` obtained by substituting the type argument `int` for the type parameter `T`. `D` also has an inherited member from the class declaration `B`. This inherited member is determined by first determining the base class type `B` of `D` by substituting `int` for `T` in the base class specification `B`. Then, as a type argument to `B`, `int[]` is substituted for `U` in `public U F(long index)`, yielding the inherited member `public int[] F(long index)`. *end example* ### 14.3.5 The new modifier @@ -855,6 +909,7 @@ When a field, method, property, event, indexer, constructor, or finalizer declar - 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)). > *Example*: The following example illustrates the rules for accessing static and instance members: +> > ```csharp > class Test > { @@ -882,6 +937,7 @@ 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. *end example* ### 14.3.9 Nested types @@ -891,6 +947,7 @@ When a field, method, property, event, indexer, constructor, or finalizer declar 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***. > *Example*: In the following example: +> > ```csharp > using System; > @@ -905,6 +962,7 @@ A type declared within a class or struct is called a ***nested type***. A type t > } > } > ``` +> > class `B` is a nested type because it is declared within class `A`, and class `A` is a non-nested type because it is declared within a compilation unit. *end example* #### 14.3.9.2 Fully qualified name @@ -920,6 +978,7 @@ Non-nested types can have `public` or `internal` declared accessibility and have - A nested type that is declared in a struct can have any of three forms of declared accessibility (`public`, `internal`, or `private`) and, like other struct members, defaults to `private` declared accessibility. > *Example*: The example +> > ```csharp > public class List > { @@ -947,6 +1006,7 @@ Non-nested types can have `public` or `internal` declared accessibility and have > public int Count { get {...} } > } > ``` +> > declares a private nested class `Node`. *end example* #### 14.3.9.4 Hiding @@ -954,6 +1014,7 @@ Non-nested types can have `public` or `internal` declared accessibility and have 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. > *Example*: The example +> > ```csharp > using System; > class Base @@ -983,6 +1044,7 @@ A nested type may hide ([§7.7.2.2](basic-concepts.md#7722-hiding-through-nestin > } > } > ``` +> > shows a nested class `M` that hides the method `M` defined in `Base`. *end example* #### 14.3.9.5 this access @@ -990,7 +1052,9 @@ A nested type may hide ([§7.7.2.2](basic-concepts.md#7722-hiding-through-nestin 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. > *Example*: The following example +> > ```csharp +> > using System; > > class C @@ -1027,6 +1091,7 @@ A nested type and its containing type do not have a special relationship with re > } > } > ``` +> > shows this technique. An instance of `C` creates an instance of `Nested`, and passes its own this to `Nested`’s constructor in order to provide subsequent access to `C`’s instance members. *end example* #### 14.3.9.6 Access to private and protected members of the containing type @@ -1034,6 +1099,7 @@ A nested type and its containing type do not have a special relationship with re 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. > *Example*: The example +> > ```csharp > using System; > @@ -1052,11 +1118,13 @@ A nested type has access to all of the members that are accessible to its contai > static void Main() => C.Nested.G(); > } > ``` +> > shows a class `C` that contains a nested class `Nested`. Within `Nested`, the method `G` calls the static method `F` defined in `C`, and `F` has private declared accessibility. *end example* A nested type also may access protected members defined in a base type of its containing type. > *Example*: In the following code +> > ```csharp > using System; > class Base @@ -1085,6 +1153,7 @@ A nested type also may access protected members defined in a base type of its co > } > } > ``` +> > the nested class `Derived.Nested` accesses the protected method `F` defined in `Derived`’s base class, `Base`, by calling through an instance of `Derived`. *end example* #### 14.3.9.7 Nested types in generic classes @@ -1094,6 +1163,7 @@ A generic class declaration may contain nested type declarations. The type param Every type declaration contained within a generic class declaration is implicitly a generic type declaration. When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, shall be named. However, from within the outer class, the nested type may be used without qualification; the instance type of the outer class may be implicitly used when constructing the nested type. > *Example*: The following shows three different correct ways to refer to a constructed type created from `Inner`; the first two are equivalent: +> > ```csharp > class Outer > { @@ -1111,11 +1181,13 @@ Every type declaration contained within a generic class declaration is implicitl > } > } > ``` +> > *end example* Although it is bad programming style, a type parameter in a nested type can hide a member or type parameter declared in the outer type. > *Example*: +> > ```csharp > class Outer > { @@ -1125,6 +1197,7 @@ Although it is bad programming style, a type parameter in a nested type can hide > } > } > ``` +> > *end example* ### 14.3.10 Reserved member names @@ -1151,9 +1224,11 @@ For a property `P` ([§14.7](classes.md#147-properties)) of type `T`, the foll T get_P(); void set_P(T value); ``` + Both signatures are reserved, even if the property is read-only or write-only. > *Example*: In the following code +> > ```csharp > using System; > class A @@ -1185,12 +1260,15 @@ Both signatures are reserved, even if the property is read-only or write-only. > } > } > ``` +> > A class `A` defines a read-only property `P`, thus reserving signatures for `get_P` and `set_P` methods. `A` class `B` derives from `A` and hides both of these reserved signatures. The example produces the output: +> > ```console > 123 > 123 > 456 > ``` +> > *end example* #### 14.3.10.3 Member names reserved for events @@ -1266,13 +1344,16 @@ When a symbolic name for a constant value is desired, but when the type of that A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. > *Example*: +> > ```csharp > class A > { > public const double X = 1.0, Y = 2.0, Z = 3.0; > } > ``` +> > is equivalent to +> > ```csharp > class A > { @@ -1281,11 +1362,13 @@ A constant declaration that declares multiple constants is equivalent to multipl > public const double Z = 3.0; > } > ``` +> > *end example* Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. The compiler automatically arranges to evaluate the constant declarations in the appropriate order. > *Example*: In the following code +> > ```csharp > class A > { @@ -1298,6 +1381,7 @@ Constants are permitted to depend on other constants within the same program as > public const int Z = A.Y + 1; > } > ``` +> > the compiler first evaluates `A.Y`, then evaluates `B.Z`, and finally evaluates `A.X`, producing the values `10`, `11`, and `12`. *end example* Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. @@ -1349,13 +1433,16 @@ The value of a field is obtained in an expression using a *simple_name* ([§11.7 A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. > *Example*: +> > ```csharp > class A > { > public static int X = 1, Y, Z = 100; > } > ``` +> > is equivalent to +> > ```csharp > class A > { @@ -1364,6 +1451,7 @@ A field declaration that declares multiple fields is equivalent to multiple decl > public static int Z = 100; > } > ``` +> > *end example* ### 14.5.2 Static and instance fields @@ -1388,6 +1476,7 @@ Attempting to assign to a readonly field or pass it as an `out` or `ref` paramet 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. > *Example*: In the following code +> > ```csharp > public class Color > { @@ -1407,6 +1496,7 @@ A static readonly field is useful when a symbolic name for a constant value is d > } > } > ``` +> > the `Black`, `White`, `Red`, `Green`, and `Blue` members cannot be declared as const members because their values cannot be computed at compile-time. However, declaring them `static readonly` instead has much the same effect. *end example* #### 14.5.3.3 Versioning of constants and static readonly fields @@ -1414,6 +1504,7 @@ A static readonly field is useful when a symbolic name for a constant value is d 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 > { @@ -1423,7 +1514,9 @@ Constants and readonly fields have different binary versioning semantics. When a > } > } > ``` +> > and +> > ```csharp > using System; > @@ -1438,6 +1531,7 @@ Constants and readonly fields have different binary versioning semantics. When a > } > } > ``` +> > The `Program1` and `Program2` namespaces denote two programs that are compiled separately. Because `Program1.Utils.X` is declared as a `static readonly` field, the value output by the `Console.WriteLine` statement is not known at compile-time, but rather is obtained at run-time. Thus, if the value of `X` is changed and `Program1` is recompiled, the `Console.WriteLine` statement will output the new value even if `Program2` isn’t recompiled. However, had `X` been a constant, the value of `X` would have been obtained at the time `Program2` was compiled, and would remain unaffected by changes in `Program1` until `Program2` is recompiled. *end example* ### 14.5.4 Volatile fields @@ -1455,6 +1549,7 @@ These restrictions ensure that all threads will observe volatile writes performe - An *enum_type* having an *enum_base* type of `byte`, `sbyte`, `short`, `ushort`, `int`, or `uint`. > *Example*: The example +> > ```csharp > using System; > using System.Threading; @@ -1488,11 +1583,15 @@ These restrictions ensure that all threads will observe volatile writes performe > } > } > } +> > ``` +> > produces the output: +> > ```console > result = 143 > ``` +> > In this example, the method `Main` starts a new thread that runs the method `Thread2`. This method stores a value into a non-volatile field called `result`, then stores `true` in the volatile field `finished`. The main thread waits for the field `finished` to be set to `true`, then reads the field `result`. Since `finished` has been declared `volatile`, the main thread shall read the value `143` from the field `result`. If the field `finished` had not been declared `volatile`, then it would be permissible for the store to `result` to be visible to the main thread *after* the store to `finished`, and hence for the main thread to read the value 0 from the field `result`. Declaring `finished` as a `volatile` field prevents any such inconsistency. *end example* ### 14.5.5 Field initialization @@ -1500,6 +1599,7 @@ These restrictions ensure that all threads will observe volatile writes performe 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”. > *Example*: The example +> > ```csharp > using System; > @@ -1515,10 +1615,13 @@ The initial value of a field, whether it be a static field or an instance field, > } > } > ``` +> > produces the output +> > ```console > b = False, i = 0 > ``` +> > because `b` and `i` are both automatically initialized to default values. *end example* ### 14.5.6 Variable initializers @@ -1528,6 +1631,7 @@ The initial value of a field, whether it be a static field or an instance field, 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. > *Example*: The example +> > ```csharp > using System; > @@ -1544,10 +1648,13 @@ Field declarations may include *variable_initializer*s. For static fields, varia > } > } > ``` +> > produces the output +> > ```console > x = 1.4142135623731, i = 100, s = Hello > ``` +> > because an assignment to `x` occurs when static field initializers execute and assignments to `i` and `s` occur when the instance field initializers execute. *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. @@ -1555,6 +1662,7 @@ The default value initialization described in [§14.5.5](classes.md#1455-field-i It is possible for static fields with variable initializers to be observed in their default value state. > *Example*: However, this is strongly discouraged as a matter of style. The example +> > ```csharp > using System; > @@ -1569,10 +1677,13 @@ It is possible for static fields with variable initializers to be observed in th > } > } > ``` +> > exhibits this behavior. Despite the circular definitions of `a` and `b`, the program is valid. It results in the output +> > ```console > a = 1, b = 2 > ``` +> > because the static fields `a` and `b` are initialized to `0` (the default value for `int`) before their initializers are executed. When the initializer for `a` runs, the value of `b` is zero, and so `a` is initialized to `1`. When the initializer for `b` runs, the value of a is already `1`, and so `b` is initialized to `2`. *end example* #### 14.5.6.2 Static field initialization @@ -1580,6 +1691,7 @@ It is possible for static fields with variable initializers to be observed in th 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. > *Example*: The example +> > ```csharp > using System; > @@ -1607,19 +1719,25 @@ The static field variable initializers of a class correspond to a sequence of as > public static int Y = Test.F("Init B"); > } > ``` +> > might produce either the output: +> > ```console > Init A > Init B > 1 1 > ``` +> > or the output: +> > ```console > Init B > Init A > 1 1 > ``` +> > because the execution of `X`’s initializer and `Y`’s initializer could occur in either order; they are only constrained to occur before the references to those fields. However, in the example: +> > ```csharp > using System; > @@ -1649,12 +1767,15 @@ The static field variable initializers of a class correspond to a sequence of as > public static int Y = Test.F("Init B"); > } > ``` +> > the output shall be: +> > ```csharp > Init B > Init A > 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. *end example* #### 14.5.6.3 Instance field initialization @@ -1664,6 +1785,7 @@ The instance field variable initializers of a class correspond to a sequence of 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*. > *Example*: In the following code +> > ```csharp > class A > { @@ -1671,6 +1793,7 @@ A variable initializer for an instance field cannot reference the instance being > int y = x + 1; // Error, reference to instance member of this > } > ``` +> > the variable initializer for `y` results in a compile-time error because it references a member of the instance being created. *end example* ## 14.6 Methods @@ -1678,6 +1801,7 @@ A variable initializer for an instance field cannot reference the instance being ### 14.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 @@ -1824,6 +1948,7 @@ A *parameter_array* consists of an optional set of *attributes* ([§21](attribut 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. > *Example*: The following illustrates different kinds of parameters: +> > ```csharp > public void M( > ref int i, @@ -1836,6 +1961,7 @@ A *parameter_array* may occur after an optional parameter, but cannot have a def > params int[] a > ) { } > ``` +> > In the *formal_parameter_list* for `M`, `i` is a required `ref` parameter, `d` is a required value parameter, `b`, `s`, `o` and `t` are optional value parameters and `a` is a parameter array. *end example* 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. @@ -1870,6 +1996,7 @@ 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. > *Example*: The example +> > ```csharp > using System; > @@ -1890,15 +2017,19 @@ A method declared as an iterator ([§14.14](classes.md#1414-iterators)) may not > } > } > ``` +> > produces the output +> > ```console > i = 2, j = 1 > ``` +> > For the invocation of `Swap` in `Main`, `x` represents `i` and `y` represents `j`. Thus, the invocation has the effect of swapping the values of `i` and `j`. *end example* In a method that takes reference parameters, it is possible for multiple names to represent the same storage location. > *Example*: In the following code +> > ```csharp > class A > { @@ -1916,6 +2047,7 @@ In a method that takes reference parameters, it is possible for multiple names t > } > } > ``` +> > the invocation of `F` in `G` passes a reference to `s` for both `a` and `b`. Thus, for that invocation, the names `s`, `a`, and `b` all refer to the same storage location, and the three assignments all modify the instance field `s`. *end example* #### 14.6.2.4 Output parameters @@ -1933,6 +2065,7 @@ A method declared as a partial method ([§14.6.9](classes.md#1469-partial-method Output parameters are typically used in methods that produce multiple return values. > *Example*: +> > ```csharp > using System; > class Test @@ -1962,11 +2095,14 @@ Output parameters are typically used in methods that produce multiple return val > } > } > ``` +> > The example produces the output: +> > ```console > c:\Windows\System\ > hello.txt > ``` +> > Note that the `dir` and `name` variables can be unassigned before they are passed to `SplitPath`, and that they are considered definitely assigned following the call. *end example* #### 14.6.2.5 Parameter arrays @@ -1985,6 +2121,7 @@ A parameter array permits arguments to be specified in one of two ways in a meth 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. > *Example*: The example +> > ```csharp > using System; > class Test @@ -2008,22 +2145,28 @@ Except for allowing a variable number of arguments in an invocation, a parameter > } > } > ``` +> > produces the output +> > ```console > Array contains 3 elements: 1 2 3 > Array contains 4 elements: 10 20 30 40 > Array contains 0 elements: > ``` +> > The first invocation of `F` simply passes the array `arr` as a value parameter. The second invocation of F automatically creates a four-element `int[]` with the given element values and passes that array instance as a value parameter. Likewise, the third invocation of `F` creates a zero-element `int[]` and passes that instance as a value parameter. The second and third invocations are precisely equivalent to writing: +> > ```csharp > F(new int[] {10, 20, 30, 40}); > F(new int[] {}); > ``` +> > *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. > *Example*: The example +> > ```csharp > using System; > class Test @@ -2042,7 +2185,9 @@ When performing overload resolution, a method with a parameter array might be ap > } > } > ``` +> > produces the output +> > ```console > F(); > F(object[]); @@ -2050,6 +2195,7 @@ When performing overload resolution, a method with a parameter array might be ap > F(object[]); > F(object[]); > ``` +> > In the example, two of the possible expanded forms of the method with a parameter array are already included in the class as regular methods. These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. When a class declares a method with a parameter array, it is not uncommon to also include some of the expanded forms as regular methods. By doing so, it is possible to avoid the allocation of an array instance that occurs when an expanded form of a method with a parameter array is invoked. *end example* @@ -2057,6 +2203,7 @@ When performing overload resolution, a method with a parameter array might be ap > An array is a reference type, so the value passed for a parameter array can be `null`. > > *Example*: The example: +> > ```csharp > using System; > @@ -2071,16 +2218,20 @@ When performing overload resolution, a method with a parameter array might be ap > } > } > ``` +> > produces the output: +> > ```console > True > False > ``` +> > The second invocation produces `False` as it is equivalent to `F(new string[] { null })` and passes an array containing a single null reference. *end example* When the type of a parameter array is `object[]`, a potential ambiguity arises between the normal form of the method and the expanded form for a single `object` parameter. The reason for the ambiguity is that an `object[]` is itself implicitly convertible to type `object`. The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed. > *Example*: The example +> > ```csharp > using System; > @@ -2107,13 +2258,16 @@ When the type of a parameter array is `object[]`, a potential ambiguity arises b > } > } > ``` +> > produces the output +> > ```console > System.Int32 System.String System.Double > System.Object[] > System.Object[] > System.Int32 System.String System.Double > ``` +> > In the first and last invocations of `F`, the normal form of `F` is applicable because an implicit conversion exists from the argument type to the parameter type (both are of type `object[]`). Thus, overload resolution selects the normal form of `F`, and the argument is passed as a regular value parameter. In the second and third invocations, the normal form of `F` is not applicable because no implicit conversion exists from the argument type to the parameter type (type `object` cannot be implicitly converted to type `object[]`). However, the expanded form of `F` is applicable, so it is selected by overload resolution. As a result, a one-element `object[]` is created by the invocation, and the single element of the array is initialized with the given argument value (which itself is a reference to an `object[]`). *end example* ### 14.6.3 Static and instance methods @@ -2146,6 +2300,7 @@ For every virtual method declared in or inherited by a class, there exists a *** - Otherwise, the most derived implementation of `M` with respect to `R` is the same as the most derived implementation of `M` with respect to the direct base class of `R`. > *Example*: The following example illustrates the differences between virtual and non-virtual methods: +> > ```csharp > using System; > @@ -2174,18 +2329,22 @@ For every virtual method declared in or inherited by a class, there exists a *** > } > } > ``` +> > In the example, `A` introduces a non-virtual method `F` and a virtual method `G`. The class `B` introduces a *new* non-virtual method `F`, thus *hiding* the inherited `F`, and also *overrides* the inherited method `G`. The example produces the output: +> > ```console > A.F > B.F > B.G > B.G > ``` +> > Notice that the statement `a.G()` invokes `B.G`, not `A.G`. This is because the run-time type of the instance (which is `B`), not the compile-time type of the instance (which is `A`), determines the actual method implementation to invoke. *end example* Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. This does not present an ambiguity problem, since all but the most derived method are hidden. > *Example*: In the following code +> > ```csharp > using System; > @@ -2224,13 +2383,16 @@ Because methods are allowed to hide inherited methods, it is possible for a clas > } > } > ``` +> > the `C` and `D` classes contain two virtual methods with the same signature: The one introduced by `A` and the one introduced by `C`. The method introduced by `C` hides the method inherited from `A`. Thus, the override declaration in `D` overrides the method introduced by `C`, and it is not possible for `D` to override the method introduced by `A`. The example produces the output: +> > ```console > B.F > B.F > D.F > D.F > ``` +> > Note that it is possible to invoke the hidden virtual method by accessing an instance of `D` through a less derived type in which the method is not hidden. *end example* ### 14.6.5 Override methods @@ -2250,6 +2412,7 @@ A compile-time error occurs unless all of the following are true for an override - The override declaration does not specify any *type_parameter_constraints_clause*s. Instead, the constraints are inherited from the overridden base method. Constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. This can lead to constraints that are not valid when explicitly specified, such as value types or sealed types. > *Example*: The following demonstrates how the overriding rules work for generic classes: +> > ```csharp > abstract class C > { @@ -2272,11 +2435,13 @@ A compile-time error occurs unless all of the following are true for an override > public override void H(C x) {...} // Error, should be C > } > ``` +> > *end example* An override declaration can access the overridden base method using a *base_access* ([§11.7.13](expressions.md#11713-base-access)). > *Example*: In the following code +> > ```csharp > class A > { @@ -2296,11 +2461,13 @@ An override declaration can access the overridden base method using a *base_acce > } > } > ``` +> > the `base.PrintFields()` invocation in `B` invokes the PrintFields method declared in `A`. A *base_access* disables the virtual invocation mechanism and simply treats the base method as a non-`virtual` method. Had the invocation in `B` been written `((A)this).PrintFields()`, it would recursively invoke the `PrintFields` method declared in `B`, not the one declared in `A`, since `PrintFields` is virtual and the run-time type of `((A)this)` is `B`. *end example* Only by including an `override` modifier can a method override another method. In all other cases, a method with the same signature as an inherited method simply hides the inherited method. > *Example*: In the following code +> > ```csharp > class A > { @@ -2312,11 +2479,13 @@ Only by including an `override` modifier can a method override another method. I > public virtual void F() {} // Warning, hiding inherited F() > } > ``` +> > the `F` method in `B` does not include an `override` modifier and therefore does not override the `F` method in `A`. Rather, the `F` method in `B` hides the method in `A`, and a warning is reported because the declaration does not include a new modifier. *end example* > *Example*: In the following code +> > ```csharp > class A > { @@ -2333,6 +2502,7 @@ Only by including an `override` modifier can a method override another method. I > public override void F() {} // Ok, overrides A.F > } > ``` +> > the `F` method in `B` hides the virtual `F` method inherited from `A`. Since the new `F` in `B` has private access, its scope only includes the class body of `B` and does not extend to `C`. Therefore, the declaration of `F` in `C` is permitted to override the `F` inherited from `A`. *end example* ### 14.6.6 Sealed methods @@ -2340,6 +2510,7 @@ Only by including an `override` modifier can a method override another method. I 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. > *Example*: The example +> > ```csharp > using System; > @@ -2360,6 +2531,7 @@ When an instance method declaration includes a `sealed` modifier, that method is > public override void G() => Console.WriteLine("C.G"); > } > ``` +> > the class `B` provides two override methods: an `F` method that has the `sealed` modifier and a `G` method that does not. `B`’s use of the `sealed` modifier prevents `C` from further overriding `F`. *end example* ### 14.6.7 Abstract methods @@ -2371,6 +2543,7 @@ An abstract method declaration introduces a new virtual method but does not prov Abstract method declarations are only permitted in abstract classes ([§14.2.2.2](classes.md#14222-abstract-classes)). > *Example*: In the following code +> > ```csharp > public abstract class Shape > { @@ -2387,11 +2560,13 @@ Abstract method declarations are only permitted in abstract classes ([§14.2.2.2 > public override void Paint(Graphics g, Rectangle r) => g.DrawRect(r); > } > ``` +> > the `Shape` class defines the abstract notion of a geometrical shape object that can paint itself. The `Paint` method is abstract because there is no meaningful default implementation. The `Ellipse` and `Box` classes are concrete `Shape` implementations. Because these classes are non-abstract, they are required to override the `Paint` method and provide an actual implementation. *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. > *Example*: In the following code +> > ```csharp > abstract class A > { @@ -2404,11 +2579,13 @@ It is a compile-time error for a *base_access* ([§11.7.13](expressions.md#11713 > public override void F() => base.F(); > } > ``` +> > a compile-time error is reported for the `base.F()` invocation because it references an abstract method. *end example* An abstract method declaration is permitted to override a virtual method. This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. > *Example*: In the following code +> > ```csharp > using System; > class A @@ -2426,6 +2603,7 @@ An abstract method declaration is permitted to override a virtual method. This a > public override void F() => Console.WriteLine("C.F"); > } > ``` +> > class `A` declares a virtual method, class `B` overrides this method with an abstract method, and class `C` overrides the abstract method to provide its own implementation. *end example* ### 14.6.8 External methods @@ -2435,6 +2613,7 @@ When a method declaration includes an `extern` modifier, the method is said to b The mechanism by which linkage to an external method is achieved, is implementation-defined. > *Example*: The following example demonstrates the use of the `extern` modifier and the `DllImport` attribute: +> > ```csharp > using System.Text; > using System.Security.Permissions; @@ -2455,6 +2634,7 @@ The mechanism by which linkage to an external method is achieved, is implementat > static extern bool SetCurrentDirectory(string name); > } > ``` +> > *end example* ### 14.6.9 Partial methods @@ -2476,13 +2656,16 @@ 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`: +> > ```csharp > partial class P > { > static partial void M(int x); > } > ``` +> > Then the implementing partial method declaration and invocation in other file: +> > ```csharp > partial class P > { @@ -2490,6 +2673,7 @@ Only a defining partial method participates in overload resolution. Thus, whethe > static partial void M(int y) {} > } > ``` +> > is **invalid** as the invocation uses the argument name from the implementing and not the defining partial method declaration. *end note* If no part of a partial type declaration contains an implementing declaration for a given partial method, any expression statement invoking it is simply removed from the combined type declaration. Thus the invocation expression, including any subexpressions, has no effect at run-time. The partial method itself is also removed and will not be a member of the combined type declaration. @@ -2594,6 +2778,7 @@ class Customer 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. > *Example*: The following is an example of a static class that declares two extension methods: +> > ```csharp > public static class Extensions > { @@ -2611,11 +2796,13 @@ 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. > *Example*: The following program uses the extension methods declared above: +> > ```csharp > static class Program > { @@ -2629,7 +2816,9 @@ 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 > { @@ -2643,6 +2832,7 @@ An extension method is a regular static method. In addition, where its enclosing > } > } > ``` +> > *end example* ### 14.6.11 Method body @@ -2662,6 +2852,7 @@ When the effective return type of a method is not `void` and the method has a bl 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; }`. > *Example*: In the following code +> > ```csharp > class A > { @@ -2687,6 +2878,7 @@ When the effective return type of a method is not `void` and the method has an e > public int I(bool b) => b ? 1 : 0; > } > ``` +> > the value-returning `F` method results in a compile-time error because control can flow off the end of the method body. The `G` and `H` methods are correct because all possible execution paths end in a return statement that specifies a return value. The `I` method is correct, because its body is equivalent to a statement block with just a single return statement in it. *end example* ## 14.7 Properties @@ -2819,6 +3011,7 @@ Based on the presence or absence of the get and set accessors, a property is cla > *Example*: In the following code +> > ```csharp > public class Button : Control > { @@ -2843,18 +3036,22 @@ Based on the presence or absence of the get and set accessors, a property is cla > } > } > ``` +> > the `Button` control declares a public `Caption` property. The get accessor of the Caption property returns the `string` stored in the private `caption` field. The set accessor checks if the new value is different from the current value, and if so, it stores the new value and repaints the control. Properties often follow the pattern shown above: The get accessor simply returns a value stored in a `private` field, and the set accessor modifies that `private` field and then performs any additional actions required to update fully the state of the object. > Given the `Button` class above, the following is an example of use of the `Caption` property: +> > ```csharp > Button okButton = new Button(); > okButton.Caption = "OK"; // Invokes set accessor > string s = okButton.Caption; // Invokes get accessor > ``` +> > Here, the set accessor is invoked by assigning a value to the property, and the get accessor is invoked by referencing the property in an expression. *end example* The get and set accessors of a property are not distinct members, and it is not possible to declare the accessors of a property separately. > *Example*: The example +> > ```csharp > class A > { @@ -2873,11 +3070,13 @@ The get and set accessors of a property are not distinct members, and it is not > } > } > ``` +> > does not declare a single read-write property. Rather, it declares two properties with the same name, one read-only and one write-only. Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur. *end example* When a derived class declares a property by the same name as an inherited property, the derived property hides the inherited property with respect to both reading and writing. > *Example*: In the following code +> > ```csharp > class A > { @@ -2895,17 +3094,21 @@ When a derived class declares a property by the same name as an inherited proper > } > } > ``` +> > the `P` property in `B` hides the `P` property in `A` with respect to both reading and writing. Thus, in the statements +> > ```csharp > B b = new B(); > b.P = 1; // Error, B.P is read-only > ((A)b).P = 1; // Ok, reference to A.P > ``` +> > the assignment to `b.P` causes a compile-time error to be reported, since the read-only `P` property in `B` hides the write-only `P` property in `A`. Note, however, that a cast can be used to access the hidden `P` property. *end example* Unlike public fields, properties provide a separation between an object’s internal state and its public interface. > *Example*: Consider the following code, which uses a `Point` struct to represent a location: +> > ```csharp > class Label > { @@ -2925,7 +3128,9 @@ Unlike public fields, properties provide a separation between an object’s inte > public string Caption => caption; > } > ``` +> > Here, the `Label` class uses two `int` fields, `x` and `y`, to store its location. The location is publicly exposed both as an `X` and a `Y` property and as a `Location` property of type `Point`. If, in a future version of `Label`, it becomes more convenient to store the location as a `Point` internally, the change can be made without affecting the public interface of the class: +> > ```csharp > class Label > { @@ -2944,6 +3149,7 @@ Unlike public fields, properties provide a separation between an object’s inte > public string Caption => caption; > } > ``` +> > Had `x` and `y` instead been `public readonly` fields, it would have been impossible to make such a change to the `Label` class. *end example* @@ -2953,6 +3159,7 @@ Unlike public fields, properties provide a separation between an object’s inte > *Example*: Since invoking a get accessor is conceptually equivalent to reading the value of a field, it is considered bad programming style for get accessors to have observable side-effects. In the example +> > ```csharp > class Counter > { @@ -2961,6 +3168,7 @@ Unlike public fields, properties provide a separation between an object’s inte > public int Next => next++; > } > ``` +> > the value of the `Next` property depends on the number of times the property has previously been accessed. Thus, accessing the property produces an observable side effect, and the property should be implemented as a method instead. > > The “no side-effects” convention for get accessors doesn’t mean that get accessors should always be written simply to return values stored in fields. Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. However, a properly designed get accessor performs no actions that cause observable changes in the state of the object. *end example* @@ -2968,6 +3176,7 @@ Unlike public fields, properties provide a separation between an object’s inte Properties can be used to delay initialization of a resource until the moment it is first referenced. > *Example*: +> > ```csharp > using System.IO; > public class Console @@ -3014,10 +3223,13 @@ Properties can be used to delay initialization of a resource until the moment it > ... > } > ``` +> > The `Console` class contains three properties, `In`, `Out`, and `Error`, that represent the standard input, output, and error devices, respectively. By exposing these members as properties, the `Console` class can delay their initialization until they are actually used. For example, upon first referencing the `Out` property, as in +> > ```csharp > Console.Out.WriteLine("hello, world"); > ``` +> > the underlying `TextWriter` for the output device is created. However, if the application makes no reference to the `In` and `Error` properties, then no objects are created for those devices. *end example* ### 14.7.4 Automatically implemented properties @@ -3029,6 +3241,7 @@ When a property is specified as an automatically implemented property, a hidden 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)). > *Example*: +> > ```csharp > public class Point > { @@ -3036,7 +3249,9 @@ An auto-property may optionally have a *property_initializer*, which is applied > public int Y { get; set; } // Automatically implemented > } > ``` +> > is equivalent to the following declaration: +> > ```csharp > public class Point > { @@ -3047,11 +3262,13 @@ An auto-property may optionally have a *property_initializer*, which is applied > public int Y { get { return y; } set { y = value; } } > } > ``` +> > *end example* > *Example*: In the following +> > ```csharp > public class ReadOnlyPoint > { @@ -3065,7 +3282,9 @@ An auto-property may optionally have a *property_initializer*, which is applied > } > } > ``` +> > is equivalent to the following declaration: +> > ```csharp > public class ReadOnlyPoint > { @@ -3081,6 +3300,7 @@ An auto-property may optionally have a *property_initializer*, which is applied > } > } > ``` +> > The assignments to the read-only field are valid, because they occur within the constructor. *end example* ### 14.7.5 Accessibility @@ -3096,6 +3316,7 @@ Once a particular property or indexer has been selected, the accessibility domai - If the usage is as the target of compound assignment ([§11.18.3](expressions.md#11183-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. > *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. +> > ```csharp > class A > { @@ -3142,11 +3363,13 @@ Once a particular property or indexer has been selected, the accessibility domai > } > } > ``` +> > *end example* 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*: +> > ```csharp > public interface I > { @@ -3162,6 +3385,7 @@ 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 @@ -3184,6 +3408,7 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov - 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 +> > ```csharp > abstract class A > { @@ -3203,9 +3428,11 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov > public abstract int Z { get; set; } > } > ``` +> > `X` is a virtual read-only property, `Y` is a virtual read-write property, and `Z` is an abstract read-write property. Because `Z` is abstract, the containing class A shall also be declared abstract. > > A class that derives from `A` is shown below: +> > ```csharp > class B : A > { @@ -3228,11 +3455,13 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov > } > } > ``` +> > Here, the declarations of `X`, `Y`, and `Z` are overriding property declarations. Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. The get accessor of `X` and the set accessor of `Y` use the base keyword to access the inherited accessors. The declaration of `Z` overrides both abstract accessors—thus, there are no outstanding `abstract` function members in `B`, and `B` is permitted to be a non-abstract class. *end example* When a property is declared as an override, any overridden accessors shall be accessible to the overriding code. In addition, the declared accessibility of both the property or indexer itself, and of the accessors, shall match that of the overridden member and accessors. > *Example*: +> > ```csharp > public class B > { @@ -3252,6 +3481,7 @@ When a property is declared as an override, any overridden accessors shall be ac > } > } > ``` +> > *end example* ## 14.8 Events @@ -3322,6 +3552,7 @@ The only operations that are permitted on an event by code that is outside the t 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.18.4](expressions.md#11184-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: +> > ```csharp > public delegate void EventHandler(object sender, EventArgs e); > @@ -3354,6 +3585,7 @@ In an operation of the form `x += y` or `x –= y`, when `x` is an event the > } > } > ``` +> > Here, the `LoginDialog` instance constructor creates two `Button` instances and attaches event handlers to the `Click` events. *end example* ### 14.8.2 Field-like events @@ -3361,6 +3593,7 @@ In an operation of the form `x += y` or `x –= y`, when `x` is an event the 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`. > *Example*: In the following code +> > ```csharp > public delegate void EventHandler(object sender, EventArgs e); > @@ -3380,28 +3613,36 @@ Within the program text of the class or struct that contains the declaration of > public void Reset() => Click = null; > } > ``` +> > `Click` is used as a field within the `Button` class. As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. The `OnClick` method in the `Button` class “raises” the `Click` event. The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events. Note that the delegate invocation is preceded by a check that ensures the delegate is non-null and that the check is made on a local copy to ensure thread safety. > > Outside the declaration of the `Button` class, the `Click` member can only be used on the left-hand side of the `+=` and `–=` operators, as in +> > ```csharp > b.Click += new EventHandler(...); > ``` +> > which appends a delegate to the invocation list of the `Click` event, and +> > ```csharp > Click –= new EventHandler(...); > ``` +> > which removes a delegate from the invocation list of the `Click` event. *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. > *Note*: Thus, an instance event declaration of the form: +> > ```csharp > class X > { > public event D Ev; > } > ``` +> > shall be compiled to something equivalent to: +> > ```csharp > class X > { @@ -3436,7 +3677,9 @@ Each *add_accessor_declaration* and *remove_accessor_declaration* corresponds to 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. > *Example*: In the following code +> > ```csharp +> > class Control: Component > { > // Unique keys for events @@ -3478,6 +3721,7 @@ Since an `event` accessor implicitly has a parameter named `value`, it is a comp > } > } > ``` +> > the `Control` class implements an internal storage mechanism for events. The `AddEventHandler` method associates a delegate value with a key, the `GetEventHandler` method returns the delegate currently associated with a key, and the `RemoveEventHandler` method removes a delegate as an event handler for the specified event. Presumably, the underlying storage mechanism is designed such that there is no cost for associating a null delegate value with a key, and thus unhandled events consume no storage. *end example* ### 14.8.4 Static and instance events @@ -3594,6 +3838,7 @@ Aside from these differences, all rules defined in [§14.7.3](classes.md#1473-ac 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. +> > ```csharp > using System; > class BitArray @@ -3641,9 +3886,11 @@ When an indexer declaration includes an `extern` modifier, the indexer is said t > } > } > ``` +> > An instance of the `BitArray` class consumes substantially less memory than a corresponding `bool[]` (since each value of the former occupies only one bit instead of the latter’s one `byte`), but it permits the same operations as a `bool[]`. > > 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 > { @@ -3672,12 +3919,15 @@ 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[]`. *end example* > *Example*: The following example shows a 26×10 grid class that has an indexer with two parameters. The first parameter is required to be an upper- or lowercase letter in the range A–Z, and the second is required to be an integer in the range 0–9. +> > ```csharp > using System; > class Grid @@ -3717,6 +3967,7 @@ When an indexer declaration includes an `extern` modifier, the indexer is said t > } > } > ``` +> > *end example* ## 14.10 Operators @@ -3810,6 +4061,7 @@ The signature of a unary operator consists of the operator token (`+`, `-`, `!`, 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.21](expressions.md#1121-boolean-expressions). > *Example*: The following example shows an implementation and subsequent usage of operator++ for an integer vector class: +> > ```csharp > public class IntVector > { @@ -3839,6 +4091,7 @@ 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)). *end example* ### 14.10.3 Binary operators @@ -3878,6 +4131,7 @@ For a given source type `S` and target type `T`, if `S` or `T` are nullable For the purposes of these rules, any type parameters associated with `S` or `T` are considered to be unique types that have no inheritance relationship with other types, and any constraints on those type parameters are ignored. > *Example*: In the following: +> > ```csharp > class C {...} > @@ -3888,6 +4142,7 @@ For the purposes of these rules, any type parameters associated with `S` or `T > public static implicit operator C(D value) {...} // Error > } > ``` +> > the first two operator declarations are permitted because `T` and `int` and `string`, respectively are considered unique types with no relationship. However, the third operator is an error because `C` is the base class of `D`. *end example* From the second rule, it follows that a conversion operator shall convert either to or from the class or struct type in which the operator is declared. @@ -3897,6 +4152,7 @@ From the second rule, it follows that a conversion operator shall convert either It is not possible to directly redefine a pre-defined conversion. Thus, conversion operators are not allowed to convert from or to `object` because implicit and explicit conversions already exist between `object` and all other types. Likewise, neither the source nor the target types of a conversion can be a base type of the other, since a conversion would then already exist. However, it *is* possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. > *Example*: +> > ```csharp > struct Convertible > { @@ -3904,6 +4160,7 @@ It is not possible to directly redefine a pre-defined conversion. Thus, conversi > public static explicit operator T(Convertible value) {...} > } > ``` +> > when type `object` is specified as a type argument for `T`, the second operator declares a conversion that already exists (an implicit, and therefore also an explicit, conversion exists from any type to type object). *end example* In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. Specifically: @@ -3916,6 +4173,7 @@ In cases where a pre-defined conversion exists between two types, any user-defin For all types but `object`, the operators declared by the `Convertible` type above do not conflict with pre-defined conversions. > *Example*: +> > ```csharp > void F(int i, Convertible n) > { @@ -3925,7 +4183,9 @@ For all types but `object`, the operators declared by the `Convertible` type > n = (Convertible)i; // User-defined implicit conversion > } > ``` +> > However, for type `object`, pre-defined conversions hide the user-defined conversions in all cases but one: +> > ```csharp > void F(object o, Convertible n) > { @@ -3935,6 +4195,7 @@ For all types but `object`, the operators declared by the `Convertible` type > n = (Convertible)o; // Pre-defined unboxing conversion > } > ``` +> > *end example* User-defined conversions are not allowed to convert from or to *interface_type*s. In particular, this restriction ensures that no user-defined transformations occur when converting to an *interface_type*, and that a conversion to an *interface_type* succeeds only if the `object` being converted actually implements the specified *interface_type*. @@ -3946,6 +4207,7 @@ The signature of a conversion operator consists of the source type and the targe > *Example*: In the following code +> > ```csharp > using System; > public struct Digit @@ -3965,6 +4227,7 @@ The signature of a conversion operator consists of the source type and the targe > public static explicit operator Digit(byte b) => new Digit(b); > } > ``` +> > the conversion from `Digit` to `byte` is implicit because it never throws exceptions or loses information, but the conversion from `byte` to `Digit` is explicit since `Digit` can only represent a subset of the possible values of a `byte`. *end example* ## 14.11 Instance constructors @@ -4030,18 +4293,23 @@ All instance constructors (except those for class `object`) implicitly include a If an instance constructor has no constructor initializer, a constructor initializer of the form `base()` is implicitly provided. > *Note*: Thus, an instance constructor declaration of the form +> > ```csharp > C(...) {...} > ``` +> > is exactly equivalent to +> > ```csharp > C(...) : base() {...} > ``` +> > *end note* The scope of the parameters given by the *formal_parameter_list* of an instance constructor declaration includes the constructor initializer of that declaration. Thus, a constructor initializer is permitted to access the parameters of the constructor. > *Example*: +> > ```csharp > class A > { @@ -4053,6 +4321,7 @@ The scope of the parameters given by the *formal_parameter_list* of an instance > public B(int x, int y) : base(x + y, x - y) {} > } > ``` +> > *end example* 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*. @@ -4066,6 +4335,7 @@ When an instance constructor has no constructor initializer, or it has a constru 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. > *Example*: Given the following: +> > ```csharp > using System; > class A @@ -4091,12 +4361,16 @@ Variable initializers are transformed into assignment statements, and these assi > Console.WriteLine($"x = {x}, y = {y}"); > } > ``` +> > when new `B()` is used to create an instance of `B`, the following output is produced: +> > ```console > x = 1, y = 0 > ``` +> > The value of `x` is 1 because the variable initializer is executed before the base class instance constructor is invoked. However, the value of `y` is 0 (the default value of an `int`) because the assignment to `y` is not executed until after the base class constructor returns. > It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the *constructor_body*. The example +> > ```csharp > using System; > using System.Collections; @@ -4133,7 +4407,9 @@ Variable initializers are transformed into assignment statements, and these assi > } > } > ``` +> > contains several variable initializers; it also contains constructor initializers of both forms (`base` and `this`). The example corresponds to the code shown below, where each comment indicates an automatically inserted statement (the syntax used for the automatically inserted constructor invocations isn’t valid, but merely serves to illustrate the mechanism). +> > ```csharp > using System.Collections; > class A @@ -4176,6 +4452,7 @@ Variable initializers are transformed into assignment statements, and these assi > } > } > ``` +> > *end example* ### 14.11.5 Default constructors @@ -4183,18 +4460,23 @@ Variable initializers are transformed into assignment statements, and these assi 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. > *Note*: Thus, the default constructor is always of the form +> > ```csharp > protected C(): base() {} > ``` +> > or +> > ```csharp > public C(): base() {} > ``` +> > where `C` is the name of the class. *end note* If overload resolution is unable to determine a unique best candidate for the base-class constructor initializer then a compile-time error occurs. > *Example*: In the following code +> > ```csharp > class Message > { @@ -4202,7 +4484,9 @@ If overload resolution is unable to determine a unique best candidate for the ba > string text; > } > ``` +> > a default constructor is provided because the class contains no instance constructor declarations. Thus, the example is precisely equivalent to +> > ```csharp > class Message > { @@ -4212,6 +4496,7 @@ If overload resolution is unable to determine a unique best candidate for the ba > public Message() : base() {} > } > ``` +> > *end example* ## 14.12 Static constructors @@ -4259,6 +4544,7 @@ If a class contains the `Main` method ([§7.1](basic-concepts.md#71-application- 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. > *Example*: The example +> > ```csharp > using System; > @@ -4297,18 +4583,22 @@ To initialize a new closed class type, first a new set of static fields ([§14.5 > } > } > ``` +> > must produce the output: +> > ```console > Init A > A.F > Init B > B.F > ``` +> > because the execution of `A`’s static constructor is triggered by the call to `A.F`, and the execution of `B`’s static constructor is triggered by the call to `B.F`. *end example* It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state. > *Example*: The example +> > ```csharp > using System; > class A @@ -4333,16 +4623,19 @@ It is possible to construct circular dependencies that allow static fields with > } > } > ``` +> > produces the output > > ```console > X = 1, Y = 2 > ``` +> > To execute the `Main` method, the system first runs the initializer for `B.Y`, prior to class `B`’s static constructor. `Y`’s initializer causes `A`’s `static` constructor to be run because the value of `A.X` is referenced. The static constructor of `A` in turn proceeds to compute the value of `X`, and in doing so fetches the default value of `Y`, which is zero. `A.X` is thus initialized to 1. The process of running `A`’s static field initializers and static constructor then completes, returning to the calculation of the initial value of `Y`, the result of which becomes 2. *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)). > *Example*: The following type uses a static constructor to enforce that the type argument is an enum: +> > ```csharp > class Gen where T : struct > { @@ -4355,6 +4648,7 @@ Because the static constructor is executed exactly once for each closed construc > } > } > ``` +> > *end example* ## 14.13 Finalizers @@ -4391,6 +4685,7 @@ Finalizers are not inherited. Thus, a class has no finalizers other than the one Finalizers are invoked automatically, and cannot be invoked explicitly. An instance becomes eligible for finalization when it is no longer possible for any code to use that instance. Execution of the finalizer for the instance may occur at any time after the instance becomes eligible for finalization ([§7.9](basic-concepts.md#79-automatic-memory-management)). When an instance is finalized, the finalizers in that instance’s inheritance chain are called, in order, from most derived to least derived. A finalizer may be executed on any thread. For further discussion of the rules that govern when and how a finalizer is executed, see [§7.9](basic-concepts.md#79-automatic-memory-management). > *Example*: The output of the example +> > ```csharp > using System; > class A @@ -4420,16 +4715,20 @@ Finalizers are invoked automatically, and cannot be invoked explicitly. An insta > } > } > ``` +> > is +> > ```console > B's finalizer > A's finalizer > ``` +> > since finalizers in an inheritance chain are called in order, from most derived to least derived. *end example* Finalizers are implemented by overriding the virtual method `Finalize` on `System.Object`. C# programs are not permitted to override this method or call it (or overrides of it) directly. > *Example*: For instance, the program +> > ```csharp > class A > { @@ -4440,17 +4739,20 @@ Finalizers are implemented by overriding the virtual method `Finalize` on `Syste > } > } > ``` +> > contains two errors. *end example* The compiler behaves as if this method, and overrides of it, do not exist at all. > *Example*: Thus, this program: +> > ```csharp > class A > { > void Finalize() {} // Permitted > } > ``` +> > is valid and the method shown hides `System.Object`’s `Finalize` method. *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). diff --git a/standard/conversions.md b/standard/conversions.md index 051fb8dd2..ae8fc8b64 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -5,11 +5,13 @@ A ***conversion*** causes an expression to be converted to, or treated as being of, a particular type; in the former case a conversion may involve a change in representation. Conversions can be ***implicit*** or ***explicit***, and this determines whether an explicit cast is required. > *Example*: For instance, the conversion from type `int` to type `long` is implicit, so expressions of type `int` can implicitly be treated as type `long`. The opposite conversion, from type `long` to type `int`, is explicit and so an explicit cast is required. +> > ```csharp > int a = 123; > long b = a; // implicit conversion from int to long > int c = (int) b; // explicit conversion from long to int > ``` +> > *end example* Some conversions are defined by the language. Programs may also define their own conversions ([§10.5](conversions.md#105-user-defined-conversions)). @@ -17,6 +19,7 @@ Some conversions are defined by the language. Programs may also define their own Some conversions in the language are defined from expressions to types, others from types to types. A conversion from a type applies to all expressions that have that type. > *Example*: +> > ```csharp > enum Color { Red, Blue, Green } > Color c0 = 0; // The expression 0 converts implicitly to enum types @@ -24,6 +27,7 @@ Some conversions in the language are defined from expressions to types, others f > String x = null; // Conversion from null expression (no type) to String > Func square = x => x * x; // Conversion from lambda expression to delegate type > ``` +> > *end example* ## 10.2 Implicit conversions @@ -145,6 +149,7 @@ Boxing a value of a *non-nullable-value-type* consists of allocating an object i Boxing a value of a *nullable_value_type* produces a null reference if it is the null value (`HasValue` is false), or the result of unwrapping and boxing the underlying value otherwise. > *Note*: The process of boxing may be imagined in terms of the existence of a boxing class for every value type. For example, consider a `struct S` implementing an interface `I`, with a boxing class called `S_Boxing`. +> > ```csharp > interface I > { @@ -171,17 +176,23 @@ Boxing a value of a *nullable_value_type* produces a null reference if it is the > } > } > ``` +> > Boxing a value `v` of type `S` now consists of executing the expression `new S_Boxing(v)` and returning the resulting instance as a value of the target type of the conversion. Thus, the statements +> > ```csharp > S s = new S(); > object box = s; > ``` +> > can be thought of as similar to: +> > ```csharp > S s = new S(); > object box = new S_Boxing(s); > ``` +> > The imagined boxing type described above does not actually exist. Instead, a boxed value of type `S` has the runtime type `S`, and a runtime type check using the `is` operator with a value type as the right operand tests whether the left operand is a boxed version of the right operand. For example, +> > ```csharp > int i = 123; > object box = i; @@ -189,9 +200,11 @@ Boxing a value of a *nullable_value_type* produces a null reference if it is the > Console.Write("Box contains an int"); > } > ``` +> > will output the string “Box contains an `int`” on the console. > > A boxing conversion implies making a copy of the value being boxed. This is different from a conversion of a *reference_type* to type `object`, in which the value continues to reference the same instance and simply is regarded as the less derived type `object`. For example, given the declaration +> > ```csharp > struct Point > { @@ -204,13 +217,16 @@ Boxing a value of a *nullable_value_type* produces a null reference if it is the > } > } > ``` +> > the following statements +> > ```csharp > Point p = new Point(10, 10); > object box = p; > p.x = 20; > Console.Write(((Point)box).x); > ``` +> > will output the value 10 on the console because the implicit boxing operation that occurs in the assignment of `p` to `box` causes the value of `p` to be copied. Had `Point` been declared a `class` instead, the value 20 would be output because `p` and `box` would reference the same instance. > > The analogy of a boxing class should not be used as more than a helpful tool for picturing how boxing works conceptually. There are numerous subtle differences between the behavior described by this specification and the behavior that would result from boxing being implemented in precisely this manner. *end note* @@ -222,6 +238,7 @@ An implicit dynamic conversion exists from an expression of type dynamic to any 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. > *Example*: The following illustrates implicit dynamic conversions: +> > ```csharp > object o = "object"; > dynamic d = "dynamic"; @@ -229,6 +246,7 @@ This implicit conversion seemingly violates the advice in the beginning of [§10 > string s2 = d; // Compiles and succeeds at run-time > int i = d; // Compiles but fails at run-time – no conversion exists > ``` +> > The assignments to `s2` and `i` both employ implicit dynamic conversions, where the binding of the operations is suspended until run-time. At run-time, implicit conversions are sought from the run-time type of `d`(`string`) to the target type. A conversion is found to `string` but not to `int`. *end example* ### 10.2.11 Implicit constant expression conversions @@ -405,15 +423,19 @@ An unboxing operation to a *non_nullable_value_type* consists of first checking Unboxing to a *nullable_value_type* produces the null value of the *nullable_value_type* if the source operand is `null`, or the wrapped result of unboxing the object instance to the underlying type of the *nullable_value_type* otherwise. > *Note*: Referring to the imaginary boxing class described in [§10.2.9](conversions.md#1029-boxing-conversions), an unboxing conversion of an object box to a *value_type* `S` consists of executing the expression `((S_Boxing)box).value`. Thus, the statements +> > ```csharp > object box = new S(); > S s = (S)box; > ``` +> > conceptually correspond to +> > ```csharp > object box = new S_Boxing(new S()); > S s = ((S_Boxing)box).value; > ``` +> > *end note* For an unboxing conversion to a given *non_nullable_value_type* to succeed at run-time, the value of the source operand shall be a reference to a boxed value of that *non_nullable_value_type*. If the source operand is `null` a `System.NullReferenceException` is thrown. If the source operand is a reference to an incompatible object, a `System.InvalidCastException` is thrown. @@ -427,6 +449,7 @@ An explicit dynamic conversion exists from an expression of type `dynamic` to an If dynamic binding of the conversion is not desired, the expression can be first converted to `object`, and then to the desired type. > *Example*: Assume the following class is defined: +> > ```csharp > class C > { @@ -443,13 +466,16 @@ If dynamic binding of the conversion is not desired, the expression can be first > } > } > ``` +> > The following illustrates explicit dynamic conversions: +> > ```csharp > object o = "1"; > dynamic d = "2"; > var c1 = (C)o; // Compiles, but explicit reference conversion fails > var c2 = (C)d; // Compiles and user defined conversion succeeds > ``` +> > The best conversion of `o` to `C` is found at compile-time to be an explicit reference conversion. This fails at run-time, because `"1"` is not in fact a `C`. The conversion of `d` to `C` however, as an explicit dynamic conversion, is suspended to run-time, where a user defined conversion from the run-time type of `d` (`string`) to `C` is found, and succeeds. *end example* ### 10.3.8 Explicit conversions involving type parameters @@ -478,6 +504,7 @@ In all cases, the rules ensure that a conversion is executed as an unboxing conv The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear. > *Example*: Consider the following declaration: +> > ```csharp > class X > { @@ -487,7 +514,9 @@ The above rules do not permit a direct explicit conversion from an unconstrained > } > } > ``` +> > If the direct explicit conversion of `t` to `long` were permitted, one might easily expect that `X.F(7)` would return `7L`. However, it would not, because the standard numeric conversions are only considered when the types are known to be numeric at binding-time. In order to make the semantics clear, the above example must instead be written: +> > ```csharp > class X > { @@ -497,6 +526,7 @@ The above rules do not permit a direct explicit conversion from an unconstrained > } > } > ``` +> > This code will now compile but executing `X.F(7)` would then throw an exception at run-time, since a boxed `int` cannot be converted directly to a `long`. *end example* ### 10.3.9 User-defined explicit conversions @@ -673,6 +703,7 @@ Specifically, an anonymous function `F` is compatible with a delegate type `D` - If the body of `F` is a statement 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 statement 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`. > *Example*: The following examples illustrate these rules: +> > ```csharp > delegate void D(int x); > D d1 = delegate { }; // Ok @@ -713,21 +744,26 @@ Specifically, an anonymous function `F` is compatible with a delegate type `D` > return "Hello"; > }; > ``` +> > *end example* > *Example*: The examples that follow use a generic delegate type `Func` that represents a function that takes an argument of type `A` and returns a value of type `R`: +> > ```csharp > delegate R Func(A arg); > ``` +> > In the assignments +> > ```csharp > Func f1 = x => x + 1; // Ok > Func f2 = x => x + 1; // Ok > Func f3 = x => x + 1; // Error > Func> f4 = async x => x + 1; // Ok > ``` +> > the parameter and return types of each anonymous function are determined from the type of the variable to which the anonymous function is assigned. > > The first assignment successfully converts the anonymous function to the delegate type `Func` because, when `x` is given type `int`, `x + 1` is a valid expression that is implicitly convertible to type `int`. @@ -749,6 +785,7 @@ Conversion of an anonymous function to a delegate type produces a delegate insta 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. Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance. The term semantically identical is used here to mean that execution of the anonymous functions will, in all cases, produce the same effects given the same arguments. This rule permits code such as the following to be optimized. + ```csharp delegate double Function(double x); @@ -772,6 +809,7 @@ class Test } } ``` + Since the two anonymous function delegates have the same (empty) set of captured outer variables, and since the anonymous functions are semantically identical, the compiler is permitted to have the delegates refer to the same target method. Indeed, the compiler is permitted to return the very same delegate instance from both anonymous function expressions. ### 10.7.3 Evaluation of lambda expression conversions to expression tree types @@ -804,6 +842,7 @@ The compile-time application of the conversion from a method group `E` to a del - The result of the conversion is a value of type `D`, namely a delegate that refers to the selected method and target object. > *Example*: The following demonstrates method group conversions: +> > ```csharp > delegate string D1(object o); > delegate object D2(string s); @@ -824,6 +863,7 @@ The compile-time application of the conversion from a method group `E` to a del > } > } > ``` +> > The assignment to `d1` implicitly converts the method group `F` to a value of type `D1`. > > The assignment to `d2` shows how it is possible to create a delegate to a method that has less derived (contravariant) parameter types and a more derived (covariant) return type. @@ -838,18 +878,23 @@ The compile-time application of the conversion from a method group `E` to a del As with all other implicit and explicit conversions, the cast operator can be used to explicitly perform a particular conversion. > *Example*: Thus, the example +> > ```csharp > object obj = new EventHandler(myDialog.OkClick); > ``` +> > could instead be written +> > ```csharp > object obj = (EventHandler)myDialog.OkClick; > ``` +> > *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. > *Example*: +> > ```csharp > delegate int D(string s, int i); > delegate int E(); @@ -868,6 +913,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. diff --git a/standard/delegates.md b/standard/delegates.md index 87de60b4f..54b1dfeba 100644 --- a/standard/delegates.md +++ b/standard/delegates.md @@ -56,10 +56,12 @@ Furthermore, each class type constraint, interface type constraint and type para Delegate types in C# are name equivalent, not structurally equivalent. > *Example*: +> > ```csharp > delegate int D1(int i, double d); > delegate int D2(int c, double d); > ``` +> > The delegate types `D1` and `D2` are two different types, so they are not interchangeable, despite their identical signatures. *end example* 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. @@ -86,6 +88,7 @@ A method or delegate type `M` is ***compatible*** with a delegate type `D` if al This definition of consistency allows covariance in return type and contravariance in parameter types. > *Example*: +> > ```csharp > delegate int D1(int i, double d); > delegate int D2(int c, double d); @@ -106,11 +109,13 @@ This definition of consistency allows covariance in return type and contravarian > public static int[] M6(object o) {...} > } > ``` +> > The methods `A.M1` and `B.M1` are compatible with both the delegate types `D1` and `D2`, since they have the same return type and parameter list. The methods `B.M2`, `B.M3`, and `B.M4` are incompatible with the delegate types `D1` and `D2`, since they have different return types or parameter lists. The methods `B.M5` and `B.M6` are both compatible with delegate type `D3`. *end example* > *Example*: +> > ```csharp > delegate bool Predicate(T value); > @@ -120,11 +125,13 @@ This definition of consistency allows covariance in return type and contravarian > static bool G(string s) {...} > } > ``` +> > The method `X.F` is compatible with the delegate type `Predicate` and the method `X.G` is compatible with the delegate type `Predicate`. *end example* > *Note*: The intuitive meaning of delegate compatibility is that a method is compatible with a delegate type if every invocation of the delegate could be replaced with an invocation of the method without violating type safety, treating optional parameters and parameter arrays as explicit parameters. For example, in the following code: +> > ```csharp > delegate void Action(T arg); > @@ -139,6 +146,7 @@ This definition of consistency allows covariance in return type and contravarian > } > } > ``` +> > The `Print` method is compatible with the `Action` delegate type because any invocation of an `Action` delegate would also be a valid invocation of the `Print` method. > > If the signature of the `Print` method above were changed to `Print(object value, bool prependTimestamp = false)` for example, the `Print` method would no longer be compatible with `Action` by the rules of this clause. *end note* @@ -152,6 +160,7 @@ An instance of a delegate is created by a *delegate_creation_expression* ([§11. - Another delegate ([§11.7.15.6](expressions.md#117156-delegate-creation-expressions)). > *Example*: +> > ```csharp > delegate void D(int x); > @@ -172,6 +181,7 @@ An instance of a delegate is created by a *delegate_creation_expression* ([§11. > } > } > ``` +> > *end example* 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. @@ -181,6 +191,7 @@ When a new delegate is created from a single delegate the resultant invocation l Delegates are combined using the binary `+` ([§11.9.5](expressions.md#1195-addition-operator)) and `+=` operators ([§11.18.3](expressions.md#11183-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.18.3](expressions.md#11183-compound-assignment)). Delegates can be compared for equality ([§11.11.9](expressions.md#11119-delegate-equality-operators)). > *Example*: The following example shows the instantiation of a number of delegates, and their corresponding invocation lists: +> > ```csharp > delegate void D(int x); > @@ -208,6 +219,7 @@ Delegates are combined using the binary `+` ([§11.9.5](expressions.md#1195-add > } > } > ``` +> > When `cd1` and `cd2` are instantiated, they each encapsulate one method. When `cd3` is instantiated, it has an invocation list of two methods, `M1` and `M2`, in that order. `cd4`’s invocation list contains `M1`, `M2`, and `M1`, in that order. For `cd5`, the invocation list contains `M1`, `M2`, `M1`, `M1`, and `M2`, in that order. > > When `cd1` and `cd2` are instantiated, they each encapsulate one method. When `cd3` is instantiated, it has an invocation list of two methods, `M1` and `M2`, in that order. `cd4`s invocation list contains `M1`, `M2`, and `M1`, in that order. For `cd5` the invocation list contains `M1`, `M2`, `M1`, `M1`, and `M2`, in that order. @@ -275,11 +287,13 @@ Attempting to invoke a delegate instance whose value is `null` results in an exc > } > } > ``` +> > As shown in the statement `cd3 += cd1;`, a delegate can be present in an invocation list multiple times. In this case, it is simply invoked once per occurrence. In an invocation list such as this, when that delegate is removed, the last occurrence in the invocation list is the one actually removed. > > Immediately prior to the execution of the final statement, `cd3 -= cd1`;, the delegate `cd3` refers to an empty invocation list. Attempting to remove a delegate from an empty list (or to remove a non-existent delegate from a non-empty list) is not an error. > > The output produced is: +> > ```console > C.M1: -1 > C.M2: -2 @@ -300,4 +314,5 @@ Attempting to invoke a delegate instance whose value is `null` results in an exc > C.M1: 60 > C.M1: 60 > ``` +> > *end example* diff --git a/standard/enums.md b/standard/enums.md index 2d7458203..54aa74e70 100644 --- a/standard/enums.md +++ b/standard/enums.md @@ -14,6 +14,7 @@ An ***enum type*** is a distinct value type ([§8.3](types.md#83-value-types)) t > Blue > } > ``` +> > declares an enum type named `Color` with members `Red`, `Green`, and `Blue`. *end example* ## 18.2 Enum declarations @@ -56,6 +57,7 @@ An enum declaration that does not explicitly declare an underlying type has an u > Blue > } > ``` +> > declares an enum with an underlying type of `long`. *end example* @@ -105,6 +107,7 @@ enum_member_declaration Each enum member has an associated constant value. The type of this value is the underlying type for the containing enum. The constant value for each enum member shall be in the range of the underlying type for the enum. > *Example*: The example +> > ```csharp > enum Color: uint > { @@ -113,11 +116,13 @@ Each enum member has an associated constant value. The type of this value is the > Blue = -3 > } > ``` +> > results in a compile-time error because the constant values `-1`, `-2`, and `-3` are not in the range of the underlying integral type `uint`. *end example* Multiple enum members may share the same associated value. > *Example*: The example +> > ```csharp > enum Color > { @@ -127,6 +132,7 @@ Multiple enum members may share the same associated value. > Max = Blue > } > ``` +> > shows an enum in which two enum members—`Blue` and `Max`—have the same associated value. *end example* The associated value of an enum member is assigned either implicitly or explicitly. If the declaration of the enum member has a *constant_expression* initializer, the value of that constant expression, implicitly converted to the underlying type of the enum, is the associated value of the enum member. If the declaration of the enum member has no initializer, its associated value is set implicitly, as follows: @@ -135,6 +141,7 @@ The associated value of an enum member is assigned either implicitly or explicit - Otherwise, the associated value of the enum member is obtained by increasing the associated value of the textually preceding enum member by one. This increased value shall be within the range of values that can be represented by the underlying type, otherwise a compile-time error occurs. > *Example*: The example +> > ```csharp > using System; > enum Color @@ -169,12 +176,15 @@ The associated value of an enum member is assigned either implicitly or explicit > } > } > ``` +> > prints out the enum member names and their associated values. The output is: +> > ```console > Red = 0 > Green = 10 > Blue = 11 > ``` +> > for the following reasons: > > - the enum member `Red` is automatically assigned the value zero (since it has no initializer and is the first enum member); @@ -184,6 +194,7 @@ The associated value of an enum member is assigned either implicitly or explicit The associated value of an enum member may not, directly or indirectly, use the value of its own associated enum member. Other than this circularity restriction, enum member initializers may freely refer to other enum member initializers, regardless of their textual position. Within an enum member initializer, values of other enum members are always treated as having the type of their underlying type, so that casts are not necessary when referring to other enum members. > *Example*: The example +> > ```csharp > enum Circular > { @@ -191,6 +202,7 @@ The associated value of an enum member may not, directly or indirectly, use the > B > } > ``` +> > results in a compile-time error because the declarations of `A` and `B` are circular. `A` depends on `B` explicitly, and `B` depends on `A` implicitly. *end example* 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. diff --git a/standard/expressions.md b/standard/expressions.md index 19cd36f18..c5821bb6e 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -68,6 +68,7 @@ When no dynamic expressions are involved, C# defaults to static binding, which m 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. > *Example*: The following illustrates the notions of static and dynamic binding and of binding-time: +> > ```csharp > object o = 5; > dynamic d = 5; @@ -75,6 +76,7 @@ Static binding takes place at compile-time, whereas dynamic binding takes place > Console.WriteLine(o); // static binding to Console.WriteLine(object) > Console.WriteLine(d); // dynamic binding to Console.WriteLine(int) > ``` +> > The first two calls are statically bound: the overload of `Console.WriteLine` is picked based on the compile-time type of their argument. Thus, the binding-time is *compile-time*. > > The third call is dynamically bound: the overload of `Console.WriteLine` is picked based on the run-time type of its argument. This happens because the argument is a dynamic expression – its compile-time type is dynamic. Thus, the binding-time for the third call is *run-time*. *end example* @@ -313,13 +315,17 @@ Binary numeric promotion occurs for the operands of the predefined `+`, `–`, ` In both of the above cases, a cast expression can be used to explicitly convert one operand to a type that is compatible with the other operand. > *Example*: In the following code +> > ```csharp > decimal AddPercent(decimal x, double percent) => x * (1.0 + percent / 100.0); > ``` +> > a binding-time error occurs because a `decimal` cannot be multiplied by a `double`. The error is resolved by explicitly converting the second operand to `decimal`, as follows: +> > ```csharp > decimal AddPercent(decimal x, double percent) => x * (decimal)(1.0 + percent / 100.0); > ``` +> > *end example* **End of informative text.** @@ -536,6 +542,7 @@ argument_value | 'out' variable_reference ; ``` + An *argument_list* consists of one or more *argument*s, separated by commas. Each argument consists of an optional *argument_name* followed by an *argument_value*. An *argument* with an *argument_name* is referred to as a ***named argument***, whereas an *argument* without an *argument_name* is a ***positional argument***. It is an error for a positional argument to appear after a named argument in an *argument_list*. The *argument_value* can take one of the following forms: @@ -586,6 +593,7 @@ Methods, indexers, and instance constructors may declare their right-most parame The expressions of an argument list are always evaluated in textual order. > *Example*: Thus, the example +> > ```csharp > class Test > { @@ -600,16 +608,20 @@ The expressions of an argument list are always evaluated in textual order. > } > } > ``` +> > produces the output +> > ```console > x = 0, y = 1, z = 2 > x = 4, y = -1, z = 3 > ``` +> > *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. > *Example*: In the following code +> > ```csharp > class Test > { @@ -624,24 +636,31 @@ The array co-variance rules ([§16.6](arrays.md#166-array-covariance)) permit a > } > } > ``` +> > the second invocation of `F` causes a `System.ArrayTypeMismatchException` to be thrown because the actual element type of `b` is `string` and not `object`. *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. > *Example*: Given the declaration +> > ```csharp > void F(int x, int y, params object[] args); > ``` +> > the following invocations of the expanded form of the method +> > ```csharp > F(10, 20, 30, 40); > F(10, 20, 1, "hello", 3.0); > ``` +> > correspond exactly to +> > ```csharp > F(10, 20, new object[] { 30, 40 }); > F(10, 20, new object[] { 1, "hello", 3.0 }); > ``` +> > *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. @@ -655,6 +674,7 @@ When arguments are omitted from a function member with corresponding optional pa 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. > *Example*: Given the method declaration: +> > ```csharp > class Chooser > { @@ -663,11 +683,14 @@ When a generic method is called without specifying type arguments, a ***type inf > public static T Choose(T first, T second) => rand.Next(2) == 0 ? first : second; > } > ``` +> > it is possible to invoke the `Choose` method without explicitly specifying a type argument: +> > ```csharp > int i = Chooser.Choose(5, 213); // Calls Choose > string s = Chooser.Choose("apple", "banana"); // Calls Choose > ``` +> > Through type inference, the type arguments `int` and `string` are determined from the arguments to the method. *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. @@ -815,6 +838,7 @@ The ***inferred return type*** is determined as follows: - Otherwise, a return type cannot be inferred for `F`. > *Example*: As an example of type inference involving anonymous functions, consider the `Select` extension method declared in the `System.Linq.Enumerable` class: +> > ```csharp > namespace System.Linq > { @@ -832,32 +856,43 @@ The ***inferred return type*** is determined as follows: > } > } > ``` +> > Assuming the `System.Linq` namespace was imported with a `using namespace` directive, and given a class `Customer` with a `Name` property of type `string`, the `Select` method can be used to select the names of a list of customers: +> > ```csharp > List customers = GetCustomerList(); > 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: +> > ```csharp > IEnumerable names = Enumerable.Select(customers, c => c.Name); > ``` +> > Since type arguments were not explicitly specified, type inference is used to infer the type arguments. First, the customers argument is related to the source parameter, inferring `TSource` to be `Customer`. Then, using the anonymous function type inference process described above, `c` is given type `Customer`, and the expression `c.Name` is related to the return type of the selector parameter, inferring `TResult` to be `string`. Thus, the invocation is equivalent to +> > ```csharp > Sequence.Select(customers, (Customer c) => c.Name) > ``` +> > and the result is of type `IEnumerable`. > > The following example demonstrates how anonymous function type inference allows type information to “flow” between arguments in a generic method invocation. Given the method: +> > ```csharp > static Z F(X value, Func f1, Func f2) > { > return f2(f1(value)); > } > ``` +> > Type inference for the invocation: +> > ```csharp > double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds); > ``` +> > 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`. *end example* #### 11.6.3.14 Type inference for conversion of method groups @@ -992,6 +1027,7 @@ Given an expression `E` and two types `T₁` and `T₂`, `T₁` is a ***better c > *Example*: The following examples show overloads that are valid and invalid according to this rule: +> > ```csharp > interface I1 {...} > interface I2 {...} @@ -1016,6 +1052,7 @@ Given an expression `E` and two types `T₁` and `T₂`, `T₁` is a ***better c > void F6(out V v); > } > ``` +> > *end example* ### 11.6.5 Compile-time checking of dynamic member invocation @@ -1418,6 +1455,7 @@ predefined_type | 'object' | 'sbyte' | 'short' | 'string' | 'uint' | 'ulong' | 'ushort' ; ``` + The *qualified_alias_member* production is defined in [§13.8](namespaces.md#138-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. @@ -1488,6 +1526,7 @@ In a member access of the form `E.I`, if `E` is a single identifier, and if the > } > } > ``` +> > Within the `A` class, those occurrences of the Color identifier that reference the Color type are delimited by `**`, and those that reference the Color field are not. *end example* ### 11.7.7 Null Conditional Member Access @@ -1516,20 +1555,26 @@ A *null_conditional_member_access* expression `E` is of the form `P?.A`. Let `T - 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: + ```csharp ((object)P == null) ? (T?)null : P.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) ? null : P.A ``` + Except that `P` is evaluated only once. > *Note*: In an expression of the form: +> > ```csharp > 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. *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)). @@ -1635,6 +1680,7 @@ Using `C` as a target, the method call is then processed as a static method invo The preceding rules mean that instance methods take precedence over extension methods, that extension methods available in inner namespace declarations take precedence over extension methods available in outer namespace declarations, and that extension methods declared directly in a namespace take precedence over extension methods imported into that same namespace with a using namespace directive. > *Example*: +> > ```csharp > public static class E > { @@ -1667,7 +1713,9 @@ The preceding rules mean that instance methods take precedence over extension me > } > } > ``` +> > In the example, `B`’s method takes precedence over the first extension method, and `C`’s method takes precedence over both extension methods. +> > ```csharp > public static class C > { @@ -1707,11 +1755,13 @@ The preceding rules mean that instance methods take precedence over extension me > ``` > > The output of this example is: +> > ```console > E.F(1) > D.G(2) > C.H(3) > ``` +> > `D.G` takes precendece over `C.G`, and `E.F` takes precedence over both `D.F` and `C.F`. *end example* #### 11.7.8.4 Delegate invocations @@ -1745,22 +1795,28 @@ null_conditional_invocation_expression A *null_conditional_invocation_expression* expression `E` is of the form `P?A`; where `A` is the remainder of the syntactically equivalent *null_conditional_member_access* or *null_conditional_element_access*, `A` will therefore start with `.` or `[`. Let `PA` signify the concatention of `P` and `A`. When `E` occurs as a *statement_expression* the meaning of `E` is the same as the meaning of the *statement*: + ```csharp if ((object)P != null) PA ``` + except that `P` is evaluated only once. When `E` occurs as a *anonymous_function_body* or *method_body* the meaning of `E` depends on its classification: - If `E` is classified as nothing then its meaning is the same as the meaning of the *block*: + ```csharp { if ((object)P != null) PA; } ``` + except that `P` is evaluated only once. - Otherwise the meaning of `E` is the same as the meaning of the *block*: + ```csharp { 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)). ### 11.7.10 Element access @@ -1833,20 +1889,26 @@ A *null_conditional_element_access* expression `E` is of the form `P?[A]B`; wher - 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: + ```csharp ((object)P == null) ? (T?)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[A]B ``` + Except that `P` is evaluated only once. > *Note*: In an expression of the form: +> > ```csharp > 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. *end note* ### 11.7.12 This access @@ -1963,6 +2025,7 @@ object_or_collection_initializer | 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 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*. @@ -2042,6 +2105,7 @@ A member initializer that specifies a collection initializer after the equals si 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. > *Example*: The following class represents a point with two coordinates: +> > ```csharp > public class Point > { @@ -2049,18 +2113,24 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > public int Y { get; set; } > } > ``` +> > An instance of `Point` can be created and initialized as follows: +> > ```csharp > Point a = new Point { X = 0, Y = 1 }; > ``` +> > which has the same effect as +> > ```csharp > Point __a = new Point(); > __a.X = 0; > __a.Y = 1; > Point a = __a; > ``` +> > where `__a` is an otherwise invisible and inaccessible temporary variable. The following class represents a rectangle created from two points: +> > ```csharp > public class Rectangle > { @@ -2068,7 +2138,9 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > public Point P2 { get; set; } > } > ``` +> > An instance of `Rectangle` can be created and initialized as follows: +> > ```csharp > Rectangle r = new Rectangle > { @@ -2076,7 +2148,9 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > P2 = new Point { X = 2, Y = 3 } > }; > ``` +> > which has the same effect as +> > ```csharp > Rectangle __r = new Rectangle(); > Point __p1 = new Point(); @@ -2089,9 +2163,11 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > __r.P2 = __p2; > Rectangle r = __r; > ``` +> > where `__r`, `__p1` and `__p2` are temporary variables that are otherwise invisible and inaccessible. > > If `Rectangle`’s constructor allocates the two embedded `Point` instances +> > ```csharp > public class Rectangle > { @@ -2099,7 +2175,9 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > public Point P2 { get; } = new Point(); > } > ``` +> > the following construct can be used to initialize the embedded `Point` instances instead of assigning new instances: +> > ```csharp > Rectangle r = new Rectangle > { @@ -2107,7 +2185,9 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > P2 = { X = 2, Y = 3 } > }; > ``` +> > which has the same effect as +> > ```csharp > Rectangle __r = new Rectangle(); > __r.P1.X = 0; @@ -2116,6 +2196,7 @@ When an initializer target refers to an indexer, the arguments to the indexer sh > __r.P2.Y = 3; > Rectangle r = __r; > ``` +> > *end example* #### 11.7.15.4 Collection initializers @@ -2147,14 +2228,17 @@ A collection initializer consists of a sequence of element initializers, enclose > *Example*: > The following is an example of an object creation expression that includes a collection initializer: +> > ```csharp > List digits = new List { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; > ``` +> > *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. > *Example*:The following class represents a contact with a name and a list of phone numbers: +> > ```csharp > public class Contact > { @@ -2162,7 +2246,9 @@ The collection object to which a collection initializer is applied shall be of a > public List PhoneNumbers { get; } = new List(); > } > ``` +> > A `List` can be created and initialized as follows: +> > ```csharp > var contacts = new List > { @@ -2178,7 +2264,9 @@ The collection object to which a collection initializer is applied shall be of a > } > }; > ``` +> > which has the same effect as +> > ```csharp > var __clist = new List(); > Contact __c1 = new Contact(); @@ -2192,6 +2280,7 @@ The collection object to which a collection initializer is applied shall be of a > __clist.Add(__c2); > var contacts = __clist; > ``` +> > where `__clist`, `__c1` and `__c2` are temporary variables that are otherwise invisible and inaccessible. *end example* #### 11.7.15.5 Array creation expressions @@ -2221,6 +2310,7 @@ In an array creation expression of the second or third form, the rank of the spe ```csharp new int[,] {{0, 1}, {2, 3}, {4, 5}} ``` + exactly corresponds to ```csharp @@ -2242,14 +2332,19 @@ The result of evaluating an array creation expression is classified as a value, An array creation expression permits instantiation of an array with elements of an array type, but the elements of such an array shall be manually initialized. > *Example*: The statement +> > ```csharp > int[][] a = new int[100][]; > ``` +> > creates a single-dimensional array with 100 elements of type `int[]`. The initial value of each element is `null`. It is not possible for the same array creation expression to also instantiate the sub-arrays, and the statement +> > ```csharp > int[][] a = new int[100][5]; // Error > ``` +> > results in a compile-time error. Instantiation of the sub-arrays can instead be performed manually, as in +> > ```csharp > int[][] a = new int[100][]; > for (int i = 0; i < 100; i++) @@ -2257,30 +2352,36 @@ An array creation expression permits instantiation of an array with elements of > a[i] = new int[5]; > } > ``` +> > *end example* > *Note*: When an array of arrays has a “rectangular” shape, that is when the sub-arrays are all of the same length, it is more efficient to use a multi-dimensional array. In the example above, instantiation of the array of arrays creates 101 objects—one outer array and 100 sub-arrays. In contrast, +> > ```csharp > int[,] = new int[100, 5]; > ``` +> > creates only a single object, a two-dimensional array, and accomplishes the allocation in a single statement. *end note* > *Example*: The following are examples of implicitly typed array creation expressions: +> > ```csharp > var a = new[] { 1, 10, 100, 1000 }; // int[] > var b = new[] { 1, 1.5, 2, 2.5 }; // double[] > var c = new[,] { { "hello", null }, { "world", "!" } }; // string[,] > var d = new[] { 1, "one", 2, "two" }; // Error > ``` +> > The last expression causes a compile-time error because neither `int` nor `string` is implicitly convertible to the other, and so there is no best common type. An explicitly typed array creation expression must be used in this case, for example specifying the type to be `object[]`. Alternatively, one of the elements can be cast to a common base type, which would then become the inferred element type. *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. > *Example*: +> > ```csharp > var contacts = new[] > { @@ -2296,6 +2397,7 @@ Implicitly typed array creation expressions can be combined with anonymous objec > } > }; > ``` +> > *end example* #### 11.7.15.6 Delegate creation expressions @@ -2336,6 +2438,7 @@ The invocation list of a delegate is determined when the delegate is instantiate It is not possible to create a delegate that refers to a property, indexer, user-defined operator, instance constructor, finalizer, or static constructor. > *Example*: As described above, when a delegate is created from a method group, the formal parameter list and return type of the delegate determine which of the overloaded methods to select. In the example +> > ```csharp > delegate double DoubleFunc(double x); > @@ -2347,6 +2450,7 @@ It is not possible to create a delegate that refers to a property, indexer, user > static double Square(double x) => x * x; > } > ``` +> > the `A.f` field is initialized with a delegate that refers to the second `Square` method because that method exactly matches the formal parameter list and return type of `DoubleFunc`. Had the second `Square` method not been present, a compile-time error would have occurred. *end example* #### 11.7.15.7 Anonymous object creation expressions @@ -2414,11 +2518,13 @@ The names of an anonymous type and of the parameter to its `Equals` method are a Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and compile-time types in the same order will produce instances of the same anonymous type. > *Example*: In the example +> > ```csharp > var p1 = new { Name = "Lawnmower", Price = 495.00 }; > var p2 = new { Name = "Shovel", Price = 26.95 }; > p1 = p2; > ``` +> > the assignment on the last line is permitted because `p1` and `p2` are of the same anonymous type. *end example* 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. @@ -2481,6 +2587,7 @@ The third form of *typeof_expression* consists of a `typeof` keyword followed by 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. > *Example*: The example +> > ```csharp > using System; > class X @@ -2514,7 +2621,9 @@ The `typeof` operator can be used on a type parameter. The result is the `System > } > } > ``` +> > produces the following output: +> > ```console > System.Int32 > System.Int32 @@ -2526,6 +2635,7 @@ The `typeof` operator can be used on a type parameter. The result is the `System > X`1[X`1[System.Int32]] > X`1[T] > ``` +> > Note that `int` and `System.Int32` are the same type. > The result of `typeof(X<>)` does not depend on the type argument but the result of `typeof(X)` does. *end example* @@ -2596,6 +2706,7 @@ For constant expressions ([§11.20](expressions.md#1120-constant-expressions)) ( The body of an anonymous function is not affected by `checked` or `unchecked` contexts in which the anonymous function occurs. > *Example*: In the following code +> > ```csharp > class Test > { @@ -2607,11 +2718,13 @@ The body of an anonymous function is not affected by `checked` or `unchecked` co > static int H() => x * y; // Depends on default > } > ``` +> > no compile-time errors are reported since neither of the expressions can be evaluated at compile-time. At run-time, the `F` method throws a `System.OverflowException`, and the `G` method returns –727379968 (the lower 32 bits of the out-of-range result). The behavior of the `H` method depends on the default overflow-checking context for the compilation, but it is either the same as `F` or the same as `G`. *end example* > *Example*: In the following code +> > ```csharp > class Test > { @@ -2623,11 +2736,13 @@ The body of an anonymous function is not affected by `checked` or `unchecked` co > static int H() => x * y; // Compile-time error, overflow > } > ``` +> > the overflows that occur when evaluating the constant expressions in `F` and `H` cause compile-time errors to be reported because the expressions are evaluated in a `checked` context. An overflow also occurs when evaluating the constant expression in `G`, but since the evaluation takes place in an `unchecked` context, the overflow is not reported. *end example* The `checked` and `unchecked` operators only affect the overflow checking context for those operations that are textually contained within the “`(`” and “`)`” tokens. The operators have no effect on function members that are invoked as a result of evaluating the contained expression. > *Example*: In the following code +> > ```csharp > class Test > { @@ -2636,11 +2751,13 @@ The `checked` and `unchecked` operators only affect the overflow checking contex > static int F() => checked(Multiply(1000000, 1000000)); > } > ``` +> > the use of `checked` in F does not affect the evaluation of `x * y` in `Multiply`, so `x * y` is evaluated in the default overflow checking context. *end example* The `unchecked` operator is convenient when writing constants of the signed integral types in hexadecimal notation. > *Example*: +> > ```csharp > class Test > { @@ -2648,6 +2765,7 @@ The `unchecked` operator is convenient when writing constants of the signed inte > public const int HighBit = unchecked((int)0x80000000); > } > ``` +> > Both of the hexadecimal constants above are of type `uint`. Because the constants are outside the `int` range, without the `unchecked` operator, the casts to `int` would produce compile-time errors. *end example* @@ -2705,6 +2823,7 @@ A *nameof_expression* is a constant expression of type `string`, and has no effe These are the same transformations applied in [§6.4.3](lexical-structure.md#643-identifiers) when testing equality between identifiers. > *Example*: The following illustrates the results of various `nameof` expressions, assuming a generic type `List` declared within the `System.Collections.Generic` namespace: +> > ```csharp > using System.Collections.Generic; > @@ -2749,6 +2868,7 @@ These are the same transformations applied in [§6.4.3](lexical-structure.md#643 > class NestedClass { } > } > ``` +> > 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* @@ -2805,22 +2925,27 @@ Lifted ([§11.4.8](expressions.md#1148-lifted-operators)) forms of the unlifted 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: - Integer negation: + ```csharp int operator –(int x); long operator –(long x); ``` + The result is computed by subtracting `X` from zero. If the value of `X` is the smallest representable value of the operand type (−2³¹ for `int` or −2⁶³ for `long`), then the mathematical negation of `X` is not representable within the operand type. If this occurs within a `checked` context, a `System.OverflowException` is thrown; if it occurs within an `unchecked` context, the result is the value of the operand and the overflow is not reported. If the operand of the negation operator is of type `uint`, it is converted to type `long`, and the type of the result is `long`. An exception is the rule that permits the `int` value `−2147483648` (−2³¹) to be written as a decimal integer literal ([§6.4.5.3](lexical-structure.md#6453-integer-literals)). If the operand of the negation operator is of type `ulong`, a compile-time error occurs. An exception is the rule that permits the `long` value `−9223372036854775808` (−2⁶³) to be written as a decimal integer literal ([§6.4.5.3](lexical-structure.md#6453-integer-literals)) - Floating-point negation: + ```csharp float operator –(float x); double operator –(double x); ``` + The result is the value of `X` with its sign inverted. If `x` is `NaN`, the result is also `NaN`. - Decimal negation: + ```csharp decimal operator –(decimal x); ``` @@ -2851,6 +2976,7 @@ uint operator ~(uint x); long operator ~(long x); ulong operator ~(ulong x); ``` + For each of these operators, the result of the operation is the bitwise complement of `x`. Every enumeration type `E` implicitly provides the following bitwise complement operator: @@ -3025,18 +3151,22 @@ For an operation of the form `x * y`, binary operator overload resolution ([§ The predefined multiplication operators are listed below. The operators all compute the product of `x` and `y`. - Integer multiplication: + ```csharp int operator *(int x, int y); uint operator *(uint x, uint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y); ``` + In a `checked` context, if the product is outside the range of the result type, a `System.OverflowException` is thrown. In an `unchecked` context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded. - Floating-point multiplication: + ```csharp float operator *(float x, float y); double operator *(double x, double y); ``` + The product is computed according to the rules of IEC 60559 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaNs. In the table, `x` and `y` are positive finite values. `z` is the result of `x * y`, rounded to the nearest representable value. If the magnitude of the result is too large for the destination type, `z` is infinity. Because of rounding, `z` may be zero even though neither `x` nor `y` is zero. @@ -3133,9 +3263,11 @@ The predefined multiplication operators are listed below. The operators all comp (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).) - Decimal multiplication: + ```csharp decimal operator *(decimal x, decimal y); ``` + 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`. @@ -3148,18 +3280,21 @@ For an operation of the form `x / y`, binary operator overload resolution ([§ The predefined division operators are listed below. The operators all compute the quotient of `x` and `y`. - Integer division: + ```csharp int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y); ``` + If the value of the right operand is zero, a `System.DivideByZeroException` is thrown. The division rounds the result towards zero. Thus the absolute value of the result is the largest possible integer that is less than or equal to the absolute value of the quotient of the two operands. The result is zero or positive when the two operands have the same sign and zero or negative when the two operands have opposite signs. If the left operand is the smallest representable `int` or `long` value and the right operand is `–1`, an overflow occurs. In a `checked` context, this causes a `System.ArithmeticException` (or a subclass thereof) to be thrown. In an `unchecked` context, it is implementation-defined as to 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. - Floating-point division: + ```csharp float operator /(float x, float y); double operator /(double x, double y); @@ -3260,6 +3395,7 @@ The predefined division operators are listed below. The operators all compute th - Decimal division: + ```csharp decimal operator /(decimal x, decimal y); ``` @@ -3277,6 +3413,7 @@ For an operation of the form `x % y`, binary operator overload resolution ([§ The predefined remainder operators are listed below. The operators all compute the remainder of the division between `x` and `y`. - Integer remainder: + ```csharp int operator %(int x, int y); uint operator %(uint x, uint y); @@ -3288,6 +3425,7 @@ The predefined remainder operators are listed below. The operators all compute t If the left operand is the smallest `int` or `long` value and the right operand is `–1`, a `System.OverflowException` is thrown if and only if `x / y` would throw an exception. - Floating-point remainder: + ```csharp float operator %(float x, float y); double operator %(double x, double y); @@ -3387,9 +3525,11 @@ The predefined remainder operators are listed below. The operators all compute t - Decimal remainder: + ```csharp decimal operator %(decimal x, decimal y); ``` + If the value of the right operand is zero, a `System.DivideByZeroException` is thrown. It is implementation-defined when a `System.ArithmeticException` (or a subclass thereof) is thrown. A conforming implementation shall not throw an exception for `x % y` in any case where `x / y` does not throw an exception. The scale of the result, before any rounding, is the larger of the scales of the two operands, and the sign of the result, if non-zero, is the same as that of `x`. Decimal remainder is equivalent to using the remainder operator of type `System.Decimal`. @@ -3404,6 +3544,7 @@ For an operation of the form `x + y`, binary operator overload resolution ([§ 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. - Integer addition: + ```csharp int operator +(int x, int y); uint operator +(uint x, uint y); @@ -3414,6 +3555,7 @@ The predefined addition operators are listed below. For numeric and enumeration In a `checked` context, if the sum is outside the range of the result type, a `System.OverflowException` is thrown. In an `unchecked` context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded. - Floating-point addition: + ```csharp float operator +(float x, float y); double operator +(double x, double y); @@ -3496,6 +3638,7 @@ The predefined addition operators are listed below. For numeric and enumeration - Decimal addition: + ```csharp decimal operator +(decimal x, decimal y); ``` @@ -3504,6 +3647,7 @@ The predefined addition operators are listed below. For numeric and enumeration Decimal addition is equivalent to using the addition operator of type `System.Decimal`. - Enumeration addition. Every enumeration type implicitly provides the following predefined operators, where `E` is the enum type, and `U` is the underlying type of `E`: + ```csharp E operator +(E x, U y); E operator +(U x, E y); @@ -3511,6 +3655,7 @@ The predefined addition operators are listed below. For numeric and enumeration At run-time these operators are evaluated exactly as `(E)((U)x + (U)y`). - String concatenation: + ```csharp string operator +(string x, string y); string operator +(string x, object y); @@ -3520,6 +3665,7 @@ The predefined addition operators are listed below. For numeric and enumeration These overloads of the binary `+` operator perform string concatenation. If an operand of string concatenation is `null`, an empty string is substituted. Otherwise, any non-`string` operand is converted to its string representation by invoking the virtual `ToString` method inherited from type `object`. If `ToString` returns `null`, an empty string is substituted. > *Example*: + > > ```csharp > using System; > class Test @@ -3540,13 +3686,16 @@ The predefined addition operators are listed below. For numeric and enumeration > } > } > ``` + > > The output shown in the comments is the typical result on a US-English system. The precise output might depend on the regional settings of the execution environment. The string-concatenation operator itself behaves the same way in each case, but the `ToString` methods implicitly called during execution might be affected by regional settings. *end example* The result of the string concatenation operator is a `string` that consists of the characters of the left operand followed by the characters of the right operand. The string concatenation operator never returns a `null` value. A `System.OutOfMemoryException` may be thrown if there is not enough memory available to allocate the resulting string. - Delegate combination. Every delegate type implicitly provides the following predefined operator, where `D` is the delegate type: + ```csharp D operator +(D x, D y); ``` + 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* @@ -3560,6 +3709,7 @@ For an operation of the form `x – y`, binary operator overload resolution ([ The predefined subtraction operators are listed below. The operators all subtract `y` from `x`. - Integer subtraction: + ```csharp int operator –(int x, int y); uint operator –(uint x, uint y); @@ -3569,10 +3719,12 @@ The predefined subtraction operators are listed below. The operators all subtrac In a `checked` context, if the difference is outside the range of the result type, a `System.OverflowException` is thrown. In an `unchecked` context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded. - Floating-point subtraction: + ```csharp float operator –(float x, float y); double operator –(double x, double y); ``` + The difference is computed according to the rules of IEC 60559 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaNs. In the table, `x` and `y` are nonzero finite values, and `z` is the result of `x – y`. If `x` and `y` are equal, `z` is positive zero. If `x – y` is too large to represent in the destination type, `z` is an infinity with the same sign as `x – y`. @@ -3651,6 +3803,7 @@ The predefined subtraction operators are listed below. The operators all subtrac (In the above table the `-y` entries denote the *negation* of `y`, not that the value is negative.) - Decimal subtraction: + ```csharp decimal operator –(decimal x, decimal y); ``` @@ -3660,9 +3813,11 @@ The predefined subtraction operators are listed below. The operators all subtrac Decimal subtraction is equivalent to using the subtraction operator of type `System.Decimal`. - Enumeration subtraction. Every enumeration type implicitly provides the following predefined operator, where `E` is the enum type, and `U` is the underlying type of `E`: + ```csharp U operator –(E x, E y); ``` + This operator is evaluated exactly as `(U)((U)x – (U)y)`. In other words, the operator computes the difference between the ordinal values of `x` and `y`, and the type of the result is the underlying type of the enumeration. ```csharp @@ -3672,9 +3827,11 @@ The predefined subtraction operators are listed below. The operators all subtrac This operator is evaluated exactly as `(E)((U)x – y)`. In other words, the operator subtracts a value from the underlying type of the enumeration, yielding a value of the enumeration. - Delegate removal. Every delegate type implicitly provides the following predefined operator, where `D` is the delegate type: + ```csharp D operator –(D x, D y); ``` + 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. @@ -3686,7 +3843,9 @@ The predefined subtraction operators are listed below. The operators all subtrac Neither of the operands’ lists (if any) is changed in the process. > *Example*: + > > ```csharp + > > delegate void D(int x); > > class C @@ -3714,6 +3873,7 @@ 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. @@ -3739,6 +3899,7 @@ When declaring an overloaded shift operator, the type of the first operand shall The predefined shift operators are listed below. - Shift left: + ```csharp int operator <<(int x, int count); uint operator <<(uint x, int count); @@ -3750,12 +3911,14 @@ The predefined shift operators are listed below. The high-order bits outside the range of the result type of `x` are discarded, the remaining bits are shifted left, and the low-order empty bit positions are set to zero. - Shift right: + ```csharp int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count); ``` + The `>>` operator shifts `x` right by a number of bits computed as described below. When `x` is of type `int` or `long`, the low-order bits of `x` are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero if `x` is non-negative and set to one if `x` is negative. @@ -3891,9 +4054,11 @@ If either operand is NaN, the result is `false` for all operators except `!=`, > *Example*: If either of `x` and `y` is NaN, then `x` < `y` is `false`, but `!(x >= y)` is `true`. *end example* When neither operand is NaN, the operators compare the values of the two floating-point operands with respect to the ordering + ```csharp –∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞ ``` + where `min` and `max` are the smallest and largest positive finite values that can be represented in the given floating-point format. Notable effects of this ordering are: - Negative and positive zeros are considered equal. @@ -3986,6 +4151,7 @@ Unless one of these conditions is true, a binding-time error occurs. > *Example*: The following example checks whether an argument of an unconstrained type parameter type is `null`. +> > ```csharp > class C > { @@ -3999,6 +4165,7 @@ Unless one of these conditions is true, a binding-time error occurs. > } > } > ``` +> > The `x == null` construct is permitted even though `T` could represent a non-nullable value type, and the result is simply defined to be `false` when `T` is a non-nullable value type. *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. @@ -4008,6 +4175,7 @@ For an operation of the form `x == y` or `x != y`, if any applicable `operat > *Example*: The example +> > ```csharp > using System; > class Test @@ -4023,16 +4191,20 @@ For an operation of the form `x == y` or `x != y`, if any applicable `operat > } > } > ``` +> > produces the output +> > ```console > True > False > False > 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`. > > Note that the above technique is not meaningful for value types. The example +> > ```csharp > class Test > { @@ -4044,6 +4216,7 @@ For an operation of the form `x == y` or `x != y`, if any applicable `operat > } > } > ``` +> > outputs `False` because the casts create references to two separate instances of boxed `int` values. *end example* ### 11.11.8 String equality operators @@ -4166,6 +4339,7 @@ E is T ? (T)(object)(E) : (T)null Note that some conversions, such as user defined conversions, are not possible with the `as` operator and should instead be performed using cast expressions. > *Example*: In the example +> > ```csharp > class X > { @@ -4186,6 +4360,7 @@ Note that some conversions, such as user defined conversions, are not possible w > } > } > ``` +> > the type parameter `T` of `G` is known to be a reference type, because it has the class constraint. The type parameter `U` of `H` is not however; hence the use of the `as` operator in `H` is disallowed. *end example* ## 11.12 Logical operators @@ -4508,6 +4683,7 @@ The parameter list of an anonymous function in the form of an *anonymous_method_ A *block* body of an anonymous function is always reachable ([§12.2](statements.md#122-end-points-and-reachability)). > *Example*: Some examples of anonymous functions follow below: +> > ```csharp > x => x + 1 // Implicitly typed, expression body > x => { return x + 1; } // Implicitly typed, block body @@ -4519,6 +4695,7 @@ A *block* body of an anonymous function is always reachable ([§12.2](statements > delegate (int x) { return x + 1; } // Anonymous method expression > delegate { return 1 + 1; } // Parameter list omitted > ``` +> > *end example* The behavior of *lambda_expression*s and *anonymous_method_expression*s is the same except for the following points: @@ -4556,6 +4733,7 @@ It is explicitly unspecified whether there is any way to execute the block of an 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. > *Example*: The following example illustrates the effect of anonymous functions on overload resolution. +> > ```csharp > class ItemList : List > { @@ -4580,9 +4758,11 @@ Anonymous functions in an argument list participate in type inference and overlo > } > } > ``` +> > The `ItemList` class has two `Sum` methods. Each takes a `selector` argument, which extracts the value to sum over from a list item. The extracted value can be either an `int` or a `double` and the resulting sum is likewise either an `int` or a `double`. > > The `Sum` methods could for example be used to compute sums from a list of detail lines in an order. +> > ```csharp > class Detail > { @@ -4599,6 +4779,7 @@ Anonymous functions in an argument list participate in type inference and overlo > ... > } > ``` +> > In the first invocation of `orderDetails.Sum`, both `Sum` methods are applicable because the anonymous function `d => d.UnitCount` is compatible with both `Func` and `Func`. However, overload resolution picks the first `Sum` method because the conversion to `Func` is better than the conversion to `Func`. > > In the second invocation of `orderDetails.Sum`, only the second `Sum` method is applicable because the anonymous function `d => d.UnitPrice * d.UnitCount` produces a value of type `double`. Thus, overload resolution picks the second `Sum` method for that invocation. *end example* @@ -4618,6 +4799,7 @@ Any local variable, value parameter, or parameter array whose scope includes the 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. > *Example*: In the example +> > ```csharp > using System; > @@ -4641,12 +4823,15 @@ When an outer variable is referenced by an anonymous function, the outer variabl > } > } > ``` +> > the local variable `x` is captured by the anonymous function, and the lifetime of `x` is extended at least until the delegate returned from `F` becomes eligible for garbage collection. Since each invocation of the anonymous function operates on the same instance of `x`, the output of the example is: +> > ```csharp > 1 > 2 > 3 > ``` +> > *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. @@ -4658,6 +4843,7 @@ When a local variable or a value parameter is captured by an anonymous function, A local variable is considered to be ***instantiated*** when execution enters the scope of the variable. > *Example*: For example, when the following method is invoked, the local variable `x` is instantiated and initialized three times—once for each iteration of the loop. +> > ```csharp > static void F() > { @@ -4668,7 +4854,9 @@ A local variable is considered to be ***instantiated*** when execution enters th > } > } > ``` +> > However, moving the declaration of `x` outside the loop results in a single instantiation of `x`: +> > ```csharp > static void F() > { @@ -4680,11 +4868,13 @@ A local variable is considered to be ***instantiated*** when execution enters th > } > } > ``` +> > *end example* When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent. > *Example*: The example +> > ```csharp > using System; > delegate void D(); @@ -4710,13 +4900,17 @@ When not captured, there is no way to observe exactly how often a local variable > } > } > ``` +> > produces the output: +> > ```console > 1 > 3 > 5 > ``` +> > However, when the declaration of `x` is moved outside the loop: +> > ```csharp > static D[] F() > { @@ -4730,17 +4924,21 @@ When not captured, there is no way to observe exactly how often a local variable > return result; > } > ``` +> > the output is: +> > ```console > 5 > 5 > 5 > ``` +> > Note that the compiler is permitted (but not required) to optimize the three instantiations into a single delegate instance ([§10.7.2](conversions.md#1072-evaluation-of-anonymous-function-conversions-to-delegate-types)). *end example* If a for-loop declares an iteration variable, that variable itself is considered to be declared outside of the loop. > *Example*: Thus, if the example is changed to capture the iteration variable itself: +> > ```csharp > static D[] F() > { @@ -4752,17 +4950,21 @@ If a for-loop declares an iteration variable, that variable itself is considered > return result; > } > ``` +> > only one instance of the iteration variable is captured, which produces the output: +> > ```console > 3 > 3 > 3 > ``` +> > *end example* It is possible for anonymous function delegates to share some captured variables yet have separate instances of others. > *Example*: For example, if `F` is changed to +> > ```csharp > static D[] F() > { @@ -4776,17 +4978,21 @@ It is possible for anonymous function delegates to share some captured variables > return result; > } > ``` +> > the three delegates capture the same instance of `x` but separate instances of `y`, and the output is: +> > ```console > 1 1 > 2 1 > 3 1 > ``` +> > *end example* Separate anonymous functions can capture the same instance of an outer variable. > *Example*: In the example: +> > ```csharp > using System; > @@ -4807,11 +5013,14 @@ Separate anonymous functions can capture the same instance of an outer variable. > } > } > ``` +> > the two anonymous functions capture the same instance of the local variable `x`, and they can thus “communicate” through that variable. The output of the example is: +> > ```console > 5 > 10 > ``` +> > *end example* ### 11.16.7 Evaluation of anonymous function expressions @@ -4841,6 +5050,7 @@ class Test } } ``` + This can be translated to a delegate instantiation that references a compiler generated static method in which the code of the anonymous function is placed: ```csharp @@ -5113,24 +5323,30 @@ from «x» in ( from «y» in S group «y» by «y».Prop ) Q The translations in the following sections assume that queries have no into continuations. > *Example*: The example: +> > ```csharp > from c in customers > group c by c.Country into g > select new { Country = g.Key, CustCount = g.Count() } > ``` +> > is translated into: +> > ```csharp > from g in > (from c in customers > group c by c.Country) > select new { Country = g.Key, CustCount = g.Count() } > ``` +> > the final translation of which is: +> > ```csharp > customers. > GroupBy(c => c.Country). > Select(g => new { Country = g.Key, CustCount = g.Count() }) > ``` +> > *end example* #### 11.17.3.3 Explicit range variable types @@ -5162,23 +5378,29 @@ join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2» The translations in the following sections assume that queries have no explicit range variable types. > *Example*: The example +> > ```csharp > from Customer c in customers > where c.City == "London" > select c > ``` +> > is translated into +> > ```csharp > from c in (customers).Cast() > where c.City == "London" > select c > ``` +> > the final translation of which is +> > ```csharp > customers. > Cast(). > Where(c => c.City == "London") > ``` +> > *end example* @@ -5200,14 +5422,18 @@ is translated into ``` > *Example*: The example +> > ```csharp > from c in customers > select c > ``` +> > is translated into +> > ```csharp > (customers).Select(c => c) > ``` +> > *end example* A degenerate query expression is one that trivially selects the elements of the source. @@ -5231,18 +5457,22 @@ is translated into ``` > *Example*: The example +> > ```csharp > from c in customers > from o in c.Orders > select new { c.Name, o.OrderID, o.Total } > ``` +> > is translated into +> > ```csharp > (customers). > SelectMany(c => c.Orders, > (c,o) => new { c.Name, o.OrderID, o.Total } > ) > ``` +> > *end example* A query expression with a second `from` clause followed by a query body `Q` containing a non-empty set of query body clauses: @@ -5261,26 +5491,32 @@ Q ``` > *Example*: The example +> > ```csharp > from c in customers > from o in c.Orders > orderby o.Total descending > select new { c.Name, o.OrderID, o.Total } > ``` +> > is translated into +> > ```csharp > from * in (customers). > SelectMany(c => c.Orders, (c,o) => new { c, o }) > orderby o.Total descending > select new { c.Name, o.OrderID, o.Total } > ``` +> > the final translation of which is +> > ```csharp > customers. > SelectMany(c => c.Orders, (c,o) => new { c, o }). > OrderByDescending(x => x.o.Total). > Select(x => new { x.c.Name, x.o.OrderID, x.o.Total }) > ``` +> > where `x` is a compiler generated identifier that is otherwise invisible and inaccessible. *end example* A `let` expression along with its preceding `from` clause: @@ -5299,25 +5535,31 @@ from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } ) ``` > *Example*: The example +> > ```csharp > from o in orders > let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) > where t >= 1000 > select new { o.OrderID, Total = t } > ``` +> > is translated into +> > ```csharp > from * in (orders).Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) > where t >= 1000 > select new { o.OrderID, Total = t } > ``` +> > the final translation of which is +> > ```csharp > orders > .Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) > .Where(x => x.t >= 1000) > .Select(x => new { x.o.OrderID, Total = x.t }) > ``` +> > where `x` is a compiler generated identifier that is otherwise invisible and inaccessible. *end example* A `where` expression along with its preceding `from` clause: @@ -5350,18 +5592,22 @@ is translated into ``` > *Example*: The example +> > ```csharp > from c in customersh > join o in orders on c.CustomerID equals o.CustomerID > select new { c.Name, o.OrderDate, o.Total } > ``` +> > is translated into +> > ```csharp > (customers).Join( > orders, > c => c.CustomerID, o => o.CustomerID, > (c, o) => new { c.Name, o.OrderDate, o.Total }) > ``` +> > *end example* A `join` clause followed by a query body clause: @@ -5411,6 +5657,7 @@ from * in ( «e1» ) . GroupJoin( ``` > *Example*: The example +> > ```csharp > from c in customers > join o in orders on c.CustomerID equals o.CustomerID into co @@ -5418,7 +5665,9 @@ from * in ( «e1» ) . GroupJoin( > where n >= 10 > select new { c.Name, OrderCount = n } > ``` +> > is translated into +> > ```csharp > from * in (customers).GroupJoin( > orders, @@ -5429,7 +5678,9 @@ from * in ( «e1» ) . GroupJoin( > where n >= 10 > select new { c.Name, OrderCount = n } > ``` +> > the final translation of which is +> > ```csharp > customers > .GroupJoin( @@ -5441,6 +5692,7 @@ from * in ( «e1» ) . GroupJoin( > .Where(y => y.n >= 10) > .Select(y => new { y.x.c.Name, OrderCount = y.n }) > ``` +> > where `x` and `y` are compiler generated identifiers that are otherwise invisible and inaccessible. *end example* An `orderby` clause and its preceding `from` clause: @@ -5465,17 +5717,21 @@ ThenBy ( «x» => «kn» ) If an `ordering` clause specifies a descending direction indicator, an invocation of `OrderByDescending` or `ThenByDescending` is produced instead. > *Example*: The example +> > ```csharp > from o in orders > orderby o.Customer.Name, o.Total descending > select o > ``` +> > has the final translation +> > ```csharp > (orders) > .OrderBy(o => o.Customer.Name) > .ThenByDescending(o => o.Total) > ``` +> > *end example* 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. @@ -5501,14 +5757,18 @@ except when `«v»` is the identifier `«x»`, the translation is simply ``` > *Example*: The example +> > ```csharp > from c in customers.Where(c => c.City == "London") > select c > ``` +> > is simply translated into +> > ```csharp > (customers).Where(c => c.City == "London") > ``` +> > *end example* #### 11.17.3.7 Group clauses @@ -5532,14 +5792,18 @@ except when `«v»` is the identifier `«x»`, the translation is ``` > *Example*: The example +> > ```csharp > from c in customers > group c.Name by c.Country > ``` +> > is translated into +> > ```csharp > (customers).GroupBy(c => c.Country, c => c.Name) > ``` +> > *end example* #### 11.17.3.8 Transparent identifiers @@ -5555,35 +5819,44 @@ When a query translation injects a transparent identifier, further translation s In the translation steps described above, transparent identifiers are always introduced together with anonymous types, with the intent of capturing multiple range variables as members of a single object. An implementation of C# is permitted to use a different mechanism than anonymous types to group together multiple range variables. The following translation examples assume that anonymous types are used, and shows one possible translation of transparent identifiers. > *Example*: The example +> > ```csharp > from c in customers > from o in c.Orders > orderby o.Total descending > select new { c.Name, o.Total } > ``` +> > is translated into +> > ```csharp > from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o }) > orderby o.Total descending > select new { c.Name, o.Total } > ``` +> > which is further translated into +> > ```csharp > customers > .SelectMany(c => c.Orders, (c,o) => new { c, o }) > .OrderByDescending(* => o.Total) > .Select(\* => new { c.Name, o.Total }) > ``` +> > which, when transparent identifiers are erased, is equivalent to +> > ```csharp > customers > .SelectMany(c => c.Orders, (c,o) => new { c, o }) > .OrderByDescending(x => x.o.Total) > .Select(x => new { x.c.Name, x.o.Total }) > ``` +> > where `x` is a compiler generated identifier that is otherwise invisible and inaccessible. > > The example +> > ```csharp > from c in customers > join o in orders on c.CustomerID equals o.CustomerID @@ -5591,7 +5864,9 @@ In the translation steps described above, transparent identifiers are always int > join p in products on d.ProductID equals p.ProductID > select new { c.Name, o.OrderDate, p.ProductName } > ``` +> > is translated into +> > ```csharp > from * in (customers).Join( > orders, @@ -5602,7 +5877,9 @@ In the translation steps described above, transparent identifiers are always int > join p in products on d.ProductID equals p.ProductID > select new { c.Name, o.OrderDate, p.ProductName } > ``` +> > which is further reduced to +> > ```csharp > customers > .Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) @@ -5610,7 +5887,9 @@ In the translation steps described above, transparent identifiers are always int > .Join(products, * => d.ProductID, p => p.ProductID, > (*, p) => new { c.Name, o.OrderDate, p.ProductName }) > ``` +> > the final translation of which is +> > ```csharp > customers > .Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) @@ -5618,6 +5897,7 @@ In the translation steps described above, transparent identifiers are always int > .Join(products, y => y.d.ProductID, p => p.ProductID, > (y, p) => new { y.x.c.Name, y.x.o.OrderDate, p.ProductName }) > ``` +> > where `x` and `y` are compiler-generated identifiers that are otherwise invisible and inaccessible. > *end example* @@ -5661,6 +5941,7 @@ class G : C public K Key { get; } } ``` + The methods above use the generic delegate types `Func` and `Func`, but they could equally well have used other delegate or expression-tree types with the same relationships in parameter and return types. > *Note*: The recommended relationship between `C` and `O` that ensures that the `ThenBy` and `ThenByDescending` methods are available only on the result of an `OrderBy` or `OrderByDescending`. *end note* @@ -5735,6 +6016,7 @@ The run-time processing of a simple assignment of the form `x` = `y` consists > *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 +> > ```csharp > string[] sa = new string[10]; > object[] oa = sa; @@ -5742,6 +6024,7 @@ The run-time processing of a simple assignment of the form `x` = `y` consists > oa[1] = "Hello"; // OK > oa[2] = new ArrayList(); // ArrayTypeMismatchException > ``` +> > the last assignment causes a `System.ArrayTypeMismatchException` to be thrown because a reference to an `ArrayList` cannot be stored in an element of a `string[]`. *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. @@ -5751,6 +6034,7 @@ When a property or indexer declared in a *struct_type* is the target of an assig > *Example*: Given the declarations: +> > ```csharp > struct Point > { @@ -5797,7 +6081,9 @@ When a property or indexer declared in a *struct_type* is the target of an assig > } > } > ``` +> > in the example +> > ```csharp > Point p = new Point(); > p.X = 100; @@ -5806,7 +6092,9 @@ When a property or indexer declared in a *struct_type* is the target of an assig > r.A = new Point(10, 10); > r.B = p; > ``` +> > the assignments to `p.X`, `p.Y`, `r.A`, and `r.B` are permitted because `p` and `r` are variables. However, in the example +> > ```csharp > Rectangle r = new Rectangle(); > r.A.X = 10; @@ -5814,6 +6102,7 @@ When a property or indexer declared in a *struct_type* is the target of an assig > r.B.X = 100; > r.B.Y = 100; > ``` +> > the assignments are all invalid, since `r.A` and `r.B` are not variables. *end example* ### 11.18.3 Compound assignment @@ -5837,6 +6126,7 @@ The second rule above permits `x «op»= y` to be evaluated as `x = (T)(x  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. > *Example*: In the following code +> > ```csharp > byte b = 0; > char ch = '\0'; @@ -5848,6 +6138,7 @@ The intuitive effect of the rule for predefined operators is simply that `x «op > ch += 1; // Error, ch = 1 not permitted > ch += (char)1; // OK > ``` +> > the intuitive reason for each error is that a corresponding simple assignment would also have been an error. *end example* @@ -5922,6 +6213,7 @@ The following conversions are permitted in constant expressions: > *Example*: In the following code +> > ```csharp > class C > { @@ -5929,6 +6221,7 @@ The following conversions are permitted in constant expressions: > const object str = "hello"; // error: implicit reference conversion > } > ``` +> > the initialization of `i` is an error because a boxing conversion is required. The initialization of `str` is an error because an implicit reference conversion from a non-`null` value is required. *end example* Whenever an expression fulfills the requirements listed above, the expression is evaluated at compile-time. This is true even if the expression is a subexpression of a larger expression that contains non-constant constructs. diff --git a/standard/interfaces.md b/standard/interfaces.md index 2f9e3fa1c..8bc90dffc 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -78,6 +78,7 @@ variance_annotation If the variance annotation is `out`, the type parameter is said to be ***covariant***. If the variance annotation is `in`, the type parameter is said to be ***contravariant***. If there is no variance annotation, the type parameter is said to be ***invariant***. > *Example*: In the following: +> > ```csharp > interface C > { @@ -85,6 +86,7 @@ If the variance annotation is `out`, the type parameter is said to be ***covaria > Z P { get; set; } > } > ``` +> > `X` is covariant, `Y` is contravariant and `Z` is invariant. *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. @@ -146,6 +148,7 @@ It is a compile-time error for an interface to directly or indirectly inherit fr The ***base interfaces*** of an interface are the explicit base interfaces and their base interfaces. In other words, the set of base interfaces is the complete transitive closure of the explicit base interfaces, their explicit base interfaces, and so on. An interface inherits all members of its base interfaces. > *Example*: In the following code +> > ```csharp > interface IControl > { @@ -164,11 +167,13 @@ The ***base interfaces*** of an interface are the explicit base interfaces and t > > interface IComboBox: ITextBox, IListBox {} > ``` +> > the base interfaces of `IComboBox` are `IControl`, `ITextBox`, and `IListBox`. In other words, the `IComboBox` interface above inherits members `SetText` and `SetItems` as well as `Paint`. *end example* Members inherited from a constructed generic type are inherited after type substitution. That is, any constituent types in the member have the base class declaration’s type parameters replaced with the corresponding type arguments used in the *class_base* specification. > *Example*: In the following code +> > ```csharp > interface IBase > { @@ -180,6 +185,7 @@ Members inherited from a constructed generic type are inherited after type subst > // Inherited: string[][,] Combine(string[,] a, string[,] b); > } > ``` +> > the interface IDerived inherits the Combine method after the type parameter `T` is replaced with `string[,]`. *end example* A class or struct that implements an interface also implicitly implements all of the interface’s base interfaces. @@ -256,15 +262,18 @@ Furthermore, each class type constraint, interface type constraint and type para These rules ensure that any covariant or contravariant usage of the interface remains typesafe. > *Example*: +> > ```csharp > interface I > { > void M() where U : T; > } > ``` +> > is ill-formed because the usage of `T` as a type parameter constraint on `U` is not input-safe. > > Were this restriction not in place it would be possible to violate type safety in the following manner: +> > ```csharp > class B {} > class D : B {} @@ -279,6 +288,7 @@ These rules ensure that any covariant or contravariant usage of the interface re > I b = new C(); > b.M(); > ``` +> > This is actually a call to `C.M`. But that call requires that `E` derive from `D`, so type safety would be violated here. *end example* ### 17.4.3 Interface properties @@ -372,11 +382,13 @@ 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. *end example* > *Example*: In the following code +> > ```csharp > interface IInteger > { @@ -401,11 +413,13 @@ 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. *end example* > *Example*: In the following code +> > ```csharp > interface IBase > { @@ -435,6 +449,7 @@ For interfaces that are strictly single-inheritance (each interface in the inher > } > } > ``` +> > the `IBase.F` member is hidden by the `ILeft.F` member. The invocation `d.F(1)` thus selects `ILeft.F`, even though `IBase.F` appears to not be hidden in the access path that leads through `IRight`. > > The intuitive rule for hiding in multiple-inheritance interfaces is simply this: If a member is hidden in any access path, it is hidden in all access paths. Because the access path from `IDerived` to `ILeft` to `IBase` hides `IBase.F`, the member is also hidden in the access path from `IDerived` to `IRight` to `IBase`. *end example* @@ -444,6 +459,7 @@ For interfaces that are strictly single-inheritance (each interface in the inher 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. > *Example*: Given the declarations +> > ```csharp > interface IControl > { @@ -455,11 +471,13 @@ An interface member is sometimes referred to by its ***qualified interface membe > void SetText(string text); > } > ``` +> > the qualified name of `Paint` is `IControl.Paint` and the qualified name of SetText is `ITextBox.SetText`. In the example above, it is not possible to refer to `Paint` as `ITextBox.Paint`. *end example* When an interface is part of a namespace, a qualified interface member name can include the namespace name. > *Example*: +> > ```csharp > namespace System > { @@ -469,6 +487,7 @@ When an interface is part of a namespace, a qualified interface member name can > } > } > ``` +> > Within the `System` namespace, both `ICloneable.Clone` and `System.ICloneable.Clone` are qualified interface member names for the `Clone` method. *end example* ## 17.6 Interface implementations @@ -478,6 +497,7 @@ When an interface is part of a namespace, a qualified interface member name can 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. > *Example*: +> > ```csharp > interface ICloneable > { @@ -495,11 +515,13 @@ Interfaces may be implemented by classes and structs. To indicate that a class o > public int CompareTo(object other) {...} > } > ``` +> > *end example* A class or struct that directly implements an interface also implicitly implements all of the interface’s base interfaces. This is true even if the class or struct doesn’t explicitly list all base interfaces in the base class list. > *Example*: +> > ```csharp > interface IControl > { @@ -517,6 +539,7 @@ A class or struct that directly implements an interface also implicitly implemen > public void SetText(string text) {...} > } > ``` +> > Here, class `TextBox` implements both `IControl` and `ITextBox`. *end example* When a class `C` directly implements an interface, all classes derived from `C` also implement the interface implicitly. @@ -524,12 +547,14 @@ When a class `C` directly implements an interface, all classes derived from `C 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)). > *Example*: The following code illustrates how a class can implement constructed interface types: +> > ```csharp > class C {} > interface I1 {} > class D : C, I1 {} > class E : C, I1 {} > ``` +> > *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). @@ -539,6 +564,7 @@ The base interfaces of a generic class declaration shall satisfy the uniqueness 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. > *Example*: +> > ```csharp > interface IList > { @@ -558,11 +584,13 @@ For purposes of implementing interfaces, a class or struct may declare ***explic > void IDictionary.Add(int index, T value) {...} > } > ``` +> > Here `IDictionary.this` and `IDictionary.Add` are explicit interface member implementations. *end example* > *Example*: In some cases, the name of an interface member might not be appropriate for the implementing class, in which case, the interface member may be implemented using explicit interface member implementation. A class implementing a file abstraction, for example, would likely implement a `Close` member function that has the effect of releasing the file resource, and implement the `Dispose` method of the `IDisposable` interface using explicit interface member implementation: +> > ```csharp > interface IDisposable > { @@ -580,6 +608,7 @@ For purposes of implementing interfaces, a class or struct may declare ***explic > } > } > ``` +> > *end example* 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. @@ -597,6 +626,7 @@ It is a compile-time error for an explicit interface method implementation to in For an explicit interface member implementation to be valid, the class or struct shall name an interface in its base class list that contains a member whose qualified interface member name, type, number of type parameters, and parameter types exactly match those of the explicit interface member implementation. If an interface function member has a parameter array, the corresponding parameter of an associated explicit interface member implementation is allowed, but not required, to have the `params` modifier. If the interface function member does not have a parameter array then an associated explicit interface member implementation shall not have a parameter array. > *Example*: Thus, in the following class +> > ```csharp > class Shape : ICloneable > { @@ -604,7 +634,9 @@ For an explicit interface member implementation to be valid, the class or struct > int IComparable.CompareTo(object other) {...} // invalid > } > ``` +> > the declaration of `IComparable.CompareTo` results in a compile-time error because `IComparable` is not listed in the base class list of `Shape` and is not a base interface of `ICloneable`. Likewise, in the declarations +> > ```csharp > class Shape : ICloneable > { @@ -616,11 +648,13 @@ For an explicit interface member implementation to be valid, the class or struct > object ICloneable.Clone() {...} // invalid > } > ``` +> > the declaration of `ICloneable.Clone` in `Ellipse` results in a compile-time error because `ICloneable` is not explicitly listed in the base class list of `Ellipse`. *end example* The qualified interface member name of an explicit interface member implementation shall reference the interface in which the member was declared. > *Example*: Thus, in the declarations +> > ```csharp > interface IControl > { @@ -638,6 +672,7 @@ The qualified interface member name of an explicit interface member implementati > void ITextBox.SetText(string text) {...} > } > ``` +> > the explicit interface member implementation of Paint must be written as `IControl.Paint`, not `ITextBox.Paint`. *end example* ### 17.6.3 Uniqueness of implemented interfaces @@ -645,6 +680,7 @@ The qualified interface member name of an explicit interface member implementati 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. > *Example*: Suppose a generic class declaration were permitted to be written as follows: +> > ```csharp > interface I > { @@ -657,11 +693,14 @@ The interfaces implemented by a generic type declaration shall remain unique for > void I.F() {...} > } > ``` +> > Were this permitted, it would be impossible to determine which code to execute in the following case: +> > ```csharp > I x = new X(); > x.F(); > ``` +> > *end example* To determine if the interface list of a generic type declaration is valid, the following steps are performed: @@ -706,6 +745,7 @@ invokes the method in `Derived`, since `Derived'` effectively re-implem 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. > *Example*: In the following code: +> > ```csharp > interface I > { @@ -721,7 +761,9 @@ When a generic method implicitly implements an interface method, the constraints > public void H(T t) where T : string {...} // Error > } > ``` +> > the method `C.F` implicitly implements `I.F`. In this case, `C.F` is not required (nor permitted) to specify the constraint `T: object` since `object` is an implicit constraint on all type parameters. The method `C.G` implicitly implements `I.G` because the constraints match those in the interface, after the interface type parameters are replaced with the corresponding type arguments. The constraint for method `C.H` is an error because sealed types (`string` in this case) cannot be used as constraints. Omitting the constraint would also be an error since constraints of implicit interface method implementations are required to match. Thus, it is impossible to implicitly implement `I.H`. This interface method can only be implemented using an explicit interface member implementation: +> > ```csharp > class C : I > { @@ -735,6 +777,7 @@ When a generic method implicitly implements an interface method, the constraints > } > } > ``` +> > In this case, the explicit interface member implementation invokes a public method having strictly weaker constraints. The assignment from t to s is valid since `T` inherits a constraint of `T: string`, even though this constraint is not expressible in source code. >*end example* @@ -756,6 +799,7 @@ A compile-time error occurs if implementations cannot be located for all members 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). > *Example*: For example, given the generic interface declaration: +> > ```csharp > interface I > { @@ -763,11 +807,14 @@ Members of a constructed interface type are considered to have any type paramete > T this[int y] { get; } > } > ``` +> > the constructed interface `I` has the members: +> > ```csharp > string[] F(int x, string[,][] y); > string[] this[int y] { get; } > ``` +> > *end example* For purposes of interface mapping, a class or struct member `A` matches an interface member `B` when: @@ -783,6 +830,7 @@ Notable implications of the interface-mapping algorithm are: - Neither non-public nor static members participate in interface mapping. > *Example*: In the following code +> > ```csharp > interface ICloneable > { @@ -795,11 +843,13 @@ Notable implications of the interface-mapping algorithm are: > public object Clone() {...} > } > ``` +> > the `ICloneable.Clone` member of `C` becomes the implementation of `Clone` in ‘ICloneable’ because explicit interface member implementations take precedence over other members. *end example* If a class or struct implements two or more interfaces containing a member with the same name, type, and parameter types, it is possible to map each of those interface members onto a single class or struct member. > *Example*: +> > ```csharp > interface IControl > { @@ -816,11 +866,13 @@ If a class or struct implements two or more interfaces containing a member with > public void Paint() {...} > } > ``` +> > Here, the `Paint` methods of both `IControl` and `IForm` are mapped onto the `Paint` method in `Page`. It is of course also possible to have separate explicit interface member implementations for the two methods. *end example* If a class or struct implements an interface that contains hidden members, then some members may need to be implemented through explicit interface member implementations. > *Example*: +> > ```csharp > interface IBase > { @@ -832,7 +884,9 @@ If a class or struct implements an interface that contains hidden members, then > new int P(); > } > ``` +> > An implementation of this interface would require at least one explicit interface member implementation, and would take one of the following forms +> > ```csharp > class C : IDerived > { @@ -850,11 +904,13 @@ If a class or struct implements an interface that contains hidden members, then > public int P() {...} > } > ``` +> > *end example* When a class implements multiple interfaces that have the same base interface, there can be only one implementation of the base interface. > *Example*: In the following code +> > ```csharp > interface IControl > { @@ -878,11 +934,13 @@ When a class implements multiple interfaces that have the same base interface, t > void IListBox.SetItems(string[] items) {...} > } > ``` +> > it is not possible to have separate implementations for the `IControl` named in the base class list, the `IControl` inherited by `ITextBox`, and the `IControl` inherited by `IListBox`. Indeed, there is no notion of a separate identity for these interfaces. Rather, the implementations of `ITextBox`and `IListBox` share the same implementation of `IControl`, and `ComboBox` is simply considered to implement three interfaces, `IControl`, `ITextBox`, and `IListBox`. *end example* The members of a base class participate in interface mapping. > *Example*: In the following code +> > ```csharp > interface Interface1 > { @@ -900,6 +958,7 @@ The members of a base class participate in interface mapping. > public new void G() {} > } > ``` +> > the method `F` in `Class1` is used in `Class2's` implementation of `Interface1`. *end example* ### 17.6.6 Interface implementation inheritance @@ -909,6 +968,7 @@ A class inherits all interface implementations provided by its base classes. Without explicitly re-implementing an interface, a derived class cannot in any way alter the interface mappings it inherits from its base classes. > *Example*: In the declarations +> > ```csharp > interface IControl > { @@ -925,7 +985,9 @@ Without explicitly re-implementing an interface, a derived class cannot in any w > public new void Paint() {...} > } > ``` +> > the `Paint` method in `TextBox` hides the `Paint` method in `Control`, but it does not alter the mapping of `Control.Paint` onto `IControl.Paint`, and calls to `Paint` through class instances and interface instances will have the following effects +> > ```csharp > Control c = new Control(); > TextBox t = new TextBox(); @@ -936,11 +998,13 @@ Without explicitly re-implementing an interface, a derived class cannot in any w > ic.Paint(); // invokes Control.Paint(); > it.Paint(); // invokes Control.Paint(); > ``` +> > *end example* However, when an interface method is mapped onto a virtual method in a class, it is possible for derived classes to override the virtual method and alter the implementation of the interface. > *Example*: Rewriting the declarations above to +> > ```csharp > interface IControl > { @@ -957,7 +1021,9 @@ However, when an interface method is mapped onto a virtual method in a class, it > public override void Paint() {...} > } > ``` +> > the following effects will now be observed +> > ```csharp > Control c = new Control(); > TextBox t = new TextBox(); @@ -968,11 +1034,13 @@ However, when an interface method is mapped onto a virtual method in a class, it > ic.Paint(); // invokes Control.Paint(); > it.Paint(); // invokes TextBox.Paint(); > ``` +> > *end example* Since explicit interface member implementations cannot be declared virtual, it is not possible to override an explicit interface member implementation. However, it is perfectly valid for an explicit interface member implementation to call another method, and that other method can be declared virtual to allow derived classes to override it. > *Example*: +> > ```csharp > interface IControl > { @@ -1000,6 +1068,7 @@ A class that inherits an interface implementation is permitted to ***re-implemen A re-implementation of an interface follows exactly the same interface mapping rules as an initial implementation of an interface. Thus, the inherited interface mapping has no effect whatsoever on the interface mapping established for the re-implementation of the interface. > *Example*: In the declarations +> > ```csharp > interface IControl > { @@ -1016,11 +1085,13 @@ A re-implementation of an interface follows exactly the same interface mapping r > public void Paint() {} > } > ``` +> > the fact that `Control` maps `IControl.Paint` onto `Control.IControl.Paint` doesn’t affect the re-implementation in `MyControl`, which maps `IControl.Paint` onto `MyControl.Paint`. *end example* Inherited public member declarations and inherited explicit interface member declarations participate in the interface mapping process for re-implemented interfaces. > *Example*: +> > ```csharp > interface IMethods > { @@ -1044,11 +1115,13 @@ Inherited public member declarations and inherited explicit interface member dec > void IMethods.H() {} > } > ``` +> > Here, the implementation of `IMethods` in `Derived` maps the interface methods onto `Derived.F`, `Base.IMethods.G`, `Derived.IMethods.H`, and `Base.I`. *end example* When a class implements an interface, it implicitly also implements all that interface’s base interfaces. Likewise, a re-implementation of an interface is also implicitly a re-implementation of all of the interface’s base interfaces. > *Example*: +> > ```csharp > interface IBase > { @@ -1072,6 +1145,7 @@ When a class implements an interface, it implicitly also implements all that int > public void G() {...} > } > ``` +> > Here, the re-implementation of `IDerived` also re-implements `IBase`, mapping `IBase.F` onto `D.F`. *end example* ### 17.6.8 Abstract classes and interfaces @@ -1079,6 +1153,7 @@ When a class implements an interface, it implicitly also implements all that int 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. > *Example*: +> > ```csharp > interface IMethods > { @@ -1092,11 +1167,13 @@ Like a non-abstract class, an abstract class shall provide implementations of al > public abstract void G(); > } > ``` +> > Here, the implementation of `IMethods` maps `F` and `G` onto abstract methods, which shall be overridden in non-abstract classes that derive from `C`. *end example* Explicit interface member implementations cannot be abstract, but explicit interface member implementations are of course permitted to call abstract methods. > *Example*: +> > ```csharp > interface IMethods > { @@ -1112,4 +1189,5 @@ Explicit interface member implementations cannot be abstract, but explicit inter > protected abstract void GG(); > } > ``` +> > Here, non-abstract classes that derive from `C` would be required to override `FF` and `GG`, thus providing the actual implementation of `IMethods`. *end example* diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index 4ee2a7a8e..6340f3214 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -59,6 +59,7 @@ The productions for *simple_name* ([§11.7.4](expressions.md#1174-simple-names)) > ```csharp > F(G(7)); > ``` +> > could be interpreted as a call to `F` with two arguments, `G < A` and `B > (7)`. Alternatively, it could be interpreted as a call to `F` with one argument, which is a call to a generic method `G` with two type arguments and one regular argument. *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 @@ -74,22 +75,30 @@ then the *type_argument_list* is retained as part of the *simple_name*, *member_ > *Example*: The statement: +> > ```csharp > F(G(7)); > ``` +> > will, according to this rule, be interpreted as a call to `F` with one argument, which is a call to a generic method `G` with two type arguments and one regular argument. The statements +> > ```csharp > F(G7); > F(G>7); > ``` +> > will each be interpreted as a call to `F` with two arguments. The statement +> > ```csharp > x = F + y; > ``` +> > will be interpreted as a less-than operator, greater-than operator and unary-plus operator, as if the statement had been written `x = (F < A) > (+y)`, instead of as a *simple_name* with a *type_argument_list* followed by a binary-plus operator. In the statement +> > ```csharp > 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. *end example* ## 6.3 Lexical analysis @@ -173,6 +182,7 @@ Two forms of comments are supported: delimited comments and single-line comments A ***delimited comment*** begins with the characters `/*` and ends with the characters `*/`. Delimited comments can occupy a portion of a line, a single line, or multiple lines. > *Example*: The example +> > ```csharp > /* Hello, world program > This program writes "hello, world" to the console @@ -185,11 +195,13 @@ A ***delimited comment*** begins with the characters `/*` and ends with the cha > } > } > ``` +> > includes a delimited comment. *end example* A ***single-line comment*** begins with the characters `//` and extends to the end of the line. > *Example*: The example +> > ```csharp > // Hello, world program > // This program writes "hello, world" to the console @@ -202,6 +214,7 @@ A ***single-line comment*** begins with the characters `//` and extends to the > } > } > ``` +> > shows several single-line comments. *end example* ```ANTLR @@ -246,9 +259,11 @@ Comments do not nest. The character sequences `/*` and `*/` have no special me Comments are not processed within character and string literals. > *Note*: These rules must be interpreted carefully. For instance, in the example below, the delimited comment that begins before `A` ends between `B` and `C()`. The reason is that +> > ```csharp > // B */ C(); > ``` +> > is not actually a single-line comment, since `//` has no special meaning within a delimited comment, and so `*/` does have its usual special meaning in that line. > > Likewise, the delimited comment starting before `D` ends before `E`. The reason is that `"D */ "` is not actually a string literal, since it appears inside a delimited comment. @@ -256,6 +271,7 @@ Comments are not processed within character and string literals. > A useful consequence of `/*` and `*/` having no special meaning within a single-line comment is that a block of source code lines can be commented out by putting `//` at the beginning of each line. In general it does not work to put `/*` before those lines and `*/` after them, as this does not properly encapsulate delimited comments in the block, and in general may completely change the structure of such delimited comments. > > Example code: +> > ```csharp > static void Main() > { @@ -264,6 +280,7 @@ Comments are not processed within character and string literals. > Console.WriteLine(/* "D */ "E"); > } > ``` +> > *end note* *Single_Line_Comment*s and *Delimited_Comment*s having particular formats can be used as *documentation comments*, as described in [§D](documentation-comments.md#annex-d-documentation-comments). @@ -321,6 +338,7 @@ Multiple translations are not performed. For instance, the string literal `"\u00 > *Example*: The example +> > ```csharp > class Class1 > { @@ -334,7 +352,9 @@ Multiple translations are not performed. For instance, the string literal `"\u00 > } > } > ``` +> > shows several uses of `\u0066`, which is the escape sequence for the letter “`f`”. The program is equivalent to +> > ```csharp > class Class1 > { @@ -348,6 +368,7 @@ Multiple translations are not performed. For instance, the string literal `"\u00 > } > } > ``` +> > *end example* ### 6.4.3 Identifiers @@ -456,6 +477,7 @@ The prefix “`@`” enables the use of keywords as identifiers, which is usefu > *Example*: The example: +> > ```csharp > class @class > { @@ -480,6 +502,7 @@ The prefix “`@`” enables the use of keywords as identifiers, which is usefu > } > } > ``` +> > defines a class named “`class`” with a static method named “`static`” that takes a parameter named “`bool`”. Note that since Unicode escapes are not permitted in keywords, the token “`cl\u0061ss`” is an identifier, and is the same identifier as “`@class`”. *end example* Two identifiers are considered the same if they are identical after the following transformations are applied, in order: @@ -701,10 +724,12 @@ fragment Hexadecimal_Escape_Sequence > *Note*: The use of the `\x` *Hexadecimal_Escape_Sequence* production can be error-prone and hard to read due to the variable number of hexadecimal digits following the `\x`. For example, in the code: +> > ```csharp > string good = "x9Good text"; > string bad = "x9Bad text"; > ``` +> > it might appear at first that the leading character is the same (`U+0009`, a tab character) in both strings. In fact the second string starts with `U+9BAD` as all three letters in the word “Bad” are valid hexadecimal digits. As a matter of style, it is recommended that `\x` is avoided in favour of either specific escape sequences (`\t` in this example) or the fixed-length `\u` escape sequence. *end note* A hexadecimal escape sequence represents a single Unicode UTF-16 code unit, with the value formed by the hexadecimal number following “`\x`”. @@ -781,6 +806,7 @@ fragment Quote_Escape_Sequence ``` > *Example*: The example +> > ```csharp > string a = "Happy birthday, Joel"; // Happy birthday, Joel > string b = @"Happy birthday, Joel"; // Happy birthday, Joel @@ -795,6 +821,7 @@ fragment Quote_Escape_Sequence > two > three"; > ``` +> > shows a variety of string literals. The last string literal, `j`, is a verbatim string literal that spans multiple lines. The characters between the quotation marks, including white space such as new line characters, are preserved verbatim, and each pair of double-quote characters is replaced by one such character. *end example* @@ -810,6 +837,7 @@ 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. > *Example*: For instance, the output produced by +> > ```csharp > class Test > { @@ -821,6 +849,7 @@ Each string literal does not necessarily result in a new string instance. When t > } > } > ``` +> > is `True` because the two literals refer to the same string instance. *end example* #### 6.4.5.7 The null literal @@ -928,6 +957,7 @@ A source line containing a `#define`, `#undef`, `#if`, `#elif`, `#else`, `#endif Pre-processing directives are not part of the syntactic grammar of C#. However, pre-processing directives can be used to include or exclude sequences of tokens and can in that way affect the meaning of a C# program. > *Example*: When compiled, the program +> > ```csharp > #define A > #undef B @@ -945,7 +975,9 @@ Pre-processing directives are not part of the syntactic grammar of C#. However, > #endif > } > ``` +> > results in the exact same sequence of tokens as the program +> > ```csharp > class C > { @@ -953,6 +985,7 @@ Pre-processing directives are not part of the syntactic grammar of C#. However, > void I() {} > } > ``` +> > Thus, whereas lexically, the two programs are quite different, syntactically, they are identical. *end example* ### 6.5.2 Conditional compilation symbols @@ -1032,6 +1065,7 @@ The processing of a `#define` directive causes the given conditional compilation Any `#define` and `#undef` directives in a compilation unit shall occur before the first *token* ([§6.4](lexical-structure.md#64-tokens)) in the compilation unit; otherwise a compile-time error occurs. In intuitive terms, `#define` and `#undef` directives shall precede any “real code” in the compilation unit. > *Example*: The example: +> > ```csharp > #define Enterprise > #if Professional || Enterprise @@ -1044,11 +1078,13 @@ Any `#define` and `#undef` directives in a compilation unit shall occur before t > #endif > } > ``` +> > is valid because the `#define` directives precede the first token (the `namespace` keyword) in the compilation unit. *end example* > *Example*: The following example results in a compile-time error because a #define follows real code: +> > ```csharp > #define A > namespace N @@ -1059,25 +1095,30 @@ Any `#define` and `#undef` directives in a compilation unit shall occur before t > #endif > } > ``` +> > *end example* A `#define` may define a conditional compilation symbol that is already defined, without there being any intervening `#undef` for that symbol. > *Example*: The example below defines a conditional compilation symbol A and then defines it again. +> > ```csharp > #define A > #define A > ``` +> > For compilers that allow conditional compilation symbols to be defined as compilation options, an alternative way for such redefinition to occur is to define the symbol as a compiler option as well as in the source. *end example* A `#undef` may “undefine” a conditional compilation symbol that is not defined. > *Example*: The example below defines a conditional compilation symbol `A` and then undefines it twice; although the second `#undef` has no effect, it is still valid. +> > ```csharp > #define A > #undef A > #undef A > ``` +> > *end example* ### 6.5.5 Conditional compilation directives @@ -1112,6 +1153,7 @@ fragment PP_Endif Conditional compilation directives shall be written in groups consisting of, in order, a `#if` directive, zero or more `#elif` directives, zero or one `#else` directive, and a `#endif` directive. Between the directives are ***conditional sections*** of source code. Each section is controlled by the immediately preceding directive. A conditional section may itself contain nested conditional compilation directives provided these directives form complete groups. > *Example*: The following example illustrates how conditional compilation directives can nest: +> > ```csharp > #define Debug // Debugging on > #undef Trace // Tracing off @@ -1130,6 +1172,7 @@ Conditional compilation directives shall be written in groups consisting of, in > ... > } > ``` +> > *end example* At most one of the contained conditional sections is selected for normal lexical processing: @@ -1147,6 +1190,7 @@ Any remaining conditional sections are skipped and no tokens, except those for p > *Example*: The following example illustrates how conditional compilation directives can nest: +> > ```csharp > #define Debug // Debugging on > #undef Trace // Tracing off @@ -1165,7 +1209,9 @@ Any remaining conditional sections are skipped and no tokens, except those for p > ... > } > ``` +> > Except for pre-processing directives, skipped source code is not subject to lexical analysis. For example, the following is valid despite the unterminated comment in the `#else` section: +> > ```csharp > #define Debug // Debugging on > class PurchaseTransaction @@ -1181,9 +1227,11 @@ Any remaining conditional sections are skipped and no tokens, except those for p > ... > } > ``` +> > Note, however, that pre-processing directives are required to be lexically correct even in skipped sections of source code. > > Pre-processing directives are not processed when they appear inside multi-line input elements. For example, the program: +> > ```csharp > class Hello > { @@ -1199,7 +1247,9 @@ Any remaining conditional sections are skipped and no tokens, except those for p > } > } > ``` +> > results in the output: +> > ```console > hello, > #if Debug @@ -1208,7 +1258,9 @@ Any remaining conditional sections are skipped and no tokens, except those for p > Nebraska > #endif > ``` +> > In peculiar cases, the set of pre-processing directives that is processed might depend on the evaluation of the *pp_expression*. The example: +> > ```csharp > #if X > /* @@ -1216,6 +1268,7 @@ Any remaining conditional sections are skipped and no tokens, except those for p > /* */ class Q { } > #endif > ``` +> > always produces the same token stream (`class` `Q` `{` `}`), regardless of whether or not `X` is defined. If `X` is defined, the only processed directives are `#if` and `#endif`, due to the multi-line comment. If `X` is undefined, then three directives (`#if`, `#else`, `#endif`) are part of the directive set. *end example* ### 6.5.6 Diagnostic directives @@ -1234,12 +1287,14 @@ fragment PP_Message ``` > *Example*: The example +> > ```csharp > #if Debug && Retail > #error A build can't be both debug and retail > #endif > class Test {...} > ``` +> > produces a compile-time error (“A build can’t be both debug and retail”) if the conditional compilation symbols `Debug` and `Retail` are both defined. Note that a *PP_Message* can contain arbitrary text; specifically, it need not contain well-formed tokens, as shown by the single quote in the word `can't`. *end example* ### 6.5.7 Region directives @@ -1260,6 +1315,7 @@ fragment PP_End_Region : 'endregion' PP_Message? ; ``` + No semantic meaning is attached to a region; regions are intended for use by the programmer or by automated tools to mark a section of source code. There must be one `#endregion` directive matching every `#region` directive. The message specified in a `#region` or `#endregion` directive likewise has no semantic meaning; it merely serves to identify the region. Matching `#region` and `#endregion` directives may have different *PP_Message*s. The lexical processing of a region: diff --git a/standard/namespaces.md b/standard/namespaces.md index 2dfec318b..94856a800 100644 --- a/standard/namespaces.md +++ b/standard/namespaces.md @@ -27,12 +27,14 @@ The *global_attributes* ([§21.3](attributes.md#213-attribute-specification)) of The *namespace_member_declaration*s of each compilation unit of a program contribute members to a single declaration space called the global namespace. > *Example*: +> > ```csharp > File A.cs: > class A {} > File B.cs: > 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 @@ -62,6 +64,7 @@ Within a *namespace_body*, the optional *using_directive*s import the names of o The *qualified_identifier* of a *namespace_declaration* may be a single identifier or a sequence of identifiers separated by “`.`” tokens. The latter form permits a program to define a nested namespace without lexically nesting several namespace declarations. > *Example*: +> > ```csharp > namespace N1.N2 > { @@ -69,7 +72,9 @@ The *qualified_identifier* of a *namespace_declaration* may be a single identifi > class B {} > } > ``` +> > is semantically equivalent to +> > ```csharp > namespace N1 > { @@ -80,11 +85,13 @@ The *qualified_identifier* of a *namespace_declaration* may be a single identifi > } > } > ``` +> > *end example* Namespaces are open-ended, and two namespace declarations with the same fully qualified name ([§7.8.2](basic-concepts.md#782-unqualified-names)) contribute to the same declaration space ([§7.3](basic-concepts.md#73-declarations)). > *Example*: In the following code +> > ```csharp > namespace N1.N2 > { @@ -96,6 +103,7 @@ Namespaces are open-ended, and two namespace declarations with the same fully qu > class B {} > } > ``` +> > the two namespace declarations above contribute to the same declaration space, in this case declaring two classes with the fully qualified names `N1.N2.A` and `N1.N2.B`. Because the two declarations 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.4 Extern alias directives @@ -171,6 +179,7 @@ using_alias_directive Within global attributes and member declarations in a compilation unit or namespace body that contains a *using_alias_directive*, the identifier introduced by the *using_alias_directive* can be used to reference the given namespace or type. > *Example*: +> > ```csharp > namespace N1.N2 > { @@ -183,7 +192,9 @@ Within global attributes and member declarations in a compilation unit or namesp > class B: A {} > } > ``` +> > Above, within member declarations in the `N3` namespace, `A` is an alias for `N1.N2.A`, and thus class `N3.B` derives from class `N1.N2.A`. The same effect can be obtained by creating an alias `R` for `N1.N2` and then referencing `R.A`: +> > ```csharp > namespace N3 > { @@ -192,11 +203,13 @@ Within global attributes and member declarations in a compilation unit or namesp > class B : R.A {} > } > ``` +> > *end example* Within using directives, global attributes and member declarations in 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 associated namespace. > *Example*: For example: +> > ```csharp > namespace N1 > { @@ -205,7 +218,9 @@ Within using directives, global attributes and member declarations in a compilat > class B : N2::A {} > } > ``` +> > 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 > { @@ -216,11 +231,13 @@ Within using directives, global attributes and member declarations in a compilat > class B : A {} > } > ``` +> >*end example* An *extern_alias_directive* or *using_alias_directive* makes an alias available within a particular compilation unit or namespace body, but it does not contribute any new members to the underlying declaration space. In other words, an alias directive is not transitive, but, rather, affects only the compilation unit or namespace body in which it occurs. > *Example*: In the following code +> > ```csharp > namespace N3 > { @@ -234,7 +251,9 @@ An *extern_alias_directive* or *using_alias_directive* makes an alias available > class B : R1::A, R2.I {} // Error, R1 and R2 unknown > } > ``` +> > 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; > @@ -250,11 +269,13 @@ An *extern_alias_directive* or *using_alias_directive* makes an alias available > class C : R1::A, R2.I {} > } > ``` +> > *end example* Each *extern_alias_directive* or *using_alias_directive* in a *compilation_unit* or *namespace_body* contributes a name to the alias declaration space ([§7.3](basic-concepts.md#73-declarations)) of the immediately enclosing *compilation_unit* or *namespace_body*. The *identifier* of the alias directive shall be unique within the corresponding alias declaration space. The alias identifier need not be unique within the global declaration space or the declaration space of the corresponding namespace. > *Example*: +> > ```csharp > extern alias A; > extern alias B; @@ -263,9 +284,11 @@ Each *extern_alias_directive* or *using_alias_directive* in a *compilation_unit* > > class B {} // 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. > > When an alias name matches the name of a member of a namespace, usage of either must be appropriately qualified: +> > ```csharp > namespace N1.N2 > { @@ -289,11 +312,13 @@ Each *extern_alias_directive* or *using_alias_directive* in a *compilation_unit* > class Z : N3.B {} // Ok: uses N3.B > } > ``` +> > 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. *end example* Just like regular members, names introduced by *alias_directives* are hidden by similarly named members in nested scopes. > *Example*: In the following code +> > ```csharp > using R = N1.N2; > @@ -303,11 +328,13 @@ Just like regular members, names introduced by *alias_directives* are hidden by > class B: R.A {} // Error, R has no member A > } > ``` +> > the reference to `R.A` in the declaration of `B` causes a compile-time error because `R` refers to `N3.R`, not `N1.N2`. *end example* The order in which *extern_alias_directive*s are written has no significance. Likewise, the order in which *using_alias_directive*s are written has no significance, but all *using_alias_directives* must come after all *extern_alias_directive*s in the same compilation unit or namespace body. Resolution of the *namespace_or_type_name* referenced by a *using_alias_directive* is not affected by the *using_alias_directive* itself or by other *using_directive*s in the immediately containing compilation unit or namespace body, but may be affected by *extern_alias_directive*s in the immediately containing compilation unit or namespace body. In other words, the *namespace_or_type_name* of a *using_alias_directive* is resolved as if the immediately containing compilation unit or namespace body had no *using_directive*s but has the correct set of *extern_alias_directive*s. > *Example*: In the following code +> > ```csharp > namespace N1.N2 {} > @@ -321,6 +348,7 @@ The order in which *extern_alias_directive*s are written has no significance. Li > 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*. *end example* A *using_alias_directive* can create an alias for any namespace or type, including the namespace within which it appears and any namespace or type nested within that namespace. @@ -328,6 +356,7 @@ A *using_alias_directive* can create an alias for any namespace or type, includi Accessing a namespace or type through an alias yields exactly the same result as accessing that namespace or type through its declared name. > *Example*: Given +> > ```csharp > namespace N1.N2 > { @@ -347,11 +376,13 @@ Accessing a namespace or type through an alias yields exactly the same result as > } > } > ``` +> > the names `N1.N2.A`, `R1.N2.A`, and `R2.A` are equivalent and all refer to the class declaration whose fully qualified name is `N1.N2.A`. *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. > *Example*: +> > ```csharp > namespace N > { @@ -373,9 +404,13 @@ Although each part of a partial type ([§14.2.7](classes.md#1427-partial-declara > } > } > ``` +> > *end example* + Using aliases can name a closed constructed type, but cannot name an unbound generic type declaration without supplying type arguments. + > *Example*: +> > ```csharp > namespace N1 > { @@ -393,6 +428,7 @@ Using aliases can name a closed constructed type, but cannot name an unbound gen > using Z = N1.A; // Error, using alias cannot have type parameters > } > ``` +> > *end example* ### 13.5.3 Using namespace directives @@ -408,6 +444,7 @@ using_namespace_directive Within member declarations in a compilation unit or namespace body that contains a *using_namespace_directive*, the types contained in the given namespace can be referenced directly. > *Example*: +> > ```csharp > namespace N1.N2 > { @@ -421,11 +458,13 @@ Within member declarations in a compilation unit or namespace body that contains > class B : A {} > } > ``` +> > Above, within member declarations in the `N3` namespace, the type members of `N1.N2` are directly available, and thus class `N3.B` derives from class `N1.N2.A`. *end example* A *using_namespace_directive* imports the types contained in the given namespace, but specifically does not import nested namespaces. > *Example*: In the following code +> > ```csharp > namespace N1.N2 > { @@ -438,11 +477,13 @@ A *using_namespace_directive* imports the types contained in the given namespace > class B : N2.A {} // Error, N2 unknown > } > ``` +> > the *using_namespace_directive* imports the types contained in `N1`, but not the namespaces nested in `N1`. Thus, the reference to `N2.A` in the declaration of `B` results in a compile-time error because no members named `N2` are in scope. *end example* Unlike a *using_alias_directive*, a *using_namespace_directive* may import types whose identifiers are already defined within the enclosing compilation unit or namespace body. In effect, names imported by a *using_namespace_directive* are hidden by similarly named members in the enclosing compilation unit or namespace body. > *Example*: +> > ```csharp > namespace N1.N2 > { @@ -456,11 +497,13 @@ Unlike a *using_alias_directive*, a *using_namespace_directive* may import types > class A {} > } > ``` +> > Here, within member declarations in the `N3` namespace, `A` refers to `N3.A` rather than `N1.N2.A`. *end example* Because names may be ambiguous when more than one imported namespace introduces the same type name, a *using_alias_directive* is useful to disambiguate the reference. > *Example*: In the following code +> > ```csharp > namespace N1 > { @@ -480,6 +523,7 @@ Because names may be ambiguous when more than one imported namespace introduces > class B : A {} // Error, A is ambiguous > } > ``` +> > both `N1` and `N2` contain a member `A`, and because `N3` imports both, referencing `A` in `N3` is a compile-time error. In this situation, the conflict can be resolved either through qualification of references to `A`, or by introducing a *using_alias_directive* that picks a particular `A`. For example: > > ```csharp @@ -492,11 +536,13 @@ Because names may be ambiguous when more than one imported namespace introduces > class B : A {} // A means N1.A > } > ``` +> > *end example* Furthermore, when more than one namespace or type imported by *using_namespace_directive*s or *using_static_directive*s in the same compilation unit or namespace body contain types or members by the same name, references to that name as a *simple_name* are considered ambiguous. > *Example*: +> > ```csharp > namespace N1 > { @@ -523,6 +569,7 @@ Furthermore, when more than one namespace or type imported by *using_namespace_d > } > } > ``` +> > `N1` contains a type member `A`, and `C` contains a static field `A`, and because `N2` imports both, referencing `A` as a *simple_name* is ambiguous and a compile-time error. *end example* Like a *using_alias_directive*, a *using_namespace_directive* does not contribute any new members to the underlying declaration space of the compilation unit or namespace, but, rather, affects only the compilation unit or namespace body in which it appears. @@ -538,9 +585,11 @@ using_static_directive : 'using' 'static' type_name ';' ; ``` + Within member declarations in a compilation unit or namespace body that contains a *using_static_directive*, the accessible nested types and static members (except extension methods) contained directly in the declaration of the given type can be referenced directly. > *Example*: +> > ```csharp > namespace N1 > { @@ -564,11 +613,13 @@ Within member declarations in a compilation unit or namespace body that contains > } > } > ``` +> > In the preceding code, within member declarations in the `N2` namespace, the static members and nested types of `N1.A` are directly available, and thus the method `N` is able to reference both the `B` and `M` members of `N1.A`. *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)). > *Example*: +> > ```csharp > namespace N1 > { @@ -593,11 +644,13 @@ A *using_static_directive* specifically does not import extension methods direct > } > } > ``` +> > the *using_static_directive* imports the extension method `M` contained in `N1.A`, but only as an extension method. Thus, the first reference to `M` in the body of `B.N` results in a compile-time error because no members named `M` are in scope. *end example* A *using_static_directive* only imports members and types declared directly in the given type, not members and types declared in base classes. > *Example*: +> > ```csharp > namespace N1 > { @@ -626,6 +679,7 @@ A *using_static_directive* only imports members and types declared directly in t > } > } > ``` +> > the *using_static_directive* imports the method `M2` contained in `N1.B`, but does not import the method `M` contained in `N1.A`. Thus, the reference to `M` in the body of `C.N` results in a compile-time error because no members named `M` are in scope. Developers must add a second `using static` directive to specify that the methods in `N1.A` should also be imported. *end example* Ambiguities between multiple *using_namespace_directives* and *using_static_directives* are discussed in [§13.5.3](namespaces.md#1353-using-namespace-directives). @@ -709,6 +763,7 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as - Otherwise, the *qualified_alias_member* is undefined and a compile-time error occurs. > *Example*: In the code: +> > ```csharp > using S = System.Net.Sockets; > @@ -728,11 +783,13 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as > } > } > ``` +> > the class `A` is referenced with `global::A` and the type `System.Net.Sockets.Socket` is referenced with `S::Socket`. Using `A.x` and `S.Socket` instead would have caused compile-time errors because `A` and `S` would have resolved to the parameters. *end example* > *Note*: The identifier `global` has special meaning only when used as the left-hand identifier of a *qualified_alias_name*. It is not a keyword and it is not itself an alias; it is a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)). In the code: +> > ```csharp > class A { } > @@ -746,6 +803,7 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as > using `global.A` causes a compile-time error since there is no entity named `global` in scope. If some entity named global were in scope, then `global` in `global.A` would have resolved to that entity. > > Using `global` as the left-hand identifier of a *qualified_alias_member* always causes a lookup in the `global` namespace, even if there is a using alias named `global`. In the code: +> > ```csharp > using global = MyGlobalTypes; > @@ -757,6 +815,7 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as > global::A y; // Valid: References A in the global namespace > } > ``` +> > `global.A` resolves to `MyGlobalTypes.A` and `global::A` resolves to class `A` in the global namespace. *end note* ### 13.8.2 Uniqueness of aliases @@ -764,6 +823,7 @@ Using this notation, the meaning of a *qualified_alias_member* is determined as 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. > *Example*: In the following: +> > ```csharp > namespace N > { @@ -782,4 +842,5 @@ Each compilation unit and namespace body has a separate declaration space for ex > } > } > ``` +> > the name `A` has two possible meanings in the second namespace body because both the class `A` and the using alias `A` are in scope. For this reason, use of `A` in the qualified name `A.Stream` is ambiguous and causes a compile-time error to occur. However, use of `A` with the `::` qualifier is not an error because `A` is looked up only as a namespace alias. *end example* diff --git a/standard/statements.md b/standard/statements.md index ead3b3b0a..a1e8a8d9d 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -30,11 +30,13 @@ embedded_statement | fixed_statement // unsafe code support ; ``` + *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)). 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. > *Example*: The code +> > ```csharp > void F(bool b) > { @@ -42,6 +44,7 @@ The *embedded_statement* nonterminal is used for statements that appear within o > int i = 44; > } > ``` +> > results in a compile-time error because an `if` statement requires an *embedded_statement* rather than a *statement* for its `if` branch. If this code were permitted, then the variable `i` would be declared, but it could never be used. Note, however, that by placing `i`’s declaration in a block, the example is valid. *end example* ## 12.2 End points and reachability @@ -53,6 +56,7 @@ Every statement has an ***end point***. In intuitive terms, the end point of a s If a statement can possibly be reached by execution, the statement is said to be ***reachable***. Conversely, if there is no possibility that a statement will be executed, the statement is said to be ***unreachable***. > *Example*: In the following code +> > ```csharp > void F() > { @@ -63,6 +67,7 @@ If a statement can possibly be reached by execution, the statement is said to be > Console.WriteLine("reachable"); > } > ``` +> > the second invocation of Console.WriteLine is unreachable because there is no possibility that the statement will be executed. *end example* 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. @@ -70,6 +75,7 @@ A warning is reported if a statement other than *throw_statement*, *block*, or * > *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.20](expressions.md#1120-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 +> > ```csharp > void F() > { @@ -78,7 +84,9 @@ A warning is reported if a statement other than *throw_statement*, *block*, or * > Console.WriteLine("unreachable"); > } > ``` +> > the Boolean expression of the `if` statement is a constant expression because both operands of the `==` operator are constants. As the constant expression is evaluated at compile-time, producing the value `false`, the `Console.WriteLine` invocation is considered unreachable. However, if `i` is changed to be a local variable +> > ```csharp > void F() > { @@ -87,11 +95,13 @@ A warning is reported if a statement other than *throw_statement*, *block*, or * > Console.WriteLine("reachable"); > } > ``` +> > the `Console.WriteLine` invocation is considered reachable, even though, in reality, it will never be executed. *end note* The *block* of a function member or an anonymous function is always considered reachable. By successively evaluating the reachability rules of each statement in a block, the reachability of any given statement can be determined. > *Example*: In the following code +> > ```csharp > void F(int x) > { @@ -100,6 +110,7 @@ The *block* of a function member or an anonymous function is always considered r > Console.WriteLine("negative"); > } > ``` +> > 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)). @@ -177,7 +188,8 @@ An empty statement is used when there are no operations to perform in a context Execution of an empty statement simply transfers control to the end point of the statement. Thus, the end point of an empty statement is reachable if the empty statement is reachable. -*Example*: An empty statement can be used when writing a `while` statement with a null body: +> *Example*: An empty statement can be used when writing a `while` statement with a null body: +> > ```csharp > bool ProcessMessage() {...} > void ProcessMessages() @@ -186,7 +198,9 @@ Execution of an empty statement simply transfers control to the end point of the > ; > } > ``` +> > Also, an empty statement can be used to declare a label just before the closing “`}`” of a block: +> > ```csharp > void F() > { @@ -200,6 +214,7 @@ Execution of an empty statement simply transfers control to the end point of the > ; > } > ``` +> > *end example* ## 12.5 Labeled statements @@ -221,6 +236,7 @@ A label can be referenced from `goto` statements ([§12.10.4](statements.md#1210 Labels have their own declaration space and do not interfere with other identifiers. > *Example*: The example +> > ```csharp > int F(int x) > { @@ -233,6 +249,7 @@ Labels have their own declaration space and do not interfere with other identifi > return x; > } > ``` +> > is valid and uses the name x as both a parameter and a label. *end example* Execution of a labeled statement corresponds exactly to execution of the statement following the label. @@ -296,6 +313,7 @@ In the context of a local variable declaration, the identifier `var` acts as a c - The initializer *expression* cannot refer to the declared variable itself > *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 @@ -303,6 +321,7 @@ In the context of a local variable declaration, the identifier `var` acts as a c > 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* 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. @@ -312,13 +331,16 @@ The scope of a local variable declared in a *local_variable_declaration* is the 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. > *Example*: The example +> > ```csharp > void F() > { > int x = 1, y, z = x * 2; > } > ``` +> > corresponds exactly to +> > ```csharp > void F() > { @@ -327,11 +349,13 @@ A local variable declaration that declares multiple variables is equivalent to m > int z; z = x * 2; > } > ``` +> > *end example* 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. > *Example*: +> > ```csharp > var i = 5; > var s = "Hello"; @@ -339,7 +363,9 @@ In an implicitly typed local variable declaration, the type of the local variabl > var numbers = new int[] {1, 2, 3}; > var orders = new Dictionary(); > ``` +> > The implicitly typed local variable declarations above are precisely equivalent to the following explicitly typed declarations: +> > ```csharp > int i = 5; > string s = "Hello"; @@ -347,6 +373,7 @@ In an implicitly typed local variable declaration, the type of the local variabl > int[] numbers = new int[] {1, 2, 3}; > Dictionary orders = new Dictionary(); > ``` +> > *end example* ### 12.6.3 Local constant declarations @@ -436,6 +463,7 @@ An `else` part is associated with the lexically nearest preceding `if` that is a > ```csharp > if (x) if (y) F(); else G(); > ``` +> > is equivalent to > > ```csharp @@ -451,6 +479,7 @@ An `else` part is associated with the lexically nearest preceding `if` that is a > } > } > ``` +> > *end example* An `if` statement is executed as follows: @@ -511,6 +540,7 @@ A `switch` statement is executed as follows: 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. > *Example*: The example +> > ```csharp > switch (i) > { @@ -525,7 +555,9 @@ If the end point of the statement list of a switch section is reachable, a compi > break; > } > ``` +> > is valid because no switch section has a reachable end point. Unlike C and C++, execution of a switch section is not permitted to “fall through” to the next switch section, and the example +> > ```csharp > switch (i) > { @@ -537,7 +569,9 @@ If the end point of the statement list of a switch section is reachable, a compi > CaseAny(); > } > ``` +> > results in a compile-time error. When execution of a switch section is to be followed by execution of another switch section, an explicit `goto case` or `goto default` statement shall be used: +> > ```csharp > switch (i) > { @@ -552,11 +586,13 @@ If the end point of the statement list of a switch section is reachable, a compi > break; > } > ``` +> > *end example* Multiple labels are permitted in a *switch_section*. > *Example*: The example +> > ```csharp > switch (i) > { @@ -572,11 +608,13 @@ Multiple labels are permitted in a *switch_section*. > break; > } > ``` +> > is valid. The example does not violate the “no fall through” rule because the labels `case 2:` and `default:` are part of the same *switch_section*. *end example* > *Note*: The “no fall through” rule prevents a common class of bugs that occur in C and C++ when `break` statements are accidentally omitted. For example, the sections of the `switch` statement above can be reversed without affecting the behavior of the statement: +> > ```csharp > switch (i) > { @@ -591,11 +629,13 @@ Multiple labels are permitted in a *switch_section*. > goto case 1; > } > ``` +> > *end note* > *Note*: The statement list of a switch section typically ends in a `break`, `goto case`, or `goto default` statement, but any construct that renders the end point of the statement list unreachable is permitted. For example, a `while` statement controlled by the Boolean expression `true` is known to never reach its end point. Likewise, a `throw` or `return` statement always transfers control elsewhere and never reaches its end point. Thus, the following example is valid: +> > ```csharp > switch (i) > { @@ -610,11 +650,13 @@ Multiple labels are permitted in a *switch_section*. > return; > } > ``` +> > *end note* > *Example*: The governing type of a `switch` statement can be the type `string`. For example: +> > ```csharp > void DoCommand(string command) > { @@ -635,6 +677,7 @@ Multiple labels are permitted in a *switch_section*. > } > } > ``` +> > *end example* @@ -840,6 +883,7 @@ An implementation is permitted to implement a given *foreach_statement* differen The placement of `v` inside the `while` loop is important for how it is captured ([§11.16.6.2](expressions.md#111662-captured-outer-variables)) by any anonymous function occurring in the *embedded_statement*. > *Example*: +> > ```csharp > int[] values = { 7, 9, 13 }; > Action f = null; @@ -852,6 +896,7 @@ The placement of `v` inside the `while` loop is important for how it is captured > } > f(); > ``` +> > If `v` in the expanded form were declared outside of the `while` loop, it would be shared among all iterations, and its value after the `for` loop would be the final value, `13`, which is what the invocation of `f` would print. Instead, because each iteration has its own variable `v`, the one captured by `f` in the first iteration will continue to hold the value `7`, which is what will be printed. (Note that earlier versions of C# declared `v` outside of the `while` loop.) *end example* The body of the `finally` block is constructed according to the following steps: @@ -865,6 +910,7 @@ The body of the `finally` block is constructed according to the following steps: ((System.IDisposable)e).Dispose(); } ``` + - Otherwise the `finally` clause is expanded to the semantic equivalent of: ```csharp @@ -877,12 +923,14 @@ The body of the `finally` block is constructed according to the following steps: } } ``` + except that if `E` is a value type, or a type parameter instantiated to a value type, then the conversion of `e` to `System.IDisposable` shall not cause boxing to occur. - Otherwise, if `E` is a sealed type, the `finally` clause is expanded to an empty block: ```csharp finally {} ``` + - Otherwise, the `finally` clause is expanded to: ```csharp @@ -901,6 +949,7 @@ The local variable `d` is not visible to or accessible to any user code. In par The order in which `foreach` traverses the elements of an array, is as follows: For single-dimensional arrays elements are traversed in increasing index order, starting with index 0 and ending with index `Length – 1`. For multi-dimensional arrays, elements are traversed such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left. > *Example*: The following example prints out each value in a two-dimensional array, in element order: +> > ```csharp > using System; > class Test @@ -920,15 +969,19 @@ The order in which `foreach` traverses the elements of an array, is as follows: > } > } > ``` +> > The output produced is as follows: +> > ```console > 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 > ``` +> > *end example* > *Example*: In the following example +> > ```csharp > int[] numbers = { 1, 3, 5, 7, 9 }; > foreach (var n in numbers) @@ -936,6 +989,7 @@ The order in which `foreach` traverses the elements of an array, is as follows: > Console.WriteLine(n); > } > ``` +> > the type of `n` is inferred to be `int`, the iteration type of `numbers`. > *end example* @@ -962,6 +1016,7 @@ When a jump statement occurs within a block, and the target of that jump stateme Execution of jump statements is complicated by the presence of intervening `try` statements. In the absence of such `try` statements, a jump statement unconditionally transfers control from the jump statement to its target. In the presence of such intervening `try` statements, execution is more complex. If the jump statement exits one or more `try` 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 intervening `try` statements have been executed. > *Example*: In the following code +> > ```csharp > using System; > class Test @@ -991,14 +1046,17 @@ Execution of jump statements is complicated by the presence of intervening `try` > } > } > ``` +> > the `finally` blocks associated with two `try` statements are executed before control is transferred to the target of the jump statement. > The output produced is as follows: +> > ```console > Before break > Innermost finally block > Outermost finally block > After break > ``` +> > *end example* ### 12.10.2 The break statement @@ -1062,6 +1120,7 @@ goto_statement The target of a `goto` *identifier* statement is the labeled statement with the given label. If a label with the given name does not exist in the current function member, or if the `goto` statement is not within the scope of the label, a compile-time error occurs. > *Note*: This rule permits the use of a `goto` statement to transfer control *out of* a nested scope, but not *into* a nested scope. In the example +> > ```csharp > using System; > @@ -1095,6 +1154,7 @@ The target of a `goto` *identifier* statement is the labeled statement with the > } > } > ``` +> > a `goto` statement is used to transfer control out of a nested scope. *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. @@ -1222,6 +1282,7 @@ In order to locate a handler for an exception, `catch` clauses are examined in l 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. > *Example*: In the following code +> > ```csharp > using System; > @@ -1256,16 +1317,21 @@ Within a `catch` block, a `throw` statement ([§12.10.6](statements.md#12106-the > } > } > ``` +> > the method `F` catches an exception, writes some diagnostic information to the console, alters the exception variable, and re-throws the exception. The exception that is re-thrown is the original exception, so the output produced is: +> > ```console > Exception in F: G > Exception in Main: G > ``` +> > If the first `catch` block had thrown `e` instead of rethrowing the current exception, the output produced would be as follows: +> > ```console > Exception in F: G > Exception in Main: F > ``` +> > *end example* It is a compile-time error for a `break`, `continue`, or `goto` statement to transfer control out of a `finally` block. When a `break`, `continue`, or `goto` statement occurs in a `finally` block, the target of the statement shall be within the same `finally` block, or otherwise a compile-time error occurs. @@ -1285,6 +1351,7 @@ The statements of a `finally` block are always executed when control leaves a `t 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)). > *Example*: In the following code +> > ```csharp > using System; > @@ -1321,12 +1388,15 @@ If an exception is thrown during execution of a `finally` block, and is not caug > } > } > ``` +> > the method `Method` throws an exception. The first action is to examine the enclosing `catch` clauses, executing any *exception filters*. Then, the `finally` clause in `Method` executes before control transfers to the enclosing matching `catch` clause. The resulting output is: +> > ```console > Filter > Finally > Catch > ``` +> > *end example* The `try` block of a `try` statement is reachable if the `try` statement is reachable. @@ -1512,6 +1582,7 @@ using (ResourceType rN = eN) ``` > *Example*: The example below creates a file named log.txt and writes two lines of text to the file. The example then opens that same file for reading and copies the contained lines of text to the console. +> > ```csharp > using System; > using System.IO; @@ -1536,6 +1607,7 @@ using (ResourceType rN = eN) > } > } > ``` +> > Since the `TextWriter` and `TextReader` classes implement the `IDisposable` interface, the example can use `using` statements to ensure that the underlying file is properly closed following the write or read operations. *end example* ## 12.15 The yield statement @@ -1559,6 +1631,7 @@ There are several restrictions on where a `yield` statement can appear, as descr - It is a compile-time error for a `yield return` statement to appear anywhere in a `try` statement that contains any *catch_clauses*. > *Example*: The following example shows some valid and invalid uses of `yield` statements. +> > ```csharp > delegate IEnumerable D(); > @@ -1595,6 +1668,7 @@ There are several restrictions on where a `yield` statement can appear, as descr > yield return 1; // Error, wrong return type for an iterator block > } > ``` +> > *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. diff --git a/standard/structs.md b/standard/structs.md index fa6a9ddb9..b0d47c877 100644 --- a/standard/structs.md +++ b/standard/structs.md @@ -125,6 +125,7 @@ Structs are value types ([§8.3](types.md#83-value-types)) and are said to have A variable of a struct type directly contains the data of the struct, whereas a variable of a class type contains a reference to an object that contains the data. When a struct `B` contains an instance field of type `A` and `A` is a struct type, it is a compile-time error for `A` to depend on `B` or a type constructed from `B`. `A struct X` *directly depends on* a struct `Y` if `X` contains an instance field of type `Y`. Given this definition, the complete set of structs upon which a struct depends is the transitive closure of the *directly depends on* relationship. > *Example*: +> > ```csharp > struct Node > { @@ -132,6 +133,7 @@ A variable of a struct type directly contains the data of the struct, whereas a > Node next; // error, Node directly depends on itself > } > ``` +> > is an error because `Node` contains an instance field of its own type. Another example > > ```csharp @@ -150,6 +152,7 @@ With classes, it is possible for two variables to reference the same object, and > *Example*: Given the declaration +> > ```csharp > struct Point > { @@ -162,13 +165,16 @@ With classes, it is possible for two variables to reference the same object, and > } > } > ``` +> > the code fragment +> > ```csharp > Point a = new Point(10, 10); > Point b = a; > a.x = 100; > System.Console.WriteLine(b.x); > ``` +> > outputs the value `10`. The assignment of `a` to `b` creates a copy of the value, and `b` is thus unaffected by the assignment to `a.x`. Had `Point` instead been declared as a class, the output would be `100` because `a` and `b` would reference the same object. *end example* ### 15.4.3 Inheritance @@ -194,14 +200,17 @@ When a property or indexer of a struct is the target of an assignment, the insta 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`. > *Example*: Referring to the `Point` struct declared above, the example +> > ```csharp > Point[] a = new Point[100]; > ``` +> > initializes each `Point` in the array to the value produced by setting the `x` and `y` fields to zero. *end example* The default value of a struct corresponds to the value returned by the default constructor of the struct ([§8.3.3](types.md#833-default-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 fields to their default values. > *Note*: Structs should be designed to consider the default initialization state a valid state. In the example +> > ```csharp > using System; > struct KeyValuePair @@ -221,6 +230,7 @@ The default value of a struct corresponds to the value returned by the default c > } > } > ``` +> > the user-defined instance constructor protects against `null` values only where it is explicitly called. In cases where a `KeyValuePair` variable is subject to default value initialization, the `key` and `value` fields will be `null`, and the struct should be prepared to handle this state. *end note* ### 15.4.6 Boxing and unboxing @@ -238,6 +248,7 @@ For further details on boxing and unboxing, see [§10.2.9](conversions.md#1029-b 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. > *Example*: +> > ```csharp > using System; > struct Counter @@ -263,17 +274,21 @@ The meaning of `this` in a struct differs from the meaning of `this` in a class, > static void Main() => Test(); > } > ``` +> > The output of the program is: +> > ```console > 1 > 2 > 3 > ``` +> > Although it is bad style for `ToString` to have side effects, the example demonstrates that no boxing occurred for the three invocations of `x.ToString()`. *end example* Similarly, boxing never implicitly occurs when accessing a member on a constrained type parameter when the member is implemented within the value type. For example, suppose an interface `ICounter` contains a method `Increment`, which can be used to modify a value. If `ICounter` is used as a constraint, the implementation of the `Increment` method is called with a reference to the variable that `Increment` was called on, never a boxed copy. > *Example*: +> > ```csharp > using System; > @@ -306,12 +321,15 @@ Similarly, boxing never implicitly occurs when accessing a member on a constrain > static void Main() => Test(); > } > ``` +> > The first call to `Increment` modifies the value in the variable `x`. This is not equivalent to the second call to `Increment`, which modifies the value in a boxed copy of `x`. Thus, the output of the program is: +> > ```console > 0 > 1 > 1 > ``` +> > *end example* ### 15.4.8 Field initializers @@ -319,6 +337,7 @@ Similarly, boxing never implicitly occurs when accessing a member on a constrain 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. > *Example*: The following +> > ```csharp > struct Point > { @@ -326,6 +345,7 @@ As described in [§15.4.5](structs.md#1545-default-values), the default value of > public int y = 1; // Error, initializer not permitted > } > ``` +> > is in error because the instance field declarations include variable initializers. *end example* ### 15.4.9 Constructors @@ -333,6 +353,7 @@ As described in [§15.4.5](structs.md#1545-default-values), the default value of 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. > *Example*: +> > ```csharp > struct Point > { @@ -345,11 +366,14 @@ Unlike a class, a struct is not permitted to declare a parameterless instance co > } > } > ``` +> > Given the above declaration, the statements +> > ```csharp > Point p1 = new Point(); > Point p2 = new Point(0, 0); > ``` +> > both create a `Point` with `x` and `y` initialized to zero. *end example* A struct instance constructor is not permitted to include a constructor initializer of the form `base(`*argument_list*`)`, where *argument_list* is optional. @@ -359,6 +383,7 @@ The `this` parameter of a struct instance constructor corresponds to an `out` pa If the struct instance constructor specifies a constructor initializer, that initializer is considered a definite assignment to this that occurs prior to the body of the constructor. Therefore, the body itself has no initialization requirements. > *Example*: Consider the instance constructor implementation below: +> > ```csharp > struct Point > { @@ -381,8 +406,10 @@ 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.18.2](expressions.md#11182-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 > struct Point > { @@ -396,6 +423,7 @@ If the struct instance constructor specifies a constructor initializer, that ini > } > } > ``` +> > *end example*] ### 15.4.10 Static constructors diff --git a/standard/types.md b/standard/types.md index 9c756aab4..38338a605 100644 --- a/standard/types.md +++ b/standard/types.md @@ -224,6 +224,7 @@ Like any other instance constructor, the default constructor of a value type is > *Example*: In the code below, variables `i`, `j` and `k` are all initialized to zero. +> > ```csharp > class A > { @@ -235,6 +236,7 @@ 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)). @@ -266,11 +268,13 @@ C# provides a set of predefined `struct` types called the simple types. The simp Because a simple type aliases a struct type, every simple type has members. > *Example*: `int` has the members declared in `System.Int32` and the members inherited from `System.Object`, and the following statements are permitted: +> > ```csharp > int i = int.MaxValue; // System.Int32.MaxValue constant > string s = i.ToString(); // System.Int32.ToString() instance method > string t = 123.ToString(); // System.Int32.ToString() instance method > ``` +> > *end example* @@ -414,6 +418,7 @@ Constructed types can also be used in expressions as simple names ([§11.7.4](ex 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. > *Example*: +> > ```csharp > namespace Widgets > { @@ -432,11 +437,13 @@ 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)). > *Example*: +> > ```csharp > class Outer > { @@ -445,6 +452,7 @@ The detailed rules for name lookup in the *namespace_or_type_name* productions i > public Inner i; // Type of i is Outer.Inner > } > ``` +> > *end example* A non-enum constructed type shall not be used as an *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)). @@ -518,11 +526,13 @@ A compile-time error occurs if one or more of a type parameter’s constraints a Since type parameters are not inherited, constraints are never inherited either. > *Example*: In the following, `D` needs to specify the constraint on its type parameter `T` so that `T` satisfies the constraint imposed by the base `class` `B`. In contrast, `class` `E` need not specify a constraint, because `List` implements `IEnumerable` for any `T`. +> > ```csharp > class B where T: IEnumerable {...} > class D : B where T: IEnumerable {...} > class E : B> {...} > ``` +> > *end example* ## 8.5 Type parameters @@ -557,10 +567,12 @@ As a type, type parameters are purely a compile-time construct. At run-time, eac 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). > *Example*: The following program represents a lambda expression both as executable code and as an expression tree. Because a conversion exists to `Func`, a conversion also exists to `Expression>`: +> > ```csharp > Func del = x => x + 1; // Code > Expression> exp = x => x + 1; // Data > ``` +> > Following these assignments, the delegate `del` references a method that returns `x + 1`, and the expression tree exp references a data structure that describes the expression `x => x + 1`. *end example* `Expression` provides an instance method `Compile` which produces a delegate of type `TDelegate`: diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index 3316b2edc..0a5d9408d 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -36,6 +36,7 @@ unsafe_statement ``` > *Example*: In the following code +> > ```csharp > public unsafe struct Node > { @@ -44,7 +45,9 @@ unsafe_statement > public Node* Right; > } > ``` +> > the `unsafe` modifier specified in the struct declaration causes the entire textual extent of the struct declaration to become an unsafe context. Thus, it is possible to declare the `Left` and `Right` fields to be of a pointer type. The example above could also be written +> > ```csharp > public struct Node > { @@ -53,11 +56,13 @@ unsafe_statement > public unsafe Node* Right; > } > ``` +> > Here, the `unsafe` modifiers in the field declarations cause those declarations to be considered unsafe contexts. *end example* Other than establishing an unsafe context, thus permitting the use of pointer types, the `unsafe` modifier has no effect on a type or a member. > *Example*: In the following code +> > ```csharp > public class A > { @@ -77,9 +82,11 @@ Other than establishing an unsafe context, thus permitting the use of pointer ty > } > } > ``` +> > the unsafe modifier on the `F` method in `A` simply causes the textual extent of `F` to become an unsafe context in which the unsafe features of the language can be used. In the override of `F` in `B`, there is no need to re-specify the `unsafe` modifier—unless, of course, the `F` method in `B` itself needs access to unsafe features. > > The situation is slightly different when a pointer type is part of the method’s signature +> > ```csharp > public unsafe class A > { @@ -91,6 +98,7 @@ Other than establishing an unsafe context, thus permitting the use of pointer ty > public unsafe override void F(char* p) {...} > } > ``` +> > Here, because `F`’s signature includes a pointer type, it can only be written in an unsafe context. However, the unsafe context can be introduced by either making the entire class unsafe, as is the case in `A`, or by including an `unsafe` modifier in the method declaration, as is the case in `B`. *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. @@ -131,9 +139,11 @@ The intuitive rule for mixing of pointers and references is that referents of re For a given implementation, all pointer types shall have the same size and representation. > *Note*: Unlike C and C++, when multiple pointers are declared in the same declaration, in C# the `*` is written along with the underlying type only, not as a prefix punctuator on each pointer name. For example: +> > ```csharp > int* pi, pj; // NOT as int *pi, *pj; > ``` +> > *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. @@ -153,6 +163,7 @@ A *pointer_type* cannot be used as a type of a subexpression of a dynamically bo A *pointer_type* may be used as the type of a volatile field ([§14.5.4](classes.md#1454-volatile-fields)). > *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: +> > ```csharp > using System; > @@ -185,11 +196,13 @@ A *pointer_type* may be used as the type of a volatile field ([§14.5.4](classes > } > } > ``` +> > *end note* A method can return a value of some type, and that type can be a pointer. > *Example*: When given a pointer to a contiguous sequence of `int`s, that sequence’s element count, and some other `int` value, the following method returns the address of that value in that sequence, if a match occurs; otherwise it returns `null`: +> > ```csharp > unsafe static int* Find(int* pi, int size, int value) > { @@ -204,6 +217,7 @@ A method can return a value of some type, and that type can be a pointer. > return null; > } > ``` +> > *end example* In an unsafe context, several constructs are available for operating on pointers: @@ -261,6 +275,7 @@ Conversions between two pointer types never change the actual pointer value. In When one pointer type is converted to another, if the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined if the result is dereferenced. In general, the concept “correctly aligned” is transitive: if a pointer to type `A` is correctly aligned for a pointer to type `B`, which, in turn, is correctly aligned for a pointer to type `C`, then a pointer to type `A` is correctly aligned for a pointer to type `C`. > *Example*: Consider the following case in which a variable having one type is accessed via a pointer to a different type: +> > ```csharp > char c = 'A'; > char* pc = &c; @@ -269,11 +284,13 @@ When one pointer type is converted to another, if the resulting pointer is not c > int i = *pi; // undefined > *pi = 123456; // undefined > ``` +> > *end example* When a pointer type is converted to a pointer to `byte`, the result points to the lowest addressed `byte` of the variable. Successive increments of the result, up to the size of the variable, yield pointers to the remaining bytes of that variable. > *Example*: The following method displays each of the eight bytes in a `double` as a hexadecimal value: +> > ```csharp > using System; > class Test @@ -293,6 +310,7 @@ When a pointer type is converted to a pointer to `byte`, the result points to th > } > } > ``` +> > Of course, the output produced depends on endianness. *end example* Mappings between pointers and integers are implementation-defined. @@ -381,6 +399,7 @@ In a pointer member access of the form `P->I`, `P` shall be an expression of a p 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). > *Example*: In the following code +> > ```csharp > using System; > @@ -406,7 +425,9 @@ A pointer member access of the form `P->I` is evaluated exactly as `(*P).I`. For > } > } > ``` +> > the `->` operator is used to access fields and invoke a method of a struct through a pointer. Because the operation `P->I` is precisely equivalent to `(*P).I`, the `Main` method could equally well have been written: +> > ```csharp > class Test > { @@ -423,6 +444,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 @@ -440,6 +462,7 @@ In a pointer element access of the form `P[E]`, `P` shall be an expression of a 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). > *Example*: In the following code +> > ```csharp > class Test > { @@ -453,7 +476,9 @@ A pointer element access of the form `P[E]` is evaluated exactly as `*(P + E)`. > } > } > ``` +> > a pointer element access is used to initialize the character buffer in a `for` loop. Because the operation `P[E]` is precisely equivalent to `*(P + E)`, the example could equally well have been written: +> > ```csharp > class Test > { @@ -470,6 +495,7 @@ A pointer element access of the form `P[E]` is evaluated exactly as `*(P + E)`. > } > } > ``` +> > *end example* The pointer element access operator does not check for out-of-bounds errors and the behavior when accessing an out-of-bounds element is undefined. @@ -493,6 +519,7 @@ Given an expression `E` which is of a type `T` and is classified as a fixed vari 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. > *Example*: In the following code +> > ```csharp > using System; > @@ -510,6 +537,7 @@ The `&` operator does not require its argument to be definitely assigned, but fo > } > } > ``` +> > `i` is considered definitely assigned following the `&i` operation used to initialize `p`. The assignment to `*p` in effect initializes `i`, but the inclusion of this initialization is the responsibility of the programmer, and no compile-time error would occur if the assignment was removed. *end example* @@ -558,6 +586,7 @@ Given an expression `P` of a pointer type `T*` and an expression `N` of type `in Given two expressions, `P` and `Q`, of a pointer type `T*`, the expression `P – Q` computes the difference between the addresses given by `P` and `Q` and then divides that difference by `sizeof(T)`. The type of the result is always `long`. In effect, `P - Q` is computed as `((long)(P) - (long)(Q)) / sizeof(T)`. > *Example*: +> > ```csharp > using System; > class Test @@ -575,11 +604,14 @@ Given two expressions, `P` and `Q`, of a pointer type `T*`, the expression `P > } > } > ``` +> > which produces the output: +> > ```console > p - q = -14 > q - p = 14 > ``` +> > *end example* 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. @@ -652,6 +684,7 @@ It is the programmer’s responsibility to ensure that pointers created by fixed Fixed objects can cause fragmentation of the heap (because they can’t be moved). For that reason, objects should be fixed only when absolutely necessary and then only for the shortest amount of time possible. > *Example*: The example +> > ```csharp > class Test > { @@ -677,6 +710,7 @@ Fixed objects can cause fragmentation of the heap (because they can’t be moved > } > } > ``` +> > demonstrates several uses of the `fixed` statement. The first statement fixes and obtains the address of a static field, the second statement fixes and obtains the address of an instance field, and the third statement fixes and obtains the address of an array element. In each case, it would have been an error to use the regular `&` operator since the variables are all classified as moveable variables. > > The third and fourth `fixed` statements in the example above produce identical results. In general, for an array instance `a`, specifying `a[0]` in a `fixed` statement is the same as simply specifying `a`. *end example* @@ -686,6 +720,7 @@ In an unsafe context, array elements of single-dimensional arrays are stored in 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. > *Example*: +> > ```csharp > using System; > @@ -718,7 +753,9 @@ Within a `fixed` statement that obtains a pointer `p` to an array instance `a`, > } > } > ``` +> > which produces the output: +> > ```console > [0,0,0] = 0 [0,0,1] = 1 [0,0,2] = 2 [0,0,3] = 3 > [0,1,0] = 4 [0,1,1] = 5 [0,1,2] = 6 [0,1,3] = 7 @@ -727,11 +764,14 @@ Within a `fixed` statement that obtains a pointer `p` to an array instance `a`, > [1,1,0] = 16 [1,1,1] = 17 [1,1,2] = 18 [1,1,3] = 19 > [1,2,0] = 20 [1,2,1] = 21 [1,2,2] = 22 [1,2,3] = 23 > ``` +> > *end example* + > *Example*: In the following code +> > ```csharp > class Test > { @@ -753,11 +793,13 @@ Within a `fixed` statement that obtains a pointer `p` to an array instance `a`, > } > } > ``` +> > a `fixed` statement is used to fix an array so its address can be passed to a method that takes a pointer. *end example* A `char*` value produced by fixing a string instance always points to a null-terminated string. Within a fixed statement that obtains a pointer `p` to a string instance `s`, the pointer values ranging from `p` to `p + s.Length ‑ 1` represent addresses of the characters in the string, and the pointer value `p + s.Length` always points to a null character (the character with value ‘\0’). > *Example*: +> > ```csharp > class Test > { @@ -779,6 +821,7 @@ A `char*` value produced by fixing a string instance always points to a null-ter > } > } > ``` +> > *end example* Modifying objects of managed type through fixed pointers can result in undefined behavior. @@ -840,13 +883,16 @@ The elements of a fixed-size buffer shall be laid out sequentially in memory. A fixed-size buffer declaration that declares multiple fixed-size buffers is equivalent to multiple declarations of a single fixed-size buffer declaration with the same attributes, and element types. > *Example*: +> > ```csharp > unsafe struct A > { > public fixed int x[5], y[10], z[100]; > } > ``` +> > is equivalent to +> > ```csharp > unsafe struct A > { @@ -855,6 +901,7 @@ A fixed-size buffer declaration that declares multiple fixed-size buffers is equ > public fixed int z[100]; > } > ``` +> > *end example* ### 22.8.3 Fixed-size buffers in expressions @@ -875,6 +922,7 @@ In a member access of the form `E.I`, if `E` is of a struct type and a member lo 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. > *Example*: The following declares and uses a struct with a fixed-size buffer member. +> > ```csharp > unsafe struct Font > { @@ -909,6 +957,7 @@ The subsequent elements of the fixed-size buffer can be accessed using pointer o > } > } > ``` +> > *end example* ### 22.8.4 Definite assignment checking @@ -944,6 +993,7 @@ All stack-allocated memory blocks created during the execution of a function mem > *Example*: In the following code +> > ```csharp > using System; > @@ -980,6 +1030,7 @@ 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. *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. diff --git a/standard/variables.md b/standard/variables.md index 6b17afe4c..465850de5 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -15,6 +15,7 @@ As described in the following subclauses, variables are either ***initially assi 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. > *Example*: In the following code +> > ```csharp > class A > { @@ -28,6 +29,7 @@ C# defines seven categories of variables: static variables, instance variables, > } > } > ``` +> > `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* ### 9.2.2 Static variables @@ -129,6 +131,7 @@ The lifetime of a local variable is the portion of program execution during whic 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. > *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: +> > ```csharp > goto L; > @@ -136,6 +139,7 @@ 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*. *end note* ## 9.3 Default values @@ -403,6 +407,7 @@ 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. +> > ```csharp > class A > { @@ -434,6 +439,7 @@ finally «finally_block» > } > } > ``` +> > *end example* #### 9.4.4.17 Foreach statements @@ -484,6 +490,7 @@ For a `constant` expression with value true: - Otherwise *v* is “definitely assigned after false expression” after the expression. > *Example*: +> > ```csharp > int x; > if (true) {} @@ -492,6 +499,7 @@ For a `constant` expression with value true: > Console.WriteLine(x); > } > ``` +> > *end example* For a constant expression with value `false`: @@ -500,6 +508,7 @@ For a constant expression with value `false`: - Otherwise *v* is “definitely assigned after true expression” after the expression. > *Example*: +> > ```csharp > int x; > if (false) @@ -507,6 +516,7 @@ For a constant expression with value `false`: > Console.WriteLine(x); > } > ``` +> > *end example* For all other constant expressions, the definite assignment state of *v* after the expression is the same as the definite assignment state of *v* before the expression. @@ -562,6 +572,7 @@ For an expression *expr* of the form: - 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*. > *Example*: In the following code +> > ```csharp > class A > { @@ -572,6 +583,7 @@ For an expression *expr* of the form: > } > } > ``` +> > the variable `x` is considered definitely assigned after `arr[x = 1]` is evaluated as the left hand side of the second simple assignment. *end example* #### 9.4.4.26 && expressions @@ -590,6 +602,7 @@ For an expression *expr* of the form: - Otherwise, the state of *v* after *expr* is not definitely assigned. > *Example*: In the following code +> > ```csharp > class A > { @@ -608,6 +621,7 @@ For an expression *expr* of the form: > } > } > ``` +> > the variable `i` is considered definitely assigned in one of the embedded statements of an `if` statement but not in the other. In the `if` statement in method `F`, the variable `i` is definitely assigned in the first embedded statement because execution of the expression `(i = y)` always precedes execution of this embedded statement. In contrast, the variable `i` is not definitely assigned in the second embedded statement, since `x >= 0` might have tested false, resulting in the variable `i`’s being unassigned. *end example* #### 9.4.4.27 || expressions @@ -626,6 +640,7 @@ For an expression *expr* of the form: - Otherwise, the state of *v* after *expr* is not definitely assigned. > *Example*: In the following code +> > ```csharp > class A > { @@ -644,6 +659,7 @@ For an expression *expr* of the form: > } > } > ``` +> > the variable `i` is considered definitely assigned in one of the embedded statements of an `if` statement but not in the other. In the `if` statement in method `G`, the variable `i` is definitely assigned in the second embedded statement because execution of the expression `(i = y)` always precedes execution of this embedded statement. In contrast, the variable `i` is not definitely assigned in the first embedded statement, since `x >= 0` might have tested true, resulting in the variable `i`’s being unassigned. *end example* #### 9.4.4.28 ! expressions @@ -695,6 +711,7 @@ For a *lambda_expression* or *anonymous_method_expression* *expr* with a body (e - The definite assignment state of an outer variable *v* after *expr* is the same as the state of *v* before *expr*. > *Example*: The example +> > ```csharp > delegate bool Filter(int i); > void F() @@ -706,11 +723,13 @@ For a *lambda_expression* or *anonymous_method_expression* *expr* with a body (e > DoWork(f); > } > ``` +> > generates a compile-time error since max is not definitely assigned where the anonymous function is declared. *end example* > *Example*: The example +> > ```csharp > delegate void D(); > void F() @@ -722,6 +741,7 @@ For a *lambda_expression* or *anonymous_method_expression* *expr* with a body (e > Console.WriteLine(n); > } > ``` +> > also generates a compile-time error since the assignment to `n` in the anonymous function has no affect on the definite assignment state of `n` outside the anonymous function. *end example* ## 9.5 Variable references