From 192f5e6ca50156833e6dd3a00d30653893d8da8e Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 14 Oct 2021 10:46:45 -0400 Subject: [PATCH 1/7] fix formatting nits --- standard/expressions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index f47f06694..1ea5ff2d8 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1490,7 +1490,7 @@ If the *primary_no_array_creation_expression* of an *element_access* is a value #### 12.7.7.2 Array access -For an array access, the *primary_no_array_creation_expression* of the *element_access* shall be a value of an *array_type*. Furthermore, the *argument_list* of an array access is not allowed to contain named arguments.The number of expressions in the *argument_list* shall be the same as the rank of the *array_type*, and each expression shall be of type `int, uint, long, ulong,` or shall be implicitly convertible to one or more of these types. +For an array access, the *primary_no_array_creation_expression* of the *element_access* shall be a value of an *array_type*. Furthermore, the *argument_list* of an array access is not allowed to contain named arguments. The number of expressions in the *argument_list* shall be the same as the rank of the *array_type*, and each expression shall be of type `int`, `uint`, `long`, or `ulong,` or shall be implicitly convertible to one or more of these types. The result of evaluating an array access is a variable of the element type of the array, namely the array element selected by the value(s) of the expression(s) in the *argument_list*. @@ -1499,8 +1499,8 @@ The run-time processing of an array access of the form `P[A]`, where `P` is a *p - `P` is evaluated. If this evaluation causes an exception, no further steps are executed. - The index expressions of the *argument_list* are evaluated in order, from left to right. Following evaluation of each index expression, an implicit conversion ([§11.2](conversions.md#112-implicit-conversions)) to one of the following types is performed: `int`, `uint`, `long`, `ulong`. The first type in this list for which an implicit conversion exists is chosen. For instance, if the index expression is of type `short` then an implicit conversion to `int` is performed, since implicit conversions from `short` to `int` and from `short` to `long` are possible. If evaluation of an index expression or the subsequent implicit conversion causes an exception, then no further index expressions are evaluated and no further steps are executed. - The value of `P` is checked to be valid. If the value of `P` is `null`, a `System.NullReferenceException` is thrown and no further steps are executed. -- The value of each expression in the *argument_list* is checked against the actual bounds of each dimension of the `array` instance referenced by `P`. If one or more values are out of range, a `System.IndexOutOfRangeException` is thrown and no further steps are executed. -- The location of the `array` element given by the index expression(s) is computed, and this location becomes the result of the `array` access. +- The value of each expression in the *argument_list* is checked against the actual bounds of each dimension of the array instance referenced by `P`. If one or more values are out of range, a `System.IndexOutOfRangeException` is thrown and no further steps are executed. +- The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access. #### 12.7.7.3 Indexer access From a94d63574977c3fc004d48336ab6f689df94adfb Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 14 Oct 2021 17:08:03 -0400 Subject: [PATCH 2/7] fix formatting nits --- standard/classes.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index d02c8598d..cdac50b60 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -172,7 +172,7 @@ interface_type_list #### 15.2.4.2 Base classes -When a *class_type* is included in the *class_base*, it specifies the direct base class of the class being declared. If a non-partial class declaration has no *class_base*, or if the *class_base* lists only interface types, the direct base class is assumed to be `object`. When a partial class declaration includes a base class specification, that base class specification shall reference the same type as all other parts of that partial type that include a base class specification. If no part of a partial class includes a base class specification, the base class is object. A class inherits members from its direct base class, as described in [§15.3.4](classes.md#1534-inheritance). +When a *class_type* is included in the *class_base*, it specifies the direct base class of the class being declared. If a non-partial class declaration has no *class_base*, or if the *class_base* lists only interface types, the direct base class is assumed to be `object`. When a partial class declaration includes a base class specification, that base class specification shall reference the same type as all other parts of that partial type that include a base class specification. If no part of a partial class includes a base class specification, the base class is `object`. A class inherits members from its direct base class, as described in [§15.3.4](classes.md#1534-inheritance). > *Example*: In the following code > ```csharp @@ -1584,7 +1584,7 @@ A variable initializer for an instance field cannot reference the instance being ### 15.6.1 General -A ***method*** is a member that implements a computation or action that can be performed by an `object` or class. Methods are declared using *method_declaration*s: +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 @@ -2659,7 +2659,7 @@ For `abstract` and `extern` properties, the *accessor_body* for each accessor sp A `get` accessor corresponds to a parameterless method with a return value of the property type. Except as the target of an assignment, when a property is referenced in an expression, the `get` accessor of the property is invoked to compute the value of the property ([§12.2.2](expressions.md#1222-values-of-expressions)). The body of a `get` accessor shall conform to the rules for value-returning methods described in [§15.6.11](classes.md#15611-method-body). In particular, all `return` statements in the body of a `get` accessor shall specify an expression that is implicitly convertible to the property type. Furthermore, the endpoint of a `get` accessor shall not be reachable. -A `set` accessor corresponds to a method with a single value parameter of the property type and a `void` return type. The implicit parameter of a `set` accessor is always named value. When a property is referenced as the target of an assignment ([§12.18](expressions.md#1218-assignment-operators)), or as the operand of `++` or `–-` ([§12.7.10](expressions.md#12710-postfix-increment-and-decrement-operators), [§12.8.6](expressions.md#1286-prefix-increment-and-decrement-operators)), the `set` accessor is invoked with an argument that provides the new value ([§12.18.2](expressions.md#12182-simple-assignment)). The body of a `set` accessor shall conform to the rules for `void` methods described in [§15.6.11](classes.md#15611-method-body). In particular, return statements in the `set` accessor body are not permitted to specify an expression. Since a `set` accessor implicitly has a parameter named `value`, it is a compile-time error for a local variable or constant declaration in a `set` accessor to have that name. +A `set` accessor corresponds to a method with a single value parameter of the property type and a `void` return type. The implicit parameter of a `set` accessor is always named `value`. When a property is referenced as the target of an assignment ([§12.18](expressions.md#1218-assignment-operators)), or as the operand of `++` or `–-` ([§12.7.10](expressions.md#12710-postfix-increment-and-decrement-operators), [§12.8.6](expressions.md#1286-prefix-increment-and-decrement-operators)), the `set` accessor is invoked with an argument that provides the new value ([§12.18.2](expressions.md#12182-simple-assignment)). The body of a `set` accessor shall conform to the rules for `void` methods described in [§15.6.11](classes.md#15611-method-body). In particular, return statements in the `set` accessor body are not permitted to specify an expression. Since a `set` accessor implicitly has a parameter named `value`, it is a compile-time error for a local variable or constant declaration in a `set` accessor to have that name. Based on the presence or absence of the `get` and `set` accessors, a property is classified as follows: @@ -2989,7 +2989,7 @@ An overriding property declaration may include the `sealed` modifier. Use of thi Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in [§15.6.4](classes.md#1564-virtual-methods), [§15.6.5](classes.md#1565-override-methods), [§15.6.6](classes.md#1566-sealed-methods), and [§15.6.7](classes.md#1567-abstract-methods) apply as if accessors were methods of a corresponding form: - A `get` accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property. -- A `set` accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property. +- A `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 @@ -3345,7 +3345,7 @@ Indexers and properties are very similar in concept, but differ in the following - A property is accessed through a *simple_name* ([§12.7.3](expressions.md#1273-simple-names)) or a *member_access* ([§12.7.5](expressions.md#1275-member-access)), whereas an indexer element is accessed through an *element_access* ([§12.7.7.3](expressions.md#12773-indexer-access)). - A property can be a static member, whereas an indexer is always an instance member. - A `get` accessor of a property corresponds to a method with no parameters, whereas a `get` accessor of an indexer corresponds to a method with the same formal parameter list as the indexer. -- A `set` accessor of a property corresponds to a method with a single parameter named value, whereas a `set` accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named value. +- A `set` accessor of a property corresponds to a method with a single parameter named `value`, whereas a `set` accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named `value`. - It is a compile-time error for an indexer accessor to declare a local variable or local constant with the same name as an indexer parameter. - In an overriding property declaration, the inherited property is accessed using the syntax `base.P`, where `P` is the property name. In an overriding indexer declaration, the inherited indexer is accessed using the syntax `base[E]`, where `E` is a comma-separated list of expressions. - There is no concept of an "automatically implemented indexer". It is an error to have a non-abstract, non-external indexer with semicolon accessors. @@ -4258,9 +4258,9 @@ It is a compile-time error for the formal parameter list of an async function to The *return_type* of an async method shall be either `void` or a ***task type***. The task types are `System.Threading.Tasks.Task` and types constructed from `System.Threading.Tasks.Task`. For the sake of brevity, in this clause these types are referenced as `Task` and `Task`, respectively. An async method returning a task type is said to be ***task-returning***. -The exact definition of the task types is implementation-defined, but from the language's point of view, a task type is in one of the states *incomplete*, *succeeded* or *faulted*. A *faulted* task records a pertinent exception. A *succeeded* `Task<*T*>` records a result of type `*T*`. Task types are awaitable, and tasks can therefore be the operands of await expressions ([§12.8.8](expressions.md#1288-await-expressions)). +The exact definition of the task types is implementation-defined, but from the language's point of view, a task type is in one of the states *incomplete*, *succeeded* or *faulted*. A *faulted* task records a pertinent exception. A *succeeded* `Task` records a result of type `T`. Task types are awaitable, and tasks can therefore be the operands of await expressions ([§12.8.8](expressions.md#1288-await-expressions)). -An async function has the ability to suspend evaluation by means of await expressions ([§12.8.8](expressions.md#1288-await-expressions)) in its body. Evaluation may later be resumed at the point of the suspending await expression by means of a ***resumption delegate***. The resumption delegate is of type `System.Action`, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. The ***current caller*** of an async function invocation is the original caller if the function invocation has never been suspended or the most recent caller of the resumption delegate otherwise. +An async function has the ability to suspend evaluation by means of await expressions in its body. Evaluation may later be resumed at the point of the suspending await expression by means of a ***resumption delegate***. The resumption delegate is of type `System.Action`, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. The ***current caller*** of an async function invocation is the original caller if the function invocation has never been suspended or the most recent caller of the resumption delegate otherwise. ### 15.15.2 Evaluation of a task-returning async function From a96f5aa8615ffb3ea2c97f7459aaabfcd1f16238 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 14 Oct 2021 17:10:15 -0400 Subject: [PATCH 3/7] fix formatting nits --- standard/interfaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 02d4078e8..9a9f2e73c 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -18,7 +18,7 @@ interface_declaration ; ``` -An *interface_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *interface_modifier*s ([§18.2.2](interfaces.md#1822-interface-modifiers)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword interface and an *identifier* that names the interface, followed by an optional *variant_type_parameter_list* specification ([§18.2.3](interfaces.md#1823-variant-type-parameter-lists)), followed by an optional *interface_base* specification ([§18.2.4](interfaces.md#1824-base-interfaces)), followed by an optional *type_parameter_constraints_clause*s specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by an *interface_body* ([§18.3](interfaces.md#183-interface-body)), optionally followed by a semicolon. +An *interface_declaration* consists of an optional set of *attributes* ([§22](attributes.md#22-attributes)), followed by an optional set of *interface_modifier*s ([§18.2.2](interfaces.md#1822-interface-modifiers)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-declarations)), followed by the keyword `interface` and an *identifier* that names the interface, followed by an optional *variant_type_parameter_list* specification ([§18.2.3](interfaces.md#1823-variant-type-parameter-lists)), followed by an optional *interface_base* specification ([§18.2.4](interfaces.md#1824-base-interfaces)), followed by an optional *type_parameter_constraints_clause*s specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by an *interface_body* ([§18.3](interfaces.md#183-interface-body)), optionally followed by a semicolon. An interface declaration shall not supply a *type_parameter_constraints_clause*s unless it also supplies a *type_parameter_list*. @@ -220,7 +220,7 @@ The inherited members of an interface are specifically not part of the declarati If a `new` modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. This warning is suppressed by removing the `new` modifier. -> *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§18.4](interfaces.md#184-interface-members)). However, the members in class object are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* +> *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§18.4](interfaces.md#184-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§8.3](basic-concepts.md#83-declarations)), and the scope of each member ([§8.7](basic-concepts.md#87-scopes)) extends to the bodies of all the parts. From ad4040bf45150096e6b663b69ddb2b5a8835215a Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sat, 12 Mar 2022 14:33:48 -0500 Subject: [PATCH 4/7] Fix formatting nits --- standard/classes.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index cdac50b60..2f2e0daac 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -216,7 +216,7 @@ 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 [§8.8](basic-concepts.md#88-namespace-and-type-names)) `Z` is not considered to have a member `Y`. *end example* +> 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 [§8.8](basic-concepts.md#88-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. @@ -478,7 +478,7 @@ The ***dynamic erasure*** of a type `C` is type `Cₓ` constructed as follows: - If `C` `Cₓ`is a constructed type `G` with type arguments `A¹, ..., Aⁿ` then `Cₓ` is the constructed type `G`. - If `C` is an array type `E[]` then `Cₓ` is the array type `Eₓ[]`. - If `C` is a pointer type `E*` then `Cₓ` is the pointer type `Eₓ*`. -- If `C` is dynamic then `Cₓ` is object. +- If `C` is dynamic then `Cₓ` is `object`. - Otherwise, `Cₓ` is `C`. The ***effective base class*** of a type parameter `T` is defined as follows: @@ -496,7 +496,7 @@ Then - If `T` has the value type constraint, its effective base class is `System.ValueType`. - Otherwise, if `R` is empty then the effective base class is `object`. -- Otherwise, the effective base class of `T` is the most-encompassed type ([§11.5.3](conversions.md#1153-evaluation-of-user-defined-conversions)) of set `R`. If the set has no encompassed type, the effective base class of `T` is object. The consistency rules ensure that the most-encompassed type exists. +- Otherwise, the effective base class of `T` is the most-encompassed type ([§11.5.3](conversions.md#1153-evaluation-of-user-defined-conversions)) of set `R`. If the set has no encompassed type, the effective base class of `T` is `object`. The consistency rules ensure that the most-encompassed type exists. If the type parameter is a method type parameter whose constraints are inherited from the base method the effective base class is calculated after type substitution. @@ -3174,7 +3174,7 @@ Within the program text of the class or struct that contains the declaration of > ``` > 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 ([§10.4.4.19](variables.md#104419-lock-statements)) in the containing object for an instance event, or the type object ([§12.7.11.7](expressions.md#127117-anonymous-object-creation-expressions)) for a static event. +When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock ([§10.4.4.19](variables.md#104419-lock-statements)) in the containing object for an instance event, or the type `object` ([§12.7.11.7](expressions.md#127117-anonymous-object-creation-expressions)) for a static event. > *Note*: Thus, an instance event declaration of the form: > ```csharp @@ -3640,7 +3640,7 @@ In cases where a pre-defined conversion exists between two types, any user-defin - If either `S` or `T` is an interface type, user-defined implicit conversions from `S` to `T` are ignored. - Otherwise, user-defined implicit conversions from `S` to `T` are still considered. -For all types but object, the operators declared by the `Convertible` type above do not conflict with pre-defined conversions. +For all types but `object`, the operators declared by the `Convertible` type above do not conflict with pre-defined conversions. > *Example*: > ```csharp @@ -3651,7 +3651,7 @@ For all types but object, the operators declared by the `Convertible` type ab > n = (Convertible)i; // User-defined implicit conversion > } > ``` -> However, for type object, pre-defined conversions hide the user-defined conversions in all cases but one: +> However, for type `object`, pre-defined conversions hide the user-defined conversions in all cases but one: > ```csharp > void F(object o, Convertible n) { > o = n; // Pre-defined boxing conversion @@ -4152,7 +4152,7 @@ The ***enumerable interfaces*** are the non-generic interface `System.Collection An iterator produces a sequence of values, all of the same type. This type is called the ***yield type*** of the iterator. -- The yield type of an iterator that returns `IEnumerator` or `IEnumerable` is object. +- The yield type of an iterator that returns `IEnumerator` or `IEnumerable` is `object`. - The yield type of an iterator that returns `IEnumerator` or `IEnumerable` is `T`. ### 15.14.5 Enumerator objects From c846c85f7b9ed90652d20087463c2d49e4ca1b9f Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sat, 12 Mar 2022 14:37:15 -0500 Subject: [PATCH 5/7] Fix formatting nits --- standard/conversions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/conversions.md b/standard/conversions.md index f8afcdb30..47fe0031a 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -452,7 +452,7 @@ For a *type_parameter* `T` that is *not* known to be a reference type ([§15.2.5 For a *type_parameter* `T` that is *not* known to be a reference type ([§15.2.5](classes.md#1525-type-parameter-constraints)), the following explicit conversions exist: -- From `T` to any *interface_type* `I` provided there is not already an implicit conversion from `T` to `I`. This conversion consists of an implicit boxing conversion ([§11.2.8](conversions.md#1128-boxing-conversions)) from `T` to object followed by an explicit reference conversion from object to `I`. At run-time, if `T` is a value type, the conversion is executed as a boxing conversion followed by an explicit reference conversion. At run-time, if `T` is a reference type, the conversion is executed as an explicit reference conversion. +- From `T` to any *interface_type* `I` provided there is not already an implicit conversion from `T` to `I`. This conversion consists of an implicit boxing conversion ([§11.2.8](conversions.md#1128-boxing-conversions)) from `T` to `object` followed by an explicit reference conversion from `object` to `I`. At run-time, if `T` is a value type, the conversion is executed as a boxing conversion followed by an explicit reference conversion. At run-time, if `T` is a reference type, the conversion is executed as an explicit reference conversion. - From a type parameter `U` to `T` provided that `T` depends on `U` ([§15.2.5](classes.md#1525-type-parameter-constraints)). At run-time, if `T` is a value type and `U` is a reference type, the conversion is executed as an unboxing conversion. At run-time, if both `T` and `U` are value types, then `T` and `U` are necessarily the same type and no conversion is performed. At run-time, if `T` is a reference type, then `U` is necessarily also a reference type and the conversion is executed as an explicit reference conversion or identity conversion. In all cases, the rules ensure that a conversion is executed as an unboxing conversion if and only if at run-time the conversion is from a reference type to a value type. From 9e627058c15ac7d5b8ec23ea9944244f0f455e27 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sat, 12 Mar 2022 14:40:16 -0500 Subject: [PATCH 6/7] Fix formatting nits --- standard/expressions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 1ea5ff2d8..46389121a 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -2116,7 +2116,7 @@ The second form of *typeof_expression* consists of a `typeof` keyword followed b When the operand of a *typeof_expression* is a sequence of tokens that satisfies the grammars of both *unbound_type_name* and *type_name*, namely when it contains neither a *generic_dimension_specifier* nor a *type_argument_list*, the sequence of tokens is considered to be a *type_name*. The meaning of an *unbound_type_name* is determined as follows: -- Convert the sequence of tokens to a *type_name* by replacing each *generic_dimension_specifier* with a *type_argument_list* having the same number of commas and the keyword object as each *type_argument*. +- Convert the sequence of tokens to a *type_name* by replacing each *generic_dimension_specifier* with a *type_argument_list* having the same number of commas and the keyword `object` as each *type_argument*. - Evaluate the resulting *type_name*, while ignoring all type parameter constraints. - The *unbound_type_name* resolves to the unbound generic type associated with the resulting constructed type ([§9.4](types.md#94-constructed-types)). @@ -2124,7 +2124,7 @@ The result of the *typeof_expression* is the `System.Type` object for the result The third form of *typeof_expression* consists of a `typeof` keyword followed by a parenthesized `void` keyword. The result of an expression of this form is the `System.Type` object that represents the absence of a type. The type object returned by `typeof(void)` is distinct from the type object returned for any type. -> *Note*: This special type object is useful in class libraries that allow reflection onto methods in the language, where those methods wish to have a way to represent the return type of any method, including `void` methods, with an instance of `System.Type`. *end note* +> *Note*: This special type `object` is useful in class libraries that allow reflection onto methods in the language, where those methods wish to have a way to represent the return type of any method, including `void` methods, with an instance of `System.Type`. *end note* The `typeof` operator can be used on a type parameter. The result is the `System.Type` object for the run-time type that was bound to the type parameter. The `typeof` operator can also be used on a constructed type or an unbound generic type ([§9.4.4](types.md#944-bound-and-unbound-types)). The `System.Type` object for an unbound generic type is not the same as the `System.Type` object of the instance type ([§15.3.2](classes.md#1532-the-instance-type)). The instance type is always a closed constructed type at run-time so its `System.Type` object depends on the run-time type arguments in use. The unbound generic type, on the other hand, has no type arguments, and yields the same `System.Type` object regardless of runtime type arguments. @@ -3613,7 +3613,7 @@ Unless one of these conditions is true, a binding-time error occurs. > - The predefined reference type equality operators do not permit value type operands to be compared (except when type parameters are compared to `null`, which is handled specially). > - Operands of predefined reference type equality operators are never boxed. It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references. > -> For an operation of the form `x == y` or `x != y`, if any applicable user-defined `operator ==` or `operator !=` exists, the operator overload resolution rules ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) will select that operator instead of the predefined reference type equality operator. It is always possible to select the predefined reference type equality operator by explicitly casting one or both of the operands to type object. *end note* +> For an operation of the form `x == y` or `x != y`, if any applicable user-defined `operator ==` or `operator !=` exists, the operator overload resolution rules ([§12.4.5](expressions.md#1245-binary-operator-overload-resolution)) will select that operator instead of the predefined reference type equality operator. It is always possible to select the predefined reference type equality operator by explicitly casting one or both of the operands to type `object`. *end note* > *Example*: The following example checks whether an argument of an unconstrained type parameter type is `null`. > ```csharp From 153d0029f8051626b08c70c204bcd6453b0ae4e3 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sat, 12 Mar 2022 14:42:22 -0500 Subject: [PATCH 7/7] Fix formatting nits --- standard/statements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/statements.md b/standard/statements.md index 74c4e80eb..7e379365a 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -757,7 +757,7 @@ The compile-time processing of a `foreach` statement first determines the ***col - Otherwise, check for an enumerable interface: - If among all the types `Tᵢ` for which there is an implicit conversion from `X` to `IEnumerable<ᵢ>`, there is a unique type `T` such that `T` is not dynamic and for all the other `Tᵢ` there is an implicit conversion from `IEnumerable` to `IEnumerable`, then the collection type is the interface `IEnumerable`, the enumerator type is the interface `IEnumerator`, and the iteration type is `T`. - Otherwise, if there is more than one such type `T`, then an error is produced and no further steps are taken. - - Otherwise, if there is an implicit conversion from `X` to the `System.Collections.IEnumerable` interface, then the collection type is this interface, the enumerator type is the interface `System.Collections.IEnumerator`, and the iteration type is object. + - Otherwise, if there is an implicit conversion from `X` to the `System.Collections.IEnumerable` interface, then the collection type is this interface, the enumerator type is the interface `System.Collections.IEnumerator`, and the iteration type is `object`. - Otherwise, an error is produced and no further steps are taken. The above steps, if successful, unambiguously produce a collection type `C`, enumerator type `E` and iteration type `T`. A `foreach` statement of the form