ВыраженияExpressions

Выражение — это последовательность операторов и операндов.An expression is a sequence of operators and operands. В этой главе определяется синтаксис, порядок вычисления операндов и операторов, а также значение выражений.This chapter defines the syntax, order of evaluation of operands and operators, and meaning of expressions.

Классификации выраженийExpression classifications

Выражение может иметь один из следующих типов.An expression is classified as one of the following:

  • Значение.A value. У каждого значения есть связанный с ним тип.Every value has an associated type.
  • Переменная.A variable. Каждая переменная имеет связанный тип, а именно объявленный тип переменной.Every variable has an associated type, namely the declared type of the variable.
  • Пространство имен.A namespace. Выражение с этой классификацией может присутствовать только в левой части member_access (доступ к члену).An expression with this classification can only appear as the left hand side of a member_access (Member access). В любом другом контексте выражение, классифицированное как пространство имен, вызывает ошибку времени компиляции.In any other context, an expression classified as a namespace causes a compile-time error.
  • Тип.A type. Выражение с этой классификацией может присутствовать только в левой части member_access (доступ к члену) или в качестве операнда для оператора as (оператор as), оператора is (оператор is) или @no Оператор __t-6 (оператор typeof).An expression with this classification can only appear as the left hand side of a member_access (Member access), or as an operand for the as operator (The as operator), the is operator (The is operator), or the typeof operator (The typeof operator). В любом другом контексте выражение, классифицированное как тип, вызывает ошибку времени компиляции.In any other context, an expression classified as a type causes a compile-time error.
  • Группа методов, которая представляет собой набор перегруженных методов, полученных при поиске члена (Уточняющий запрос члена).A method group, which is a set of overloaded methods resulting from a member lookup (Member lookup). Группа методов может иметь связанное выражение экземпляра и связанный список аргументов типа.A method group may have an associated instance expression and an associated type argument list. При вызове метода экземпляра результат вычисления выражения экземпляра становится экземпляром, представленным this (этот доступ).When an instance method is invoked, the result of evaluating the instance expression becomes the instance represented by this (This access). Группа методов разрешена в invocation_expression (выражениях вызова), delegate_creation_expression (выражения создания делегата) и в левой части оператора is и может быть неявно преобразуется в совместимый тип делегата (преобразования групп методов).A method group is permitted in an invocation_expression (Invocation expressions) , a delegate_creation_expression (Delegate creation expressions) and as the left hand side of an is operator, and can be implicitly converted to a compatible delegate type (Method group conversions). В любом другом контексте выражение, классифицированное как группа методов, вызывает ошибку времени компиляции.In any other context, an expression classified as a method group causes a compile-time error.
  • Литерал null.A null literal. Выражение с этой классификацией может быть неявно преобразовано в ссылочный тип или тип, допускающий значение null.An expression with this classification can be implicitly converted to a reference type or nullable type.
  • Анонимная функция.An anonymous function. Выражение с этой классификацией может быть неявно преобразовано в совместимый тип делегата или тип дерева выражения.An expression with this classification can be implicitly converted to a compatible delegate type or expression tree type.
  • Доступ к свойству.A property access. Каждый доступ к свойству имеет связанный тип, а именно тип свойства.Every property access has an associated type, namely the type of the property. Кроме того, доступ к свойству может иметь связанное выражение экземпляра.Furthermore, a property access may have an associated instance expression. Если для доступа к свойству экземпляра вызывается метод доступа (get или set), результат вычисления выражения экземпляра становится экземпляром, представленным this (этот доступ).When an accessor (the get or set block) of an instance property access is invoked, the result of evaluating the instance expression becomes the instance represented by this (This access).
  • Доступ к событию.An event access. Каждый доступ к событию имеет связанный тип, а именно тип события.Every event access has an associated type, namely the type of the event. Кроме того, доступ к событию может иметь связанное выражение экземпляра.Furthermore, an event access may have an associated instance expression. Доступ к событиям может отображаться в виде левого операнда операторов += и -= (назначение события).An event access may appear as the left hand operand of the += and -= operators (Event assignment). В любом другом контексте выражение, классифицированное как доступ к событию, вызывает ошибку времени компиляции.In any other context, an expression classified as an event access causes a compile-time error.
  • Доступ к индексатору.An indexer access. Каждый доступ к индексатору имеет связанный тип, а именно тип элемента индексатора.Every indexer access has an associated type, namely the element type of the indexer. Более того, доступ к индексатору имеет связанное выражение экземпляра и связанный список аргументов.Furthermore, an indexer access has an associated instance expression and an associated argument list. При вызове метода доступа к индексатору (get или set) результат вычисления выражения экземпляра становится экземпляром, представленным this (этот доступ), а результат вычисления списка аргументов становится список параметров вызова.When an accessor (the get or set block) of an indexer access is invoked, the result of evaluating the instance expression becomes the instance represented by this (This access), and the result of evaluating the argument list becomes the parameter list of the invocation.
  • Возмож.Nothing. Это происходит, когда выражение является вызовом метода с типом возвращаемого значения void.This occurs when the expression is an invocation of a method with a return type of void. Выражение, классифицированное как Nothing, допустимо только в контексте statement_expression (операторов выражения).An expression classified as nothing is only valid in the context of a statement_expression (Expression statements).

Окончательный результат выражения никогда не является пространством имен, типом, группой методов или доступом к событию.The final result of an expression is never a namespace, type, method group, or event access. Вместо этого, как отмечалось выше, эти категории выражений являются промежуточными конструкциями, которые разрешены только в определенных контекстах.Rather, as noted above, these categories of expressions are intermediate constructs that are only permitted in certain contexts.

Доступ к свойству или доступ к индексатору всегда реклассифицируется как значение путем выполнения вызова метода доступа Get или метода доступа set.A property access or indexer access is always reclassified as a value by performing an invocation of the get accessor or the set accessor. Конкретный метод доступа определяется контекстом доступа к свойству или индексатору: Если доступ является целью назначения, метод доступа set вызывается для назначения нового значения (простое присваивание).The particular accessor is determined by the context of the property or indexer access: If the access is the target of an assignment, the set accessor is invoked to assign a new value (Simple assignment). В противном случае вызывается метод доступа Get для получения текущего значения (значений выражений).Otherwise, the get accessor is invoked to obtain the current value (Values of expressions).

Значения выраженийValues of expressions

Большинству конструкций, которые используют выражение, в конечном итоге требуется, чтобы выражение обстоит значение.Most of the constructs that involve an expression ultimately require the expression to denote a value. В таких случаях, если фактическое выражение обозначает пространство имен, тип, группу методов или ничего, возникает ошибка времени компиляции.In such cases, if the actual expression denotes a namespace, a type, a method group, or nothing, a compile-time error occurs. Однако если выражение обозначает доступ к свойству, доступ к индексатору или переменную, значение свойства, индексатора или переменной неявно подставляется:However, if the expression denotes a property access, an indexer access, or a variable, the value of the property, indexer, or variable is implicitly substituted:

  • Значением переменной является просто значение, хранящееся в данный момент в месте хранения, определенном переменной.The value of a variable is simply the value currently stored in the storage location identified by the variable. Переменная должна считаться определенно присвоенным (определенному назначению), прежде чем ее значение можно будет получить, иначе возникает ошибка времени компиляции.A variable must be considered definitely assigned (Definite assignment) before its value can be obtained, or otherwise a compile-time error occurs.
  • Значение выражения доступа к свойству получается путем вызова метода доступа Get свойства.The value of a property access expression is obtained by invoking the get accessor of the property. Если свойство не имеет метода доступа Get, возникает ошибка времени компиляции.If the property has no get accessor, a compile-time error occurs. В противном случае выполняется вызов функции-члена (Проверка разрешения динамической перегрузки во время компиляции), а результат вызова становится значением выражения доступа к свойству.Otherwise, a function member invocation (Compile-time checking of dynamic overload resolution) is performed, and the result of the invocation becomes the value of the property access expression.
  • Значение выражения доступа к индексатору получается путем вызова метода доступа Get индексатора.The value of an indexer access expression is obtained by invoking the get accessor of the indexer. Если у индексатора нет метода доступа Get, возникает ошибка времени компиляции.If the indexer has no get accessor, a compile-time error occurs. В противном случае вызов функции-члена (Проверка разрешения динамической перегрузки во время компиляции) выполняется со списком аргументов, связанным с выражением доступа к индексатору, и результат вызова становится значением доступа к индексатору. выражение.Otherwise, a function member invocation (Compile-time checking of dynamic overload resolution) is performed with the argument list associated with the indexer access expression, and the result of the invocation becomes the value of the indexer access expression.

Статическая и динамическая привязкиStatic and Dynamic Binding

Процесс определения значения операции на основе типа или значения составляющих выражений (аргументы, операнды, приемники) часто называется привязкой.The process of determining the meaning of an operation based on the type or value of constituent expressions (arguments, operands, receivers) is often referred to as binding. Например, значение вызова метода определяется на основе типа получателя и аргументов.For instance the meaning of a method call is determined based on the type of the receiver and arguments. Значение оператора определяется на основе типа его операндов.The meaning of an operator is determined based on the type of its operands.

C# Значение операции обычно определяется во время компиляции на основе типа его составляющих выражений во время компиляции.In C# the meaning of an operation is usually determined at compile-time, based on the compile-time type of its constituent expressions. Аналогично, если выражение содержит ошибку, обнаруживается ошибка и сообщается компилятору.Likewise, if an expression contains an error, the error is detected and reported by the compiler. Этот подход называется статической привязкой.This approach is known as static binding.

Однако если выражение является динамическим выражением (т. е. имеет тип dynamic), это означает, что любая привязка, в которой он участвует, должна основываться на типе времени выполнения (т. е. фактический тип объекта, который он обозначает во время выполнения), а не на его тип. время компиляции.However, if an expression is a dynamic expression (i.e. has the type dynamic) this indicates that any binding that it participates in should be based on its run-time type (i.e. the actual type of the object it denotes at run-time) rather than the type it has at compile-time. Привязка такой операции, таким образом, откладывается до тех пор, пока операция не будет выполнена во время выполнения программы.The binding of such an operation is therefore deferred until the time where the operation is to be executed during the running of the program. Это называется динамической привязкой.This is referred to as dynamic binding.

При динамической привязке операции компилятор выполняет небольшую проверку или не проверяется.When an operation is dynamically bound, little or no checking is performed by the compiler. Вместо этого в случае сбоя привязки времени выполнения ошибки выводятся как исключения во время выполнения.Instead if the run-time binding fails, errors are reported as exceptions at run-time.

Следующие операции в C# подчиняются привязке:The following operations in C# are subject to binding:

  • Доступ к члену: e.MMember access: e.M
  • Вызов метода: e.M(e1, ..., eN)Method invocation: e.M(e1, ..., eN)
  • Вызов делегата: e(e1, ..., eN)Delegate invocation:e(e1, ..., eN)
  • Доступ к элементу: e[e1, ..., eN]Element access: e[e1, ..., eN]
  • Создание объекта: new C(e1, ..., eN)Object creation: new C(e1, ..., eN)
  • Перегруженные унарные операторы: +, -, !, ~, ++, --, true, falseOverloaded unary operators: +, -, !, ~, ++, --, true, false
  • Перегруженные бинарные операторы: +, -, *, /, %, &, &&, |, ||, ??, 0, 1, 2, 6, @no__t-7, 8Overloaded binary operators: +, -, *, /, %, &, &&, |, ||, ??, ^, <<, >>, ==,!=, >, <, >=, <=
  • Операторы присваивания: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, 0Assignment operators: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
  • Явные и неявные преобразованияImplicit and explicit conversions

Если динамические выражения не используются, C# по умолчанию используется статическая привязка, что означает, что в процессе выбора используются типы во время компиляции для составных выражений.When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of constituent expressions are used in the selection process. Однако если одно из составных выражений в приведенных выше операциях является динамическим выражением, то операция динамически привязана.However, when one of the constituent expressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound.

Время привязкиBinding-time

Статическая привязка выполняется во время компиляции, тогда как динамическая привязка выполняется во время выполнения.Static binding takes place at compile-time, whereas dynamic binding takes place at run-time. В следующих разделах термин « время привязки » относится к времени компиляции или времени выполнения, в зависимости от того, когда происходит привязка.In the following sections, the term binding-time refers to either compile-time or run-time, depending on when the binding takes place.

В следующем примере показаны понятия статической и динамической привязки и времени привязки.The following example illustrates the notions of static and dynamic binding and of binding-time:

object  o = 5;
dynamic d = 5;

Console.WriteLine(5);  // static  binding to Console.WriteLine(int)
Console.WriteLine(o);  // static  binding to Console.WriteLine(object)
Console.WriteLine(d);  // dynamic binding to Console.WriteLine(int)

Первые два вызова имеют статическую привязку: перегрузка Console.WriteLine выбирается на основе типа их аргумента во время компиляции.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.

Третий вызов динамически привязан: перегрузка Console.WriteLine выбирается на основе типа времени выполнения своего аргумента.The third call is dynamically bound: the overload of Console.WriteLine is picked based on the run-time type of its argument. Это происходит потому, что аргумент является динамическим выражением, его тип времени компиляции — dynamic.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.

динамическая привязка;Dynamic binding

Динамическая привязка предназначена для того C# , чтобы программы могли взаимодействовать с динамическими объектами, т. е. объекты, которые не соответствуют обычным правилам C# системы типов.The purpose of dynamic binding is to allow C# programs to interact with dynamic objects, i.e. objects that do not follow the normal rules of the C# type system. Динамические объекты могут быть объектами других языков программирования с разными системами типов или объектами, которые программно устанавливают для реализации собственной семантики привязки для различных операций.Dynamic objects may be objects from other programming languages with different types systems, or they may be objects that are programmatically setup to implement their own binding semantics for different operations.

Механизм, с помощью которого динамический объект реализует собственную семантику, определяется реализацией.The mechanism by which a dynamic object implements its own semantics is implementation defined. Заданный интерфейс — опять же определенная реализация — реализуется динамическими объектами, чтобы сообщить времени C# выполнения о том, что они имеют специальную семантику.A given interface -- again implementation defined -- is implemented by dynamic objects to signal to the C# run-time that they have special semantics. Таким образом, всякий раз, когда операции с динамическим объектом связаны динамически, их собственная семантика привязки, C# а не, как указано в этом документе, принимаются.Thus, whenever operations on a dynamic object are dynamically bound, their own binding semantics, rather than those of C# as specified in this document, take over.

Хотя динамическая привязка предназначена для обеспечения взаимодействия с динамическими объектами, C# обеспечивает динамическую привязку для всех объектов независимо от того, являются они динамическими или нет.While the purpose of dynamic binding is to allow interoperation with dynamic objects, C# allows dynamic binding on all objects, whether they are dynamic or not. Это обеспечивает более гладкую интеграцию динамических объектов, так как результаты операций на них могут не быть динамическими объектами, но по-прежнему имеют тип, неизвестный для программиста во время компиляции.This allows for a smoother integration of dynamic objects, as the results of operations on them may not themselves be dynamic objects, but are still of a type unknown to the programmer at compile-time. Кроме того, динамическая привязка может помочь устранить подверженные ошибкам код на основе отражения, даже если объекты не участвуют в динамических объектах.Also dynamic binding can help eliminate error-prone reflection-based code even when no objects involved are dynamic objects.

В следующих разделах описана каждая конструкция в языке, именно при применении динамической привязки, проверка времени компиляции — если применено, и каков результат компиляции и классификация выражений.The following sections describe for each construct in the language exactly when dynamic binding is applied, what compile time checking -- if any -- is applied, and what the compile-time result and expression classification is.

Типы составных выраженийTypes of constituent expressions

Если операция имеет статическую привязку, тип составного выражения (например, получателя, аргумента, индекса или операнда) всегда считается типом времени компиляции этого выражения.When an operation is statically bound, the type of a constituent expression (e.g. a receiver, an argument, an index or an operand) is always considered to be the compile-time type of that expression.

При динамической привязке операции тип составного выражения определяется различными способами в зависимости от типа времени компиляции составного выражения:When an operation is dynamically bound, the type of a constituent expression is determined in different ways depending on the compile-time type of the constituent expression:

  • Составное выражение типа времени компиляции dynamic считается типом фактического значения, которое выражение вычисляет во время выполнения.A constituent expression of compile-time type dynamic is considered to have the type of the actual value that the expression evaluates to at runtime
  • Составное выражение, тип времени компиляции которого является параметром типа, считается типом, к которому привязывается параметр типа во время выполненияA constituent expression whose compile-time type is a type parameter is considered to have the type which the type parameter is bound to at runtime
  • В противном случае составное выражение считается типом времени компиляции.Otherwise the constituent expression is considered to have its compile-time type.

ОператорыOperators

Выражения формируются из операндов и операторов.Expressions are constructed from operands and operators. Операторы в выражении указывают, какие действия нужно применить к операндам.The operators of an expression indicate which operations to apply to the operands. Примеры операторов: +, -, *, / и new.Examples of operators include +, -, *, /, and new. Операндами могут являться литералы, поля, локальные переменные, выражения и т. п.Examples of operands include literals, fields, local variables, and expressions.

Существует три вида операторов:There are three kinds of operators:

  • Унарные операторы.Unary operators. Унарные операторы принимают один операнд и используют любую префиксную нотацию (например, --x) или постфиксную нотацию (например, x++).The unary operators take one operand and use either prefix notation (such as --x) or postfix notation (such as x++).
  • Бинарные операторы.Binary operators. Бинарные операторы принимают два операнда и используют нотацию инфиксные (например, x + y).The binary operators take two operands and all use infix notation (such as x + y).
  • Оператор Ternary.Ternary operator. Существует только один оператор ternary (?:); Он принимает три операнда и использует нотацию инфиксные (c ? x : y).Only one ternary operator, ?:, exists; it takes three operands and uses infix notation (c ? x : y).

Порядок вычисления операторов в выражении определяется приоритетом и ассоциативностью операторов (приоритет операторов и ассоциативность).The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators (Operator precedence and associativity).

Операнды в выражении вычисляются слева направо.Operands in an expression are evaluated from left to right. Например, в F(i) + G(i++) * H(i) метод F вызывается с использованием старого значения i, затем метод G вызывается со старым значением i, и, наконец, метод H вызывается с новым значением i.For example, in F(i) + G(i++) * H(i), method F is called using the old value of i, then method G is called with the old value of i, and, finally, method H is called with the new value of i. Это отдельно от и не связано с приоритетом операторов.This is separate from and unrelated to operator precedence.

Большинство операторов могут быть перегружены.Certain operators can be overloaded. Перегрузка операторов позволяет задавать пользовательские реализации операторов для операций, в которых один или оба операнда имеют определяемый пользователем класс или тип структуры (перегрузка операторов).Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type (Operator overloading).

Приоритет и ассоциативность операторовOperator precedence and associativity

Если выражение содержит несколько операторов, порядок вычисления этих операторов определяется их приоритетом.When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. Например, выражение x + y * z вычисляется как x + (y * z), так как оператор @no__t 2 имеет более высокий приоритет, чем бинарный оператор +.For example, the expression x + y * z is evaluated as x + (y * z) because the * operator has higher precedence than the binary + operator. Приоритет оператора определяется определением связанного с ним грамматики.The precedence of an operator is established by the definition of its associated grammar production. Например, additive_expression состоит из последовательности multiplicative_expressions, разделенной операторами + или -, таким образом предоставляя операторам + и - более низкий приоритет, чем *, / и @no__ операторы t-8.For example, an additive_expression consists of a sequence of multiplicative_expressions separated by + or - operators, thus giving the + and - operators lower precedence than the *, /, and % operators.

В следующей таблице перечислены все операторы в порядке приоритета от самого высокого до самого низкого:The following table summarizes all operators in order of precedence from highest to lowest:

РазделSection КатегорияCategory ИнструкцииOperators
Первичные выраженияPrimary expressions ПервичныйPrimary x.y f(x) a[x] x++ x-- new typeof default checked unchecked delegatex.y f(x) a[x] x++ x-- new typeof default checked unchecked delegate
Унарные операторыUnary operators УнарныйUnary + - ! ~ ++x --x (T)x+ - ! ~ ++x --x (T)x
Арифметические операторыArithmetic operators МультипликативныйMultiplicative * / %* / %
Арифметические операторыArithmetic operators АддитивныйAdditive + -+ -
Операторы сдвигаShift operators СдвигShift << >><< >>
Реляционные операторы и операторы тестирования типаRelational and type-testing operators Тестирования типа и относительныеRelational and type testing < > <= >= is as< > <= >= is as
Реляционные операторы и операторы тестирования типаRelational and type-testing operators РавенствоEquality == !=== !=
Логические операторыLogical operators Логическое ИLogical AND &
Логические операторыLogical operators Логическое исключающее ИЛИLogical XOR ^
Логические операторыLogical operators Логическое ИЛИLogical OR |
Условные логические операторыConditional logical operators Условное ИConditional AND &&
Условные логические операторыConditional logical operators Условное ИЛИConditional OR ||
Оператор объединения со значением NULLThe null coalescing operator Объединение со значением NULLNull coalescing ??
Условный операторConditional operator УсловиеConditional ?:
Операторы присваивания, выражения анонимных функцийAssignment operators, Anonymous function expressions Присваивание и лямбда-выражениеAssignment and lambda expression = *= /= %= += -= <<= >>= &= ^= |= =>= *= /= %= += -= <<= >>= &= ^= |= =>

Если операнд находится между двумя операторами с одинаковым приоритетом, то ассоциативность операторов управляет порядком, в котором выполняются операции:When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed:

  • За исключением операторов присваивания и оператора объединения со значением NULL, все бинарные операторы имеют левую ассоциативность, то есть операции выполняются слева направо.Except for the assignment operators and the null coalescing operator, all binary operators are left-associative, meaning that operations are performed from left to right. Например, выражение x + y + z вычисляется как (x + y) + z.For example, x + y + z is evaluated as (x + y) + z.
  • Операторы присваивания, оператор объединения со значением NULL и условный оператор (?:) являются прямыми ассоциативными, то есть операции выполняются справа налево.The assignment operators, the null coalescing operator and the conditional operator (?:) are right-associative, meaning that operations are performed from right to left. Например, выражение x = y = z вычисляется как x = (y = z).For example, x = y = z is evaluated as x = (y = z).

Приоритет и ассоциативность операторов можно изменять, используя скобки.Precedence and associativity can be controlled using parentheses. Например, в выражении x + y * z сначала y умножается на z, а результат прибавляется к x, а в выражении (x + y) * z сначала суммируются x и y, а результат умножается на z.For example, x + y * z first multiplies y by z and then adds the result to x, but (x + y) * z first adds x and y and then multiplies the result by z.

Перегрузка операторовOperator overloading

Все унарные и бинарные операторы имеют стандартные реализации, которые автоматически доступны в любом выражении.All unary and binary operators have predefined implementations that are automatically available in any expression. Помимо предопределенных реализаций, пользовательские реализации можно реализовать, включив объявления operator в классы и структуры (Операторы).In addition to the predefined implementations, user-defined implementations can be introduced by including operator declarations in classes and structs (Operators). Реализации определяемых пользователем операторов всегда имеют приоритет над предопределенными реализациями оператора: Только если применимые реализации определяемых пользователем операторов отсутствуют, будут рассматриваться стандартные реализации операторов, как описано в разделе разрешение перегрузки унарного оператора и разрешение перегрузки бинарного оператора.User-defined operator implementations always take precedence over predefined operator implementations: Only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered, as described in Unary operator overload resolution and Binary operator overload resolution.

Ниже перечислены Унарные операторы с перегрузками .The overloadable unary operators are:

+   -   !   ~   ++   --   true   false

Хотя true и false не используются явно в выражениях (и, следовательно, не включены в таблицу приоритетов в очередности и ассоциативности операторов), они считаются операторами, так как они вызываются в нескольких выражениях. контексты: логические выражения (логические выражения) и выражения, включающие условный (Условный оператор) и условные логические операторы (Условные логические операторы).Although true and false are not used explicitly in expressions (and therefore are not included in the precedence table in Operator precedence and associativity), they are considered operators because they are invoked in several expression contexts: boolean expressions (Boolean expressions) and expressions involving the conditional (Conditional operator), and conditional logical operators (Conditional logical operators).

Ниже приведены перегруженные бинарные операторы .The overloadable binary operators are:

+   -   *   /   %   &   |   ^   <<   >>   ==   !=   >   <   >=   <=

Перегружать можно только операторы, перечисленные выше.Only the operators listed above can be overloaded. В частности, невозможно перегрузить доступ к члену, вызов метода или =, &&, ||, ??, ?:, =>, typeof, unchecked, @no__t – 11 и 2.In particular, it is not possible to overload member access, method invocation, or the =, &&, ||, ??, ?:, =>, checked, unchecked, new, typeof, default, as, and is operators.

При перегрузке бинарного оператора соответствующий оператор присвоения (если таковой имеется) также неявно перегружается.When a binary operator is overloaded, the corresponding assignment operator, if any, is also implicitly overloaded. Например, перегрузка оператора * также является перегрузкой оператора *=.For example, an overload of operator * is also an overload of operator *=. Это описано далее в составном назначении.This is described further in Compound assignment. Обратите внимание, что сам оператор присваивания (=) не может быть перегружен.Note that the assignment operator itself (=) cannot be overloaded. Присваивание всегда выполняет простую побитовую копию значения в переменную.An assignment always performs a simple bit-wise copy of a value into a variable.

Операции приведения, такие как (T)x, перегружаются путем предоставления пользовательских преобразований (пользовательскихпреобразований).Cast operations, such as (T)x, are overloaded by providing user-defined conversions (User-defined conversions).

Доступ к элементам, например a[x], не считается перегруженным оператором.Element access, such as a[x], is not considered an overloadable operator. Вместо этого определяемое пользователем индексирование поддерживается через индексаторы (индексаторы).Instead, user-defined indexing is supported through indexers (Indexers).

В выражениях указываются операторы с помощью нотации оператора, и в объявлениях операторы указываются с помощью функциональной нотации.In expressions, operators are referenced using operator notation, and in declarations, operators are referenced using functional notation. В следующей таблице показана связь между оператором и функциональными представлениями для унарных и бинарных операторов.The following table shows the relationship between operator and functional notations for unary and binary operators. В первой записи Op обозначает любой перегруженный унарный префикс.In the first entry, op denotes any overloadable unary prefix operator. Во второй записи Op обозначает унарный постфиксный оператор ++ и --.In the second entry, op denotes the unary postfix ++ and -- operators. В третьей записи Op обозначает любой перегруженный бинарный оператор.In the third entry, op denotes any overloadable binary operator.

Нотация оператораOperator notation Функциональная нотацияFunctional notation
op x operator op(x)
x op operator op(x)
x op y operator op(x,y)

Объявления определяемых пользователем операторов всегда должны иметь хотя бы один из параметров типа класса или структуры, который содержит объявление оператора.User-defined operator declarations always require at least one of the parameters to be of the class or struct type that contains the operator declaration. Таким образом, определяемый пользователем оператор не может иметь ту же сигнатуру, что и предопределенный оператор.Thus, it is not possible for a user-defined operator to have the same signature as a predefined operator.

Объявления определяемых пользователем операторов не могут изменять синтаксис, приоритет или ассоциативность оператора.User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. Например, оператор / всегда является бинарным оператором, всегда имеет уровень приоритета, заданный в приоритете и ассоциативности операторов, и всегда является ассоциативным.For example, the / operator is always a binary operator, always has the precedence level specified in Operator precedence and associativity, and is always left-associative.

Хотя определяемый пользователем оператор может выполнять любые вычисления, реализации, которые создают результаты, отличные от интуитивно ожидаемых, настоятельно не рекомендуются.While it is possible for a user-defined operator to perform any computation it pleases, implementations that produce results other than those that are intuitively expected are strongly discouraged. Например, реализация operator == должна сравнивать два операнда на равенство и возвращать соответствующий результат bool.For example, an implementation of operator == should compare the two operands for equality and return an appropriate bool result.

Описания отдельных операторов в первичных выражениях через Условные логические операторы определяют стандартные реализации операторов и дополнительные правила, применяемые к каждому оператору.The descriptions of individual operators in Primary expressions through Conditional logical operators specify the predefined implementations of the operators and any additional rules that apply to each operator. В описаниях используются условия разрешения перегрузки унарного оператора, разрешения перегрузки бинарного оператораи числового повышения, определения которых находятся в следующих разделах.The descriptions make use of the terms unary operator overload resolution, binary operator overload resolution, and numeric promotion, definitions of which are found in the following sections.

Разрешение перегрузки унарного оператораUnary operator overload resolution

Операция формы op x или x op, где op является перегруженным унарным оператором, а x — выражением типа X, обрабатывается следующим образом:An operation of the form op x or x op, where op is an overloadable unary operator, and x is an expression of type X, is processed as follows:

  • Набор определяемых пользователем операторов, предоставляемых X для операции operator op(x), определяется с помощью правил пользовательских операторов-кандидатов.The set of candidate user-defined operators provided by X for the operation operator op(x) is determined using the rules of Candidate user-defined operators.
  • Если набор определяемых пользователем операторов не пуст, то он становится набором операторов-кандидатов для операции.If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. В противном случае стандартные унарные реализации operator op, включая формы с нулификацией, становятся набором операторов кандидатов для операции.Otherwise, the predefined unary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. Предопределенные реализации данного оператора задаются в описании оператора (Первичные выражения и Унарные операторы).The predefined implementations of a given operator are specified in the description of the operator (Primary expressions and Unary operators).
  • Правила разрешения перегрузки для разрешения перегрузки применяются к набору операторов-кандидатов, чтобы выбрать лучший оператор относительно списка аргументов @no__t – 1, и этот оператор станет результатом процесса разрешения перегрузки.The overload resolution rules of Overload resolution are applied to the set of candidate operators to select the best operator with respect to the argument list (x), and this operator becomes the result of the overload resolution process. Если при разрешении перегрузки не удается выбрать один из лучших операторов, возникает ошибка времени привязки.If overload resolution fails to select a single best operator, a binding-time error occurs.

Разрешение перегрузки бинарного оператораBinary operator overload resolution

Операция формы x op y, где op является перегруженным бинарным оператором, x является выражением типа X, а y — выражением типа Y, обрабатывается следующим образом:An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • Определен набор определяемых пользователем операторов, предоставленных X и Y для операции operator op(x,y).The set of candidate user-defined operators provided by X and Y for the operation operator op(x,y) is determined. Набор состоит из объединения операторов-кандидатов, предоставляемых @no__t – 0, и операторов-кандидатов, предоставляемых @no__t – 1, каждый из которых определяется с помощью правил пользовательских операторов-кандидатов.The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of Candidate user-defined operators. Если X и Y относятся к одному типу, или если X и Y являются производными от общего базового типа, то общие операторы-кандидаты выполняются только в объединенном наборе один раз.If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
  • Если набор определяемых пользователем операторов не пуст, то он становится набором операторов-кандидатов для операции.If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. В противном случае Стандартные двоичные реализации operator op, включая формы с нулификацией, становятся набором операторов-кандидатов для операции.Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. Предопределенные реализации данного оператора задаются в описании оператора (арифметические операторы через Условные логические операторы).The predefined implementations of a given operator are specified in the description of the operator (Arithmetic operators through Conditional logical operators). Для предопределенных операторов перечисления и делегата учитываются только те операторы, которые определены типом перечисления или делегатом, который является типом времени привязки одного из операндов.For predefined enum and delegate operators, the only operators considered are those defined by an enum or delegate type that is the binding-time type of one of the operands.
  • Правила разрешения перегрузки для разрешения перегрузки применяются к набору операторов-кандидатов, чтобы выбрать лучший оператор относительно списка аргументов @no__t – 1, и этот оператор станет результатом процесса разрешения перегрузки.The overload resolution rules of Overload resolution are applied to the set of candidate operators to select the best operator with respect to the argument list (x,y), and this operator becomes the result of the overload resolution process. Если при разрешении перегрузки не удается выбрать один из лучших операторов, возникает ошибка времени привязки.If overload resolution fails to select a single best operator, a binding-time error occurs.

Пользовательские операторы-кандидатыCandidate user-defined operators

При наличии типа T и операции operator op(A), где op является перегруженным оператором, а A — списком аргументов, набор определяемых пользователем операторов, предоставляемых T для operator op(A), определяется следующим образом:Given a type T and an operation operator op(A), where op is an overloadable operator and A is an argument list, the set of candidate user-defined operators provided by T for operator op(A) is determined as follows:

  • Определите тип T0.Determine the type T0. Если T является типом, допускающим значение null, T0 является базовым типом, в противном случае T0 равно T.If T is a nullable type, T0 is its underlying type, otherwise T0 is equal to T.
  • Для всех объявлений operator op в T0 и всех приподнятых форм таких операторов, если применим хотя бы один оператор (применимый член функции) относительно списка аргументов A, то набор потенциальных операторов состоит из всех таких применимые операторы в T0.For all operator op declarations in T0 and all lifted forms of such operators, if at least one operator is applicable (Applicable function member) with respect to the argument list A, then the set of candidate operators consists of all such applicable operators in T0.
  • В противном случае, если T0 равно object, набор операторов кандидатов будет пустым.Otherwise, if T0 is object, the set of candidate operators is empty.
  • В противном случае набор операторов кандидатов, предоставляемый T0, является набором операторов кандидатов, предоставляемых прямым базовым классом T0, или эффективным базовым классом для T0, если T0 является параметром типа.Otherwise, the set of candidate operators provided by T0 is the set of candidate operators provided by the direct base class of T0, or the effective base class of T0 if T0 is a type parameter.

Числовые акцииNumeric promotions

Числовое продвижение состоит из автоматического выполнения определенных неявных преобразований операндов предопределенных унарных и бинарных числовых операторов.Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. Числовое продвижение не является отдельным механизмом, а влияет на применение разрешения перегрузки к предопределенным операторам.Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. Числовое повышение особенно не влияет на вычисление определяемых пользователем операторов, хотя пользовательские операторы можно реализовать для похожих эффектов.Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.

В качестве примера числового повышения можно рассмотреть стандартные реализации оператора binary *:As an example of numeric promotion, consider the predefined implementations of the binary * operator:

int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);

Если к этому набору операторов применяются правила разрешения перегрузки (разрешение перегрузки), то в результате выбирается первый из операторов, для которых существуют неявные преобразования из типов операндов.When overload resolution rules (Overload resolution) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. Например, для операции b * s, где b — это byte, а sshort, разрешение перегрузки выбирает operator *(int,int) в качестве лучшего оператора.For example, for the operation b * s, where b is a byte and s is a short, overload resolution selects operator *(int,int) as the best operator. Таким образом, в результате b и s преобразуются в int, а тип результата — int.Thus, the effect is that b and s are converted to int, and the type of the result is int. Аналогичным образом для операции i * d, где i — это int, а ddouble, разрешение перегрузки выбирает operator *(double,double) в качестве лучшего оператора.Likewise, for the operation i * d, where i is an int and d is a double, overload resolution selects operator *(double,double) as the best operator.

Унарные числовые акцииUnary numeric promotions

Унарное числовое продвижение выполняется для операндов предопределенных унарных операторов +, - и ~.Unary numeric promotion occurs for the operands of the predefined +, -, and ~ unary operators. Унарное числовое продвижение просто состоит из преобразования операндов типа sbyte, byte, short, ushort или char для типа int.Unary numeric promotion simply consists of converting operands of type sbyte, byte, short, ushort, or char to type int. Кроме того, для унарного оператора - унарное числовое продвижение преобразует операнды типа uint в тип long.Additionally, for the unary - operator, unary numeric promotion converts operands of type uint to type long.

Двоичные числовые акцииBinary numeric promotions

Двоичное числовое расширение выполняется для операндов предопределенных +, -, *, /, %, &, |, ^, 1, 2 и 3 бинарных операторов.Binary numeric promotion occurs for the operands of the predefined +, -, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Двоичное числовое расширение неявно преобразует оба операнда в общий тип, который, в случае нереляционных операторов, также станет типом результата операции.Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Двоичное числовое расширение состоит из применения следующих правил в том порядке, в котором они отображаются здесь:Binary numeric promotion consists of applying the following rules, in the order they appear here:

  • Если любой из операндов имеет тип decimal, то другой операнд преобразуется в тип decimal или ошибка времени привязки возникает, если другой операнд имеет тип float или double.If either operand is of type decimal, the other operand is converted to type decimal, or a binding-time error occurs if the other operand is of type float or double.
  • В противном случае, если любой из операндов имеет тип double, другой операнд преобразуется в тип double.Otherwise, if either operand is of type double, the other operand is converted to type double.
  • В противном случае, если любой из операндов имеет тип float, другой операнд преобразуется в тип float.Otherwise, if either operand is of type float, the other operand is converted to type float.
  • В противном случае, если любой из операндов имеет тип ulong, другой операнд преобразуется в тип ulong или ошибка времени привязки возникает, если другой операнд имеет тип sbyte, short, int или long.Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a binding-time error occurs if the other operand is of type sbyte, short, int, or long.
  • В противном случае, если любой из операндов имеет тип long, другой операнд преобразуется в тип long.Otherwise, if either operand is of type long, the other operand is converted to type long.
  • В противном случае, если любой из операндов имеет тип uint, а другой операнд имеет тип sbyte, short или int, оба операнда преобразуются в тип long.Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
  • В противном случае, если любой из операндов имеет тип uint, другой операнд преобразуется в тип uint.Otherwise, if either operand is of type uint, the other operand is converted to type uint.
  • В противном случае оба операнда преобразуются в тип int.Otherwise, both operands are converted to type int.

Обратите внимание, что первое правило запрещает любые операции, которые сочетают тип decimal с типами double и float.Note that the first rule disallows any operations that mix the decimal type with the double and float types. Это правило связано с тем, что нет неявных преобразований между типом decimal и типами double и float.The rule follows from the fact that there are no implicit conversions between the decimal type and the double and float types.

Также обратите внимание, что операнд не может иметь тип ulong, если другой операнд имеет целочисленный тип со знаком.Also note that it is not possible for an operand to be of type ulong when the other operand is of a signed integral type. Причина в том, что целочисленный тип, который может представлять полный диапазон ulong, а также целочисленные типы со знаком, не существует.The reason is that no integral type exists that can represent the full range of ulong as well as the signed integral types.

В обоих приведенных выше случаях выражение приведения можно использовать для явного преобразования одного операнда в тип, совместимый с другим операндом.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.

В примереIn the example

decimal AddPercent(decimal x, double percent) {
    return x * (1.0 + percent / 100.0);
}

Ошибка времени привязки возникает, поскольку decimal не может быть умножена на double.a binding-time error occurs because a decimal cannot be multiplied by a double. Эта ошибка решается путем явного преобразования второго операнда в decimal следующим образом:The error is resolved by explicitly converting the second operand to decimal, as follows:

decimal AddPercent(decimal x, double percent) {
    return x * (decimal)(1.0 + percent / 100.0);
}

Операторы с нулификациейLifted operators

Операторы с нулификацией позволяют использовать стандартные и определяемые пользователем операторы, которые работают с типами значений, не допускающими значения NULL, а также использоваться с формами этих типов, допускающими значение null.Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Операторы с нулификацией создаются на основе предопределенных и определяемых пользователем операторов, отвечающих определенным требованиям, как описано ниже.Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:

  • Для унарных операторовFor the unary operators

    +  ++  -  --  !  ~
    

    оператор с нулификацией оператора существует, если типы операнда и результата являются типами значений, не допускающими значения NULL.a lifted form of an operator exists if the operand and result types are both non-nullable value types. Форма с нулификацией создается путем добавления одного модификатора ? к операндам и типам результатов.The lifted form is constructed by adding a single ? modifier to the operand and result types. Оператор с нулификацией создает значение null, если операнд имеет значение null.The lifted operator produces a null value if the operand is null. В противном случае оператор с нулификацией разворачивает операнд, применяет базовый оператор и заключает результат в оболочку.Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.

  • Для бинарных операторовFor the binary operators

    +  -  *  /  %  &  |  ^  <<  >>
    

    оператор с нулификацией оператора существует, если типы операнда и результата являются типами значений, не допускающими значения NULL.a lifted form of an operator exists if the operand and result types are all non-nullable value types. Форма с нулификацией создается путем добавления одного модификатора ? к каждому операнду и типу результата.The lifted form is constructed by adding a single ? modifier to each operand and result type. Оператор с нулификацией создает значение null, если один или оба операнда имеют значение null (исключением являются операторы & и | типа bool?, как описано в логических логических операторах).The lifted operator produces a null value if one or both operands are null (an exception being the & and | operators of the bool? type, as described in Boolean logical operators). В противном случае оператор с нулификацией разворачивает операнды, применяет базовый оператор и заключает результат в оболочку.Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result.

  • Для операторов равенстваFor the equality operators

    ==  !=
    

    оператор с нулификацией оператора существует, если типы операндов являются типами значений, не допускающими значения NULL, а тип результата — bool.a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. Форма с нулификацией создается путем добавления одного модификатора ? к каждому типу операнда.The lifted form is constructed by adding a single ? modifier to each operand type. Оператор с нулификацией считает, что два значения NULL равны, и значение NULL не равно любому значению, отличному от NULL.The lifted operator considers two null values equal, and a null value unequal to any non-null value. Если оба операнда не равны NULL, оператор с нулификацией разворачивает операнды и применяет базовый оператор для получения результата bool.If both operands are non-null, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.

  • Для реляционных операторовFor the relational operators

    <  >  <=  >=
    

    оператор с нулификацией оператора существует, если типы операндов являются типами значений, не допускающими значения NULL, а тип результата — bool.a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. Форма с нулификацией создается путем добавления одного модификатора ? к каждому типу операнда.The lifted form is constructed by adding a single ? modifier to each operand type. Оператор с нулификацией создает значение false, если один или оба операнда имеют значение null.The lifted operator produces the value false if one or both operands are null. В противном случае оператор с нулификацией разворачивает операнды и применяет базовый оператор для получения результата bool.Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.

Поиск членаMember lookup

Поиск члена — это процесс, в котором определяется значение имени в контексте типа.A member lookup is the process whereby the meaning of a name in the context of a type is determined. Поиск члена может выполняться как часть оценки simple_name (простые имена) или member_access (доступ к члену) в выражении.A member lookup can occur as part of evaluating a simple_name (Simple names) or a member_access (Member access) in an expression. Если simple_name или member_access встречаются как primary_expression invocation_expression (вызовы методов), говорят, что элемент вызывается.If the simple_name or member_access occurs as the primary_expression of an invocation_expression (Method invocations), the member is said to be invoked.

Если член является методом или событием или является константой, полем или свойством типа делегата (делегатов) или типа dynamic (динамический тип), то член называется invocable.If a member is a method or event, or if it is a constant, field or property of either a delegate type (Delegates) or the type dynamic (The dynamic type), then the member is said to be invocable.

При поиске члена учитывается не только имя элемента, но и число параметров типа, которое имеет элемент, и доступность элемента.Member lookup considers not only the name of a member but also the number of type parameters the member has and whether the member is accessible. В целях уточняющего запроса членов универсальные методы и вложенные универсальные типы имеют количество параметров типа, указанных в соответствующих объявлениях, а все остальные элементы имеют нулевые параметры типа.For the purposes of member lookup, generic methods and nested generic types have the number of type parameters indicated in their respective declarations and all other members have zero type parameters.

Поиск члена имени @ no__t-0 с параметрами K @ no__t-2type в типе @ no__t-3 обрабатывается следующим образом:A member lookup of a name N with K type parameters in a type T is processed as follows:

  • Во-первых, определяется набор доступных членов с именем @ no__t-0:First, a set of accessible members named N is determined:
    • Если T является параметром типа, то набором является объединение наборов доступных элементов с именем @ no__t-1 в каждом из типов, указанных в качестве первичного ограничения или вторичного ограничения (ограничения параметра типа) в параметре@ no__t-3, вместе с набором Доступные члены с именем @ no__t-4 в object.If T is a type parameter, then the set is the union of the sets of accessible members named N in each of the types specified as a primary constraint or secondary constraint (Type parameter constraints) for T, along with the set of accessible members named N in object.
    • В противном случае набор состоит из всех доступных (доступ к членам) членов с именем @ no__t-1 в @ no__t-2, включая унаследованные члены и доступные члены с именем @ no__t-3 в object.Otherwise, the set consists of all accessible (Member access) members named N in T, including inherited members and the accessible members named N in object. Если T является сконструированным типом, то набор элементов получается с помощью подставляемых аргументов типа, как описано в разделе члены сконструированных типов.If T is a constructed type, the set of members is obtained by substituting type arguments as described in Members of constructed types. Элементы, включающие модификатор override, исключаются из набора.Members that include an override modifier are excluded from the set.
  • Затем, если K равно нулю, все вложенные типы, объявления которых содержат параметры типа, удаляются.Next, if K is zero, all nested types whose declarations include type parameters are removed. Если K не равен нулю, удаляются все элементы с другим числом параметров типа.If K is not zero, all members with a different number of type parameters are removed. Обратите внимание, что если K равен нулю, методы с параметрами типа не удаляются, так как процесс определения типа (вывод типа) может иметь возможность определить аргументы типа.Note that when K is zero, methods having type parameters are not removed, since the type inference process (Type inference) might be able to infer the type arguments.
  • Далее, если вызываетсяэлемент, все элементы, не являющиесяinvocable , удаляются из набора.Next, if the member is invoked, all non-invocable members are removed from the set.
  • Затем элементы, скрытые другими элементами, удаляются из набора.Next, members that are hidden by other members are removed from the set. Для каждого члена S.M в наборе, где S — это тип, в котором объявлен член @ no__t-2, применяются следующие правила.For every member S.M in the set, where S is the type in which the member M is declared, the following rules are applied:
    • Если M является константой, полем, свойством, событием или членом перечисления, все члены, объявленные в базовом типе S, удаляются из набора.If M is a constant, field, property, event, or enumeration member, then all members declared in a base type of S are removed from the set.
    • Если M является объявлением типа, все типы, не являющиеся типами, объявленные в базовом типе S, удаляются из набора, а все объявления типов с одинаковым количеством параметров типа, как M, объявленные в базовом типе S, удаляются из набора.If M is a type declaration, then all non-types declared in a base type of S are removed from the set, and all type declarations with the same number of type parameters as M declared in a base type of S are removed from the set.
    • Если M является методом, все члены, не являющиеся методами, объявленные в базовом типе S, удаляются из набора.If M is a method, then all non-method members declared in a base type of S are removed from the set.
  • Затем члены интерфейса, скрытые членами класса, удаляются из набора.Next, interface members that are hidden by class members are removed from the set. Этот шаг действует только в том случае, если T является параметром типа, а T имеет и эффективный базовый класс, отличный от object, и непустой эффективный набор интерфейсов (ограничения параметра типа).This step only has an effect if T is a type parameter and T has both an effective base class other than object and a non-empty effective interface set (Type parameter constraints). Для каждого члена S.M в наборе, где S — это тип, в котором объявлен член M, применяются следующие правила, если S является объявлением класса, кроме object:For every member S.M in the set, where S is the type in which the member M is declared, the following rules are applied if S is a class declaration other than object:
    • Если M является константой, полем, свойством, событием, членом перечисления или объявлением типа, все члены, объявленные в объявлении интерфейса, удаляются из набора.If M is a constant, field, property, event, enumeration member, or type declaration, then all members declared in an interface declaration are removed from the set.
    • Если M является методом, все члены, не являющиеся методами, объявленные в объявлении интерфейса, удаляются из набора, а все методы с той же сигнатурой, что и M, объявленные в объявлении интерфейса, удаляются из набора.If M is a method, then all non-method members declared in an interface declaration are removed from the set, and all methods with the same signature as M declared in an interface declaration are removed from the set.
  • Наконец, после удаления скрытых членов определяется результат поиска:Finally, having removed hidden members, the result of the lookup is determined:
    • Если набор состоит из одного элемента, который не является методом, то этот элемент является результатом уточняющего запроса.If the set consists of a single member that is not a method, then this member is the result of the lookup.
    • В противном случае, если набор содержит только методы, то эта группа методов является результатом уточняющего запроса.Otherwise, if the set contains only methods, then this group of methods is the result of the lookup.
    • В противном случае Уточняющий запрос неоднозначен и возникает ошибка времени привязки.Otherwise, the lookup is ambiguous, and a binding-time error occurs.

Для поисков членов в типах, отличных от параметров и интерфейсов, и уточняющих запросов в интерфейсах, которые являются строго одиночным наследованием (каждый интерфейс в цепочке наследования имеет ровно ноль или один прямой базовый интерфейс), результатом правил подстановки будет только производные члены скрывают базовые члены с тем же именем или сигнатурой.For member lookups in types other than type parameters and interfaces, and member lookups in interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effect of the lookup rules is simply that derived members hide base members with the same name or signature. Такие уточняющие запросы с одним наследованием никогда не являются неоднозначными.Such single-inheritance lookups are never ambiguous. Неоднозначности, которые могут возникать из поисков членов в интерфейсах с множественным наследованием, описаны в разделе доступ к членам интерфейса.The ambiguities that can possibly arise from member lookups in multiple-inheritance interfaces are described in Interface member access.

Базовые типыBase types

В целях уточняющего запроса члена тип T считается следующими базовыми типами:For purposes of member lookup, a type T is considered to have the following base types:

  • Если T равно object, то T не имеет базового типа.If T is object, then T has no base type.
  • Если T является enum_type, то базовые типы T являются типами классов System.Enum, System.ValueType и object.If T is an enum_type, the base types of T are the class types System.Enum, System.ValueType, and object.
  • Если T является struct_type, то базовые типы T являются типами классов System.ValueType и object.If T is a struct_type, the base types of T are the class types System.ValueType and object.
  • Если T является class_type, то базовые типы T являются базовыми классами T, включая тип класса object.If T is a class_type, the base types of T are the base classes of T, including the class type object.
  • Если T является interface_type, то базовые типы T являются базовыми интерфейсами T и типом класса object.If T is an interface_type, the base types of T are the base interfaces of T and the class type object.
  • Если T является array_type, то базовые типы T являются типами классов System.Array и object.If T is an array_type, the base types of T are the class types System.Array and object.
  • Если T является delegate_type, то базовые типы T являются типами классов System.Delegate и object.If T is a delegate_type, the base types of T are the class types System.Delegate and object.

Члены функцийFunction members

Члены функций — это члены, содержащие исполняемые операторы.Function members are members that contain executable statements. Члены функций всегда являются членами типов и не могут быть членами пространств имен.Function members are always members of types and cannot be members of namespaces. C#определяет следующие категории членов функций:C# defines the following categories of function members:

  • МетодыMethods
  • СвойстваProperties
  • СобытияEvents
  • ИндексаторыIndexers
  • Определяемые пользователем операторыUser-defined operators
  • Конструкторы экземпляровInstance constructors
  • Статические конструкторыStatic constructors
  • ДеструкторыDestructors

За исключением деструкторов и статических конструкторов (которые не могут вызываться явным образом), инструкции, содержащиеся в функциях, выполняются через вызовы членов функций.Except for destructors and static constructors (which cannot be invoked explicitly), the statements contained in function members are executed through function member invocations. Фактический синтаксис для написания вызова функции-члена зависит от определенной категории члена функции.The actual syntax for writing a function member invocation depends on the particular function member category.

Список аргументов (списки аргументов) вызова функции-члена предоставляет фактические значения или ссылки на переменные для параметров члена функции.The argument list (Argument lists) of a function member invocation provides actual values or variable references for the parameters of the function member.

Вызовы универсальных методов могут использовать определение типа для определения набора аргументов типа для передачи в метод.Invocations of generic methods may employ type inference to determine the set of type arguments to pass to the method. Этот процесс описан в разделе Определение типа.This process is described in Type inference.

Вызовы методов, индексаторов, операторов и конструкторов экземпляров используют разрешение перегрузки для определения того, какой из потенциальных наборов вызываемых функций выдается.Invocations of methods, indexers, operators and instance constructors employ overload resolution to determine which of a candidate set of function members to invoke. Этот процесс описан в разделе разрешение перегрузки.This process is described in Overload resolution.

После определения определенного члена функции во время привязки, возможно, посредством разрешения перегрузки, фактический процесс выполнения члена функции во время компиляции описывается в разделе Проверка динамического разрешения перегрузки при компилировании.Once a particular function member has been identified at binding-time, possibly through overload resolution, the actual run-time process of invoking the function member is described in Compile-time checking of dynamic overload resolution.

В следующей таблице приведена сводка обработки, выполняемой в конструкциях, в которых используются шесть категорий членов функций, которые могут быть явно вызваны.The following table summarizes the processing that takes place in constructs involving the six categories of function members that can be explicitly invoked. В таблице e, x, y и value обозначают выражения, классифицированные как переменные или значения, T обозначает выражение, классифицированное как тип, F — это простое имя метода, а P — простое имя свойства.In the table, e, x, y, and value indicate expressions classified as variables or values, T indicates an expression classified as a type, F is the simple name of a method, and P is the simple name of a property.

СозданияConstruct ПримерExample ОписаниеDescription
Вызов методаMethod invocation F(x,y) Разрешение перегрузки применяется для выбора лучшего метода F в содержащем его классе или структуре.Overload resolution is applied to select the best method F in the containing class or struct. Метод вызывается со списком аргументов (x,y).The method is invoked with the argument list (x,y). Если метод не static, то выражением экземпляра является this.If the method is not static, the instance expression is this.
T.F(x,y) Разрешение перегрузки применяется для выбора лучшего метода F в классе или структуре T.Overload resolution is applied to select the best method F in the class or struct T. Если метод не static, возникает ошибка времени привязки.A binding-time error occurs if the method is not static. Метод вызывается со списком аргументов (x,y).The method is invoked with the argument list (x,y).
e.F(x,y) Разрешение перегрузки применяется для выбора лучшего метода F в классе, структуре или интерфейсе, заданном типом e.Overload resolution is applied to select the best method F in the class, struct, or interface given by the type of e. Если метод имеет значение static, возникает ошибка времени привязки.A binding-time error occurs if the method is static. Метод вызывается с выражением экземпляра e, а список аргументов (x,y).The method is invoked with the instance expression e and the argument list (x,y).
Доступ к свойствуProperty access P Вызывается метод доступа get свойства P в содержащем его классе или структуре.The get accessor of the property P in the containing class or struct is invoked. Ошибка времени компиляции возникает, если P только для записи.A compile-time error occurs if P is write-only. Если P не static, то выражением экземпляра является this.If P is not static, the instance expression is this.
P = value Метод доступа set свойства P в содержащем классе или структуре вызывается со списком аргументов (value).The set accessor of the property P in the containing class or struct is invoked with the argument list (value). Ошибка времени компиляции возникает, если P доступен только для чтения.A compile-time error occurs if P is read-only. Если P не static, то выражением экземпляра является this.If P is not static, the instance expression is this.
T.P Вызывается метод доступа get свойства P в классе или структуре T.The get accessor of the property P in the class or struct T is invoked. Ошибка времени компиляции возникает, если P не static или если P доступен только для записи.A compile-time error occurs if P is not static or if P is write-only.
T.P = value Метод доступа set свойства P в классе или структуре T вызывается со списком аргументов (value).The set accessor of the property P in the class or struct T is invoked with the argument list (value). Ошибка времени компиляции возникает, если P не static или если P доступен только для чтения.A compile-time error occurs if P is not static or if P is read-only.
e.P Метод доступа get свойства P в классе, структуре или интерфейсе, заданном типом e, вызывается с выражением экземпляра e.The get accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e. Ошибка времени привязки возникает, если P равно static или если P — только для записи.A binding-time error occurs if P is static or if P is write-only.
e.P = value Метод доступа set свойства P в классе, структуре или интерфейсе, заданном типом e, вызывается с выражением экземпляра e и списком аргументов (value).The set accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e and the argument list (value). Ошибка времени привязки возникает, если P равно static или если P доступен только для чтения.A binding-time error occurs if P is static or if P is read-only.
Доступ к событиямEvent access E += value Вызывается метод доступа add для события E в содержащем его классе или структуре.The add accessor of the event E in the containing class or struct is invoked. Если E не является статическим, выражение экземпляра this.If E is not static, the instance expression is this.
E -= value Вызывается метод доступа remove для события E в содержащем его классе или структуре.The remove accessor of the event E in the containing class or struct is invoked. Если E не является статическим, выражение экземпляра this.If E is not static, the instance expression is this.
T.E += value Вызывается метод доступа add события E в классе или структуре T.The add accessor of the event E in the class or struct T is invoked. Ошибка времени привязки возникает, если E не является статическим.A binding-time error occurs if E is not static.
T.E -= value Вызывается метод доступа remove события E в классе или структуре T.The remove accessor of the event E in the class or struct T is invoked. Ошибка времени привязки возникает, если E не является статическим.A binding-time error occurs if E is not static.
e.E += value Метод доступа add для события E в классе, структуре или интерфейсе, заданном типом e, вызывается с выражением экземпляра e.The add accessor of the event E in the class, struct, or interface given by the type of e is invoked with the instance expression e. Ошибка времени привязки возникает, если E является статическим.A binding-time error occurs if E is static.
e.E -= value Метод доступа remove для события E в классе, структуре или интерфейсе, заданном типом e, вызывается с выражением экземпляра e.The remove accessor of the event E in the class, struct, or interface given by the type of e is invoked with the instance expression e. Ошибка времени привязки возникает, если E является статическим.A binding-time error occurs if E is static.
Доступ к индексаторуIndexer access e[x,y] Разрешение перегрузки применяется для выбора лучшего индексатора в классе, структуре или интерфейсе, заданном типом e.Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. Метод доступа get для индексатора вызывается с выражением экземпляра e и списком аргументов (x,y).The get accessor of the indexer is invoked with the instance expression e and the argument list (x,y). Если индексатор доступен только для записи, возникает ошибка времени привязки.A binding-time error occurs if the indexer is write-only.
e[x,y] = value Разрешение перегрузки применяется для выбора лучшего индексатора в классе, структуре или интерфейсе, заданном типом e.Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. Метод доступа set для индексатора вызывается с выражением экземпляра e и списком аргументов (x,y,value).The set accessor of the indexer is invoked with the instance expression e and the argument list (x,y,value). Если индексатор доступен только для чтения, возникает ошибка времени привязки.A binding-time error occurs if the indexer is read-only.
Вызов оператораOperator invocation -x Разрешение перегрузки применяется для выбора лучшего унарного оператора в классе или структуре, заданной типом x.Overload resolution is applied to select the best unary operator in the class or struct given by the type of x. Выбранный оператор вызывается со списком аргументов (x).The selected operator is invoked with the argument list (x).
x + y Разрешение перегрузки применяется для выбора лучшего бинарного оператора в классах или структурах, заданных типами x и y.Overload resolution is applied to select the best binary operator in the classes or structs given by the types of x and y. Выбранный оператор вызывается со списком аргументов (x,y).The selected operator is invoked with the argument list (x,y).
Вызов конструктора экземпляровInstance constructor invocation new T(x,y) Разрешение перегрузки применяется для выбора лучшего конструктора экземпляров в классе или структуре T.Overload resolution is applied to select the best instance constructor in the class or struct T. Конструктор экземпляра вызывается со списком аргументов (x,y).The instance constructor is invoked with the argument list (x,y).

Списки аргументовArgument lists

Каждый вызов функции и делегата включает список аргументов, который предоставляет фактические значения или ссылки на переменные для параметров члена функции.Every function member and delegate invocation includes an argument list which provides actual values or variable references for the parameters of the function member. Синтаксис для указания списка аргументов вызова функции-члена зависит от категории члена функции:The syntax for specifying the argument list of a function member invocation depends on the function member category:

  • Для конструкторов экземпляров, методов, индексаторов и делегатов аргументы задаются как argument_list, как описано ниже.For instance constructors, methods, indexers and delegates, the arguments are specified as an argument_list, as described below. Для индексаторов при вызове метода доступа set список аргументов также включает выражение, указанное в качестве правого операнда оператора присваивания.For indexers, when invoking the set accessor, the argument list additionally includes the expression specified as the right operand of the assignment operator.
  • Для свойств список аргументов пуст при вызове метода доступа get и состоит из выражения, указанного в качестве правого операнда оператора присваивания при вызове метода доступа set.For properties, the argument list is empty when invoking the get accessor, and consists of the expression specified as the right operand of the assignment operator when invoking the set accessor.
  • Для событий список аргументов состоит из выражения, указанного в качестве правого операнда оператора += или -=.For events, the argument list consists of the expression specified as the right operand of the += or -= operator.
  • Для определяемых пользователем операторов список аргументов состоит из одного операнда унарного оператора или двух операндов бинарного оператора.For user-defined operators, the argument list consists of the single operand of the unary operator or the two operands of the binary operator.

Аргументы свойств (Свойства), события (события) и определяемые пользователем операторы (Операторы) всегда передаются как параметры значений (Параметры значения).The arguments of properties (Properties), events (Events), and user-defined operators (Operators) are always passed as value parameters (Value parameters). Аргументы индексаторов (индексаторы) всегда передаются как параметры значений (Параметры значений) или массивы параметров (массивы параметров).The arguments of indexers (Indexers) are always passed as value parameters (Value parameters) or parameter arrays (Parameter arrays). Параметры reference и OUTPUT не поддерживаются для этих категорий членов функций.Reference and output parameters are not supported for these categories of function members.

Аргументы конструктора экземпляра, метода, индексатора или вызова делегата указаны как argument_list:The arguments of an instance constructor, method, indexer or delegate invocation are specified as an argument_list:

argument_list
    : argument (',' argument)*
    ;

argument
    : argument_name? argument_value
    ;

argument_name
    : identifier ':'
    ;

argument_value
    : expression
    | 'ref' variable_reference
    | 'out' variable_reference
    ;

Argument_list состоит из одного или нескольких аргументов, разделенных запятыми.An argument_list consists of one or more arguments, separated by commas. Каждый аргумент состоит из необязательного argument_name , за которым следует argument_value.Each argument consists of an optional argument_name followed by an argument_value. Аргумент с argument_name называется именованным аргументом, в то время как аргумент без argument_name является положением аргумента.An argument with an argument_name is referred to as a named argument, whereas an argument without an argument_name is a positional argument. Является ошибкой то, что аргумент позиционирования должен отображаться после именованного аргумента в argument_list.It is an error for a positional argument to appear after a named argument in an argument_list.

Argument_value может принимать одну из следующих форм:The argument_value can take one of the following forms:

Соответствующие параметрыCorresponding parameters

Для каждого аргумента в списке аргументов должен быть соответствующий параметр в вызываемом члене функции или делегате.For each argument in an argument list there has to be a corresponding parameter in the function member or delegate being invoked.

Список параметров, используемый в следующем примере, определяется следующим образом.The parameter list used in the following is determined as follows:

  • Для виртуальных методов и индексаторов, определенных в классах, список параметров выбирается из наиболее конкретного объявления или переопределения члена функции, начиная со статического типа получателя и заканчивая поиском по его базовым классам.For virtual methods and indexers defined in classes, the parameter list is picked from the most specific declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes.
  • Для методов интерфейса и индексаторов список параметров выбирается в виде наиболее конкретного определения элемента, начиная с типа интерфейса и заканчивая базовыми интерфейсами.For interface methods and indexers, the parameter list is picked form the most specific definition of the member, starting with the interface type and searching through the base interfaces. Если уникальный список параметров не найден, список параметров с недоступными именами и необязательными параметрами не создается, поэтому вызовы не могут использовать именованные параметры или опускать необязательные аргументы.If no unique parameter list is found, a parameter list with inaccessible names and no optional parameters is constructed, so that invocations cannot use named parameters or omit optional arguments.
  • Для разделяемых методов используется список параметров объявления разделяемого метода.For partial methods, the parameter list of the defining partial method declaration is used.
  • Для всех других функций-членов и делегатов существует только один список параметров, который используется.For all other function members and delegates there is only a single parameter list, which is the one used.

Расположение аргумента или параметра определяется как число аргументов или параметров, предшествующих ему в списке аргументов или списке параметров.The position of an argument or parameter is defined as the number of arguments or parameters preceding it in the argument list or parameter list.

Соответствующие параметры для аргументов члена функции устанавливаются следующим образом:The corresponding parameters for function member arguments are established as follows:

  • Аргументы в argument_list конструкторов экземпляров, методов, индексаторов и делегатов:Arguments in the argument_list of instance constructors, methods, indexers and delegates:
    • Заданный аргумент, в котором фиксированный параметр находится в той же позиции в списке параметров, соответствует этому параметру.A positional argument where a fixed parameter occurs at the same position in the parameter list corresponds to that parameter.
    • Заданный аргумент члена функции с массивом параметров, вызываемым в нормальной форме, соответствует массиву параметров, который должен находиться в той же позиции в списке параметров.A positional argument of a function member with a parameter array invoked in its normal form corresponds to the parameter array, which must occur at the same position in the parameter list.
    • Позиционированный аргумент члена функции с массивом параметров, вызванный в развернутой форме, где нет фиксированных параметров в той же позиции в списке параметров, соответствует элементу в массиве параметров.A positional argument of a function member with a parameter array invoked in its expanded form, where no fixed parameter occurs at the same position in the parameter list, corresponds to an element in the parameter array.
    • Именованный аргумент соответствует параметру с тем же именем в списке параметров.A named argument corresponds to the parameter of the same name in the parameter list.
    • Для индексаторов при вызове метода доступа set выражение, указанное в качестве правого операнда оператора присваивания, соответствует неявному параметру value объявления метода доступа set.For indexers, when invoking the set accessor, the expression specified as the right operand of the assignment operator corresponds to the implicit value parameter of the set accessor declaration.
  • Для свойств при вызове метода доступа get аргументы отсутствуют.For properties, when invoking the get accessor there are no arguments. При вызове метода доступа set выражение, указанное как правый операнд оператора присваивания, соответствует неявному параметру value объявления метода доступа set.When invoking the set accessor, the expression specified as the right operand of the assignment operator corresponds to the implicit value parameter of the set accessor declaration.
  • Для определяемых пользователем унарных операторов (включая преобразования) один операнд соответствует единственному параметру объявления оператора.For user-defined unary operators (including conversions), the single operand corresponds to the single parameter of the operator declaration.
  • Для определяемых пользователем бинарных операторов левый операнд соответствует первому параметру, а правый операнд соответствует второму параметру объявления оператора.For user-defined binary operators, the left operand corresponds to the first parameter, and the right operand corresponds to the second parameter of the operator declaration.

Вычисление списков аргументов во время выполненияRun-time evaluation of argument lists

Во время выполнения обработки вызова функции-члена (проверки динамического разрешения перегрузки во время компиляции) выражения или ссылки на переменные списка аргументов вычисляются по порядку слева направо, как показано ниже:During the run-time processing of a function member invocation (Compile-time checking of dynamic overload resolution), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows:

  • Для параметра значения вычисляется выражение аргумента и выполняется неявное преобразование (неявные преобразования) в соответствующий тип параметра.For a value parameter, the argument expression is evaluated and an implicit conversion (Implicit conversions) to the corresponding parameter type is performed. Полученное значение станет начальным значением параметра value в вызове функции.The resulting value becomes the initial value of the value parameter in the function member invocation.
  • Для ссылочного или выходного параметра вычисляется ссылка на переменную, а результирующее место хранения становится местом хранения, представленным параметром в вызове члена функции.For a reference or output parameter, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. Если ссылка на переменную, заданную как ссылочный или выходной параметр, является элементом массива reference_type, выполняется проверка во время выполнения, чтобы гарантировать, что тип элемента массива идентичен типу параметра.If the variable reference given as a reference or output parameter is an array element of a reference_type, a run-time check is performed to ensure that the element type of the array is identical to the type of the parameter. Если эта проверка завершается неудачей, возникает исключение System.ArrayTypeMismatchException.If this check fails, a System.ArrayTypeMismatchException is thrown.

Методы, индексаторы и конструкторы экземпляров могут объявить их правый параметр как массив параметров (массивы параметров).Methods, indexers, and instance constructors may declare their right-most parameter to be a parameter array (Parameter arrays). Такие функции вызываются либо в нормальной форме, либо в развернутой форме в зависимости от того, какой из них применим (применимый член функции):Such function members are invoked either in their normal form or in their expanded form depending on which is applicable (Applicable function member):

  • Когда член функции с массивом параметров вызывается в своей нормальной форме, аргумент, заданный для массива параметров, должен быть единственным выражением, которое неявно преобразует (неявные преобразования) в тип массива параметров.When a function member with a parameter array is invoked in its normal form, the argument given for the parameter array must be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. В этом случае массив параметров работает точно так же, как параметр значения.In this case, the parameter array acts precisely like a value parameter.
  • При вызове функции-члена с массивом параметров в развернутой форме вызов должен указать ноль или более задающих аргументов для массива параметров, где каждый аргумент представляет собой выражение, которое является неявно преобразуемым (неявные преобразования ) к типу элемента массива параметров.When a function member with a parameter array is invoked in its expanded form, the invocation must specify zero or more positional arguments for the parameter array, where each argument is an expression that is implicitly convertible (Implicit conversions) to the element type of the parameter array. В этом случае вызов создает экземпляр типа массива параметров с длиной, соответствующей количеству аргументов, инициализирует элементы экземпляра массива с заданными значениями аргументов и использует только что созданный экземпляр массива в качестве фактического параметр.In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument.

Выражения списка аргументов всегда оцениваются в том порядке, в котором они записываются.The expressions of an argument list are always evaluated in the order they are written. Таким образом, примерThus, the example

class Test
{
    static void F(int x, int y = -1, int z = -2) {
        System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
    }

    static void Main() {
        int i = 0;
        F(i++, i++, i++);
        F(z: i++, x: i++);
    }
}

выводятся следующие выходные данныеproduces the output

x = 0, y = 1, z = 2
x = 4, y = -1, z = 3

Правила совместной дисперсии массивов (Ковариация массивов) допускают значение типа массива A[], чтобы быть ссылкой на экземпляр типа массива B[], при условии, что существует неявная конвертация ссылки с B на A.The array co-variance rules (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. Из-за этих правил, когда элемент массива reference_type передается в качестве ссылочного или выходного параметра, проверка времени выполнения необходима, чтобы гарантировать, что фактический тип элемента массива идентичен параметру.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. В примереIn the example

class Test
{
    static void F(ref object x) {...}

    static void Main() {
        object[] a = new object[10];
        object[] b = new string[10];
        F(ref a[0]);        // Ok
        F(ref b[1]);        // ArrayTypeMismatchException
    }
}

второй вызов F приводит к возникновению System.ArrayTypeMismatchException, так как фактический тип элемента bstring, а не object.the second invocation of F causes a System.ArrayTypeMismatchException to be thrown because the actual element type of b is string and not object.

При вызове функции-члена с массивом параметров в развернутой форме вызов обрабатывается точно так же, как если бы выражение создания массива с инициализатором массива (выражения создания массива) было вставлено вокруг развернутых параметров.When a function member with a parameter array is invoked in its expanded form, the invocation is processed exactly as if an array creation expression with an array initializer (Array creation expressions) was inserted around the expanded parameters. Например, с учетом объявленияFor example, given the declaration

void F(int x, int y, params object[] args);

следующие вызовы расширенной формы методаthe following invocations of the expanded form of the method

F(10, 20);
F(10, 20, 30, 40);
F(10, 20, 1, "hello", 3.0);

точно соответствуетcorrespond exactly to

F(10, 20, new object[] {});
F(10, 20, new object[] {30, 40});
F(10, 20, new object[] {1, "hello", 3.0});

В частности, обратите внимание, что пустой массив создается, если для массива параметров заданы нулевые аргументы.In particular, note that an empty array is created when there are zero arguments given for the parameter array.

Если аргументы в члене функции с соответствующими необязательными параметрами опущены, неявно передаются аргументы по умолчанию для объявления члена функции.When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. Так как они всегда постоянны, их вычисление не повлияет на порядок вычисления оставшихся аргументов.Because these are always constant, their evaluation will not impact the evaluation order of the remaining arguments.

Определение типаType inference

При вызове универсального метода без указания аргументов типа процесс определения типа пытается определить аргументы типа для вызова.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. Например, при объявлении метода:For example, given the method declaration:

class Chooser
{
    static Random rand = new Random();

    public static T Choose<T>(T first, T second) {
        return (rand.Next(2) == 0)? first: second;
    }
}

можно вызвать метод Choose без явного указания аргумента типа:it is possible to invoke the Choose method without explicitly specifying a type argument:

int i = Chooser.Choose(5, 213);                 // Calls Choose<int>

string s = Chooser.Choose("foo", "bar");        // Calls Choose<string>

С помощью вывода типа аргументы типа int и string определяются из аргументов метода.Through type inference, the type arguments int and string are determined from the arguments to the method.

Определение типа происходит как часть обработки вызова метода во время привязки (вызовы метода) и выполняется перед этапом разрешения перегрузки вызова.Type inference occurs as part of the binding-time processing of a method invocation (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 actual type arguments for the invocation. Если вывод типа для определенного метода завершается ошибкой, этот метод не участвует в разрешении перегрузки.If type inference for a particular method fails, that method does not participate in overload resolution. Сбой вывода типа в и самого себя не приводит к ошибке времени привязки.The failure of type inference, in and of itself, does not cause a binding-time error. Однако часто возникает ошибка времени привязки, когда при разрешении перегрузки не удается найти применимые методы.However, it often leads to a binding-time error when overload resolution then fails to find any applicable methods.

Если заданное число аргументов отличается от числа параметров в методе, то вывод немедленно завершается ошибкой.If the supplied number of arguments is different than the number of parameters in the method, then inference immediately fails. В противном случае предположим, что универсальный метод имеет следующую сигнатуру:Otherwise, assume that the generic method has the following signature:

Tr M<X1,...,Xn>(T1 x1, ..., Tm xm)

При вызове метода в форме M(E1...Em) задача вывода типа — найти уникальные аргументы типа S1...Sn для каждого из параметров типа X1...Xn, чтобы вызов M<S1...Sn>(E1...Em) стал допустимым.With a method call of the form M(E1...Em) the task of type inference is to find unique type arguments S1...Sn for each of the type parameters X1...Xn so that the call M<S1...Sn>(E1...Em) becomes valid.

Во время процесса вывода каждый параметр типа Xi фиксируется в определенном типе Si или нефиксированном с связанным набором границ.During the process of inference each type parameter Xi is either fixed to a particular type Si or unfixed with an associated set of bounds. Каждый из границ имеет тип T.Each of the bounds is some type T. Изначально каждая переменная типа, Xi, не фиксирована с пустым набором границ.Initially each type variable Xi is unfixed with an empty set of bounds.

Определение типа выполняется поэтапно.Type inference takes place in phases. На каждом этапе будет предпринята попытка определить аргументы типа для дополнительных переменных типа на основе результатов предыдущего этапа.Each phase will try to infer type arguments for more type variables based on the findings of the previous phase. Первый этап выполняет некоторые начальные выводы границ, тогда как второй этап исправляет переменные типа в конкретные типы и выводит дополнительные границы.The first phase makes some initial inferences of bounds, whereas the second phase fixes type variables to specific types and infers further bounds. Второй этап может потребоваться повторить несколько раз.The second phase may have to be repeated a number of times.

Примечание. Вывод типа происходит не только при вызове универсального метода.Note: Type inference takes place not only when a generic method is called. Определение типа для преобразования групп методов описывается в разделе Определение типа для преобразования групп методов и поиск наиболее общего типа набора выражений описывается в разделе Поиск наиболее общего типа набора выражений.Type inference for conversion of method groups is described in Type inference for conversion of method groups and finding the best common type of a set of expressions is described in Finding the best common type of a set of expressions.

Первый этапThe first phase

Для каждого из аргументов метода Ei:For each of the method arguments Ei:

  • Если Ei является анонимной функцией, вывод явного типа параметра (явное определение типа параметра) выполняется из Ei в TiIf Ei is an anonymous function, an explicit parameter type inference (Explicit parameter type inferences) is made from Ei to Ti
  • В противном случае, если Ei имеет тип U, а xi является параметром значения, то выведение с нижней границей выполняется из U в Ti.Otherwise, if Ei has a type U and xi is a value parameter then a lower-bound inference is made from U to Ti.
  • В противном случае, если Ei имеет тип U, а xi — параметр ref или out, то точное определение выполняется из U в Ti.Otherwise, if Ei has a type U and xi is a ref or out parameter then an exact inference is made from U to Ti.
  • В противном случае вывод этого аргумента не выполняется.Otherwise, no inference is made for this argument.

Второй этапThe second phase

Второй этап продолжается следующим образом.The second phase proceeds as follows:

  • Все переменные нефиксированного типа Xi, которые не зависят от (зависимости) Xj, являются фиксированными (исправление).All unfixed type variables Xi which do not depend on (Dependence) any Xj are fixed (Fixing).
  • Если таких переменных типа не существует, все переменные нефиксированного типа, Xi, фиксированы , для которых выполняется следующее удержание:If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold:
    • Существует по меньшей мере одна переменная типа Xj, которая зависит от XiThere is at least one type variable Xj that depends on Xi
    • Xi имеет непустой набор границXi has a non-empty set of bounds
  • Если таких переменных типа не существует и по-прежнему существуют нефиксированные переменные типа, определение типа завершается ошибкой.If no such type variables exist and there are still unfixed type variables, type inference fails.
  • В противном случае, если еще не существует переменных нефиксированного типа, вывод типа будет выполнен.Otherwise, if no further unfixed type variables exist, type inference succeeds.
  • В противном случае для всех аргументов Ei с соответствующим типом параметра Ti, где типы выходных данных (типы вывода) содержат переменные нефиксированного типа Xj, но типы входных данных (типы ввода) — нет, вывод типа выходных данных (выводтипа вывода) выполняется из 1 в 3.Otherwise, for all arguments Ei with corresponding parameter type Ti where the output types (Output types) contain unfixed type variables Xj but the input types (Input types) do not, an output type inference (Output type inferences) is made from Ei to Ti. Затем второй этап повторяется.Then the second phase is repeated.

Типы входных данныхInput types

Если E является группой методов или неявно типизированной анонимной функцией, а T является типом делегата или типом дерева выражения, то все типы параметров T являются входными типами E с типом T.If E is a method group or implicitly typed anonymous function and T is a delegate type or expression tree type then all the parameter types of T are input types of E with type T.

Типы выходных данныхOutput types

Если E является группой методов или анонимной функцией, а T является типом делегата или типом дерева выражения, то тип возвращаемого значения T — это тип выходных данных E с типом T.If E is a method group or an anonymous function and T is a delegate type or expression tree type then the return type of T is an output type of E with type T.

ОтDependence

Нефиксированная переменная типа Xi зависит непосредственно от переменной нефиксированного типа Xj, если для некоторого аргумента Ek с типом Tk Xj происходит во входном типе Ek с типом Tk и 0 происходит в Тип выходных данных 2 с типом 3.An unfixed type variable Xi depends directly on an unfixed type variable Xj if for some argument Ek with type Tk Xj occurs in an input type of Ek with type Tk and Xi occurs in an output type of Ek with type Tk.

Xj зависит от Xi, если Xj зависит непосредственно от Xi или если Xi зависят непосредственно от Xk и Xk зависят от 1.Xj depends on Xi if Xj depends directly on Xi or if Xi depends directly on Xk and Xk depends on Xj. Таким словами, "зависит от" — это транзитивное, но не перегибкое замыкание "зависит непосредственно от".Thus "depends on" is the transitive but not reflexive closure of "depends directly on".

Вывод типа выходных данныхOutput type inferences

Вывод типа выходных данных выполняется из выражения E к типу T следующим образом:An output type inference is made from an expression E to a type T in the following way:

  • Если E является анонимной функцией с выводимым типом возвращаемого значения U (выводимый тип возвращаемогозначения), а T — тип делегата или тип дерева выражения с типом возвращаемого значения Tb, то выведение с нижним связыванием (вывод нижних границ) выполняется из U в 0.If E is an anonymous function with inferred return type U (Inferred return type) and T is a delegate type or expression tree type with return type Tb, then a lower-bound inference (Lower-bound inferences) is made from U to Tb.
  • В противном случае, если E является группой методов, а T является типом делегата или типом дерева выражения с типами параметров T1...Tk и типом возвращаемого значения Tb, а разрешение перегрузки E с типами T1...Tk дает один метод с типом возвращаемого значения U. , выведение с более низкой границей выполняется из U в 1.Otherwise, if E is a method group and T is a delegate type or expression tree type with parameter types T1...Tk and return type Tb, and overload resolution of E with the types T1...Tk yields a single method with return type U, then a lower-bound inference is made from U to Tb.
  • В противном случае, если E является выражением с типом U, выведение с нижней границей выполняется из U в T.Otherwise, if E is an expression with type U, then a lower-bound inference is made from U to T.
  • В противном случае вывод не выполняется.Otherwise, no inferences are made.

Явные определения типов параметровExplicit parameter type inferences

Вывод явного типа параметра выполняется из выражения E к типу T следующим образом:An explicit parameter type inference is made from an expression E to a type T in the following way:

  • Если E является явно типизированной анонимной функцией с типами параметров U1...Uk, а T является типом делегата или типом дерева выражения с типами параметров V1...Vk, то для каждого Ui выполняется точный вывод (точные выводы) . от @no__t до 8 до соответствующего 0.If E is an explicitly typed anonymous function with parameter types U1...Uk and T is a delegate type or expression tree type with parameter types V1...Vk then for each Ui an exact inference (Exact inferences) is made from Ui to the corresponding Vi.

Точные выводыExact inferences

Точный вывод типа U в тип V выполняется следующим образом:An exact inference from a type U to a type V is made as follows:

  • Если V является одним из неисправленных Xi, то U добавляется в набор точных границ для Xi.If V is one of the unfixed Xi then U is added to the set of exact bounds for Xi.

  • В противном случае наборы V1...Vk и U1...Uk определяются с помощью проверки, применимы ли в одном из следующих вариантов:Otherwise, sets V1...Vk and U1...Uk are determined by checking if any of the following cases apply:

    • V является типом массива V1[...], а U — типом массива U1[...] того же ранга.V is an array type V1[...] and U is an array type U1[...] of the same rank
    • V является типом V1?, а U — типом U1?V is the type V1? and U is the type U1?
    • V является сконструированным типом C<V1...Vk>and U является сконструированным типом C<U1...Uk>V is a constructed type C<V1...Vk>and U is a constructed type C<U1...Uk>

    Если какой-либо из этих случаев применяется, выполняется точное получение из каждого Ui к соответствующему @no__t 4.If any of these cases apply then an exact inference is made from each Ui to the corresponding Vi.

  • В противном случае вывод не выполняется.Otherwise no inferences are made.

Вывод нижних границLower-bound inferences

Вывод с нижней границей из типа U в тип V выполняется следующим образом:A lower-bound inference from a type U to a type V is made as follows:

  • Если V является одним из неисправленных Xi, то U добавляется в набор нижних границ для Xi.If V is one of the unfixed Xi then U is added to the set of lower bounds for Xi.

  • В противном случае, если V является типом V1?and U является типом U1?, выведение нижней границы выполняется из U1 в V1.Otherwise, if V is the type V1?and U is the type U1? then a lower bound inference is made from U1 to V1.

  • В противном случае наборы U1...Uk и V1...Vk определяются с помощью проверки, применимы ли в одном из следующих вариантов:Otherwise, sets U1...Uk and V1...Vk are determined by checking if any of the following cases apply:

    • V является типом массива V1[...], а U — типом массива U1[...] (или параметр типа, действующий базовый тип которого — U1[...]) того же ранга.V is an array type V1[...] and U is an array type U1[...] (or a type parameter whose effective base type is U1[...]) of the same rank

    • V является одним из IEnumerable<V1>, ICollection<V1> или IList<V1> и U — одномерный массив U1[] (или параметр типа, действующий базовый тип U1[]).V is one of IEnumerable<V1>, ICollection<V1> or IList<V1> and U is a one-dimensional array type U1[](or a type parameter whose effective base type is U1[])

    • V — это сконструированный класс, структура, интерфейс или тип делегата C<V1...Vk>, а также уникальный тип C<U1...Uk>, такой как U (или, если U является параметром типа, его действующий базовый класс или любой член его действующего набора интерфейсов) идентичен , наследуется от (прямо или косвенно) или реализует (прямо или косвенно) C<U1...Uk>.V is a constructed class, struct, interface or delegate type C<V1...Vk> and there is a unique type C<U1...Uk> such that U (or, if U is a type parameter, its effective base class or any member of its effective interface set) is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) C<U1...Uk>.

      (Ограничение уникальности означает, что в интерфейсе вариантов C<T> {} class U: C<X>, C<Y> {} не выполняется вывод при получении от U до C<T>, так как U1 может быть X или Y.)(The "uniqueness" restriction means that in the case interface C<T> {} class U: C<X>, C<Y> {}, then no inference is made when inferring from U to C<T> because U1 could be X or Y.)

    Если какой-либо из этих случаев применяется, то вывод выполняется из каждого Ui в соответствующее Vi следующим образом:If any of these cases apply then an inference is made from each Ui to the corresponding Vi as follows:

    • Если Ui не является ссылочным типом, выполняется точное определениеIf Ui is not known to be a reference type then an exact inference is made
    • В противном случае, если U является типом массива, выполняется вывод с нижней границейOtherwise, if U is an array type then a lower-bound inference is made
    • В противном случае, если V равно C<V1...Vk>, то вывод зависит от параметра i-TH типа в C:Otherwise, if V is C<V1...Vk> then inference depends on the i-th type parameter of C:
      • Если он является ковариантным, выполняется вывод с нижней границей .If it is covariant then a lower-bound inference is made.
      • Если он является контравариантным, выполняется Вывод верхней границы .If it is contravariant then an upper-bound inference is made.
      • Если он инвариантен, выполняется точное определение .If it is invariant then an exact inference is made.
  • В противном случае вывод не выполняется.Otherwise, no inferences are made.

Вывод с верхней границейUpper-bound inferences

Вывод верхней границы типа U в тип V выполняется следующим образом:An upper-bound inference from a type U to a type V is made as follows:

  • Если V является одним из неисправленных Xi, то U добавляется в набор верхних границ для Xi.If V is one of the unfixed Xi then U is added to the set of upper bounds for Xi.

  • В противном случае наборы V1...Vk и U1...Uk определяются с помощью проверки, применимы ли в одном из следующих вариантов:Otherwise, sets V1...Vk and U1...Uk are determined by checking if any of the following cases apply:

    • U является типом массива U1[...], а V — типом массива V1[...] того же ранга.U is an array type U1[...] and V is an array type V1[...] of the same rank

    • U является одним из IEnumerable<Ue>, ICollection<Ue> или IList<Ue>, а V — одномерным массивом Ve[]U is one of IEnumerable<Ue>, ICollection<Ue> or IList<Ue> and V is a one-dimensional array type Ve[]

    • U является типом U1?, а V — типом V1?U is the type U1? and V is the type V1?

    • U сконструирован классом, структурой, интерфейсом или типом делегата C<U1...Uk>, а V — класс, структура, интерфейс или тип делегата, который идентичен, наследуется от (прямо или косвенно) или реализует (прямо или косвенно) уникальный тип C<V1...Vk>U is constructed class, struct, interface or delegate type C<U1...Uk> and V is a class, struct, interface or delegate type which is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) a unique type C<V1...Vk>

      (Ограничение уникальности означает, что если у нас есть interface C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}, вывод не выполняется при получении от C<U1> до V<Q>.(The "uniqueness" restriction means that if we have interface C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}, then no inference is made when inferring from C<U1> to V<Q>. Вывод не выполняется из U1 в X<Q> или Y<Q>.)Inferences are not made from U1 to either X<Q> or Y<Q>.)

    Если какой-либо из этих случаев применяется, то вывод выполняется из каждого Ui в соответствующее Vi следующим образом:If any of these cases apply then an inference is made from each Ui to the corresponding Vi as follows:

    • Если Ui не является ссылочным типом, выполняется точное определениеIf Ui is not known to be a reference type then an exact inference is made
    • В противном случае, если V является типом массива, выполняется вывод с верхней границейOtherwise, if V is an array type then an upper-bound inference is made
    • В противном случае, если U равно C<U1...Uk>, то вывод зависит от параметра i-TH типа в C:Otherwise, if U is C<U1...Uk> then inference depends on the i-th type parameter of C:
      • Если он является ковариантным, выполняется Вывод верхней границы .If it is covariant then an upper-bound inference is made.
      • Если он является контравариантным, выполняется вывод с более низкой границей .If it is contravariant then a lower-bound inference is made.
      • Если он инвариантен, выполняется точное определение .If it is invariant then an exact inference is made.
  • В противном случае вывод не выполняется.Otherwise, no inferences are made.

ЕеFixing

Нефиксированная переменная типа Xi с набором границ фиксируется следующим образом:An unfixed type variable Xi with a set of bounds is fixed as follows:

  • Набор типов кандидатов Uj запускается как набор всех типов в наборе границ для Xi.The set of candidate types Uj starts out as the set of all types in the set of bounds for Xi.
  • Затем мы рассмотрим каждую границу для Xi, в свою очередь: Для каждого точного привязанного U из Xi все типы Uj, которые не идентичны U, удаляются из набора кандидатов.We then examine each bound for Xi in turn: For each exact bound U of Xi all types Uj which are not identical to U are removed from the candidate set. Для каждой нижней границы U из Xi всех типов Uj, к которым отсутствует неявное преобразование из U, удаляется из набора кандидатов.For each lower bound U of Xi all types Uj to which there is not an implicit conversion from U are removed from the candidate set. Для каждой верхней границы U из Xi всех типов Uj, из которых не выполняется неявное преобразование в U, удаляются из набора кандидатов.For each upper bound U of Xi all types Uj from which there is not an implicit conversion to U are removed from the candidate set.
  • Если между оставшимися типами кандидатов Uj существует уникальный тип V, из которого происходит неявное преобразование для всех других типов кандидатов, то Xi фиксируется в V.If among the remaining candidate types Uj there is a unique type V from which there is an implicit conversion to all the other candidate types, then Xi is fixed to V.
  • В противном случае вывод типа завершается ошибкой.Otherwise, type inference fails.

Выводимый тип возвращаемого значенияInferred return type

Выводимый тип возвращаемого значения анонимной функции F используется при определении типа и разрешении перегрузки.The inferred return type of an anonymous function F is used during type inference and overload resolution. Выводимый тип возвращаемого значения может быть определен только для анонимной функции, в которой известны все типы параметров, так как они явным образом задаются путем преобразования анонимной функции или выводятся во время определения типа для включающего универсального вызов метода.The inferred return type can only be determined for an anonymous function where all parameter types are known, either because they are explicitly given, provided through an anonymous function conversion or inferred during type inference on an enclosing generic method invocation.

Выводимый тип результата определяется следующим образом:The inferred result type is determined as follows:

  • Если тело F является выражением типа, выводимый тип результата F является типом этого выражения.If the body of F is an expression that has a type, then the inferred result type of F is the type of that expression.
  • Если тело F является блоком , а набор выражений в операторе блока return имеет наиболее распространенный тип T (Поиск наиболее общего типа набора выражений), то выводимый тип результата FT.If the body of F is a block and the set of expressions in the block's return statements has a best common type T (Finding the best common type of a set of expressions), then the inferred result type of F is T.
  • В противном случае тип результата не может быть определен для F.Otherwise, a result type cannot be inferred for F.

Выводимый тип возвращаемого значения определяется следующим образом:The inferred return type is determined as follows:

  • Если F является асинхронным, а текст F является либо выражением, классифицированным как Nothing (классификация выражений), либо блоком операторов, где нет выражений возврата, то выводимый тип возвращаемого значения — System.Threading.Tasks.Task.If F is async and the body of F is either an expression classified as nothing (Expression classifications), or a statement block where no return statements have expressions, the inferred return type is System.Threading.Tasks.Task
  • Если F является асинхронным и имеет выводимый тип результата T, выводимый тип возвращаемого значения — System.Threading.Tasks.Task<T>.If F is async and has an inferred result type T, the inferred return type is System.Threading.Tasks.Task<T>.
  • Если F не является асинхронным и имеет выводимый тип результата T, выводимый тип возвращаемого значения — T.If F is non-async and has an inferred result type T, the inferred return type is T.
  • В противном случае тип возвращаемого значения не может быть определен для F.Otherwise a return type cannot be inferred for F.

В качестве примера вывода типа, включающего анонимные функции, рассмотрим метод расширения Select, объявленный в классе System.Linq.Enumerable:As an example of type inference involving anonymous functions, consider the Select extension method declared in the System.Linq.Enumerable class:

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TResult> Select<TSource,TResult>(
            this IEnumerable<TSource> source,
            Func<TSource,TResult> selector)
        {
            foreach (TSource element in source) yield return selector(element);
        }
    }
}

Предполагая, что пространство имен System.Linq было импортировано с предложением using и имеет класс Customer со свойством Name типа string, для выбора имен списка клиентов можно использовать метод Select.Assuming the System.Linq namespace was imported with a using clause, 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:

List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);

Вызов метода расширения (вызовы метода расширения) Select обрабатывается путем перезаписи вызова в вызов статического метода:The extension method invocation (Extension method invocations) of Select is processed by rewriting the invocation to a static method invocation:

IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);

Поскольку аргументы типа не заданы явно, определение типа используется для вывода аргументов типа.Since type arguments were not explicitly specified, type inference is used to infer the type arguments. Во-первых, аргумент customers связан с параметром source, выведением T в Customer.First, the customers argument is related to the source parameter, inferring T to be Customer. Затем, используя описанный выше процесс определения типа анонимной функции, c получает тип Customer, а выражение c.Name связано с типом возвращаемого значения параметра selector, выдавая S в string.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 S to be string. Таким же вызов эквивалентенThus, the invocation is equivalent to

Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)

результат имеет тип IEnumerable<string>.and the result is of type IEnumerable<string>.

В следующем примере показано, как вывод типа анонимной функции позволяет получить сведения о типе для "Flow" между аргументами в вызове универсального метода.The following example demonstrates how anonymous function type inference allows type information to "flow" between arguments in a generic method invocation. При наличии метода:Given the method:

static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) {
    return f2(f1(value));
}

Определение типа для вызова:Type inference for the invocation:

double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds);

продолжается следующим образом: Во-первых, аргумент "1:15:30" связан с параметром value, выведение X в string.proceeds as follows: First, the argument "1:15:30" is related to the value parameter, inferring X to be string. Затем параметру первой анонимной функции, s, присваивается выводимый тип string, а выражение TimeSpan.Parse(s) связано с типом возвращаемого значения f1, а YSystem.TimeSpan.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. Наконец, параметру второй анонимной функции, t, присваивается выводимый тип System.TimeSpan, а выражение t.TotalSeconds связано с типом возвращаемого значения f2, а Zdouble.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. Таким образом, результат вызова имеет тип double.Thus, the result of the invocation is of type double.

Определение типа для преобразования групп методовType inference for conversion of method groups

Как и вызовы универсальных методов, вывод типа необходимо также применять, когда группа методов M, содержащая универсальный метод, преобразуется в данный тип делегата D (преобразования группы методов).Similar to calls of generic methods, type inference must also be applied when a method group M containing a generic method is converted to a given delegate type D (Method group conversions). При наличии методаGiven a method

Tr M<X1...Xn>(T1 x1 ... Tm xm)

и группа методов M назначается типу делегата D задача вывода типа — найти аргументы типа S1...Sn, чтобы выражение:and the method group M being assigned to the delegate type D the task of type inference is to find type arguments S1...Sn so that the expression:

M<S1...Sn>

преобразуется в совместимый (объявления делегатов) с D.becomes compatible (Delegate declarations) with D.

В отличие от алгоритма определения типа для вызовов универсальных методов, в данном случае существуют только типыаргументов, выраженияаргументов отсутствуют.Unlike the type inference algorithm for generic method calls, in this case there are only argument types, no argument expressions. В частности, нет анонимных функций и, следовательно, нет необходимости в нескольких этапах вывода.In particular, there are no anonymous functions and hence no need for multiple phases of inference.

Вместо этого все Xi считаются нефиксированными, а вывод с нижней границей выполняется из каждого типа аргумента Uj из @no__t -5 в соответствующий тип параметра Tj из M.Instead, all Xi are considered unfixed, and a lower-bound inference is made from each argument type Uj of D to the corresponding parameter type Tj of M. Если для любого из Xi не найдены границы, определение типа завершается ошибкой.If for any of the Xi no bounds were found, type inference fails. В противном случае все @no__t- 0 фиксируются в соответствии с Si, что является результатом вывода типа.Otherwise, all Xi are fixed to corresponding Si, which are the result of type inference.

Поиск наиболее общего типа набора выраженийFinding the best common type of a set of expressions

В некоторых случаях для набора выражений необходимо вывести общий тип.In some cases, a common type needs to be inferred for a set of expressions. В частности, типы элементов неявно типизированных массивов и возвращаемые типы анонимных функций с блоками текста обнаруживаются таким образом.In particular, the element types of implicitly typed arrays and the return types of anonymous functions with block bodies are found in this way.

Интуитивно, учитывая набор выражений E1...Em, этот вывод должен быть эквивалентен вызову метода.Intuitively, given a set of expressions E1...Em this inference should be equivalent to calling a method

Tr M<X>(X x1 ... X xm)

с аргументами Ei в качестве аргументов.with the Ei as arguments.

Точнее, вывод начинается с переменной нефиксированного типа X.More precisely, the inference starts out with an unfixed type variable X. Затем выводятся выходные данные типа вывода из каждого Ei в X.Output type inferences are then made from each Ei to X. Наконец, X является фиксированным и, в случае успеха, полученный тип S является самым лучшим наиболее распространенным типом для выражений.Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions. Если такого S не существует, выражения не имеют наиболее общего типа.If no such S exists, the expressions have no best common type.

Разрешение перегрузкиOverload resolution

Разрешение перегрузки — это механизм времени привязки, позволяющий выбрать наиболее подходящий член функции для вызова по заданному списку аргументов и набору потенциальных членов-функций.Overload resolution is a binding-time mechanism for selecting the best function member to invoke given an argument list and a set of candidate function members. Разрешение перегружаемых функций выбирает член функции для вызова в следующих различных контекстах в C#:Overload resolution selects the function member to invoke in the following distinct contexts within C#:

Каждый из этих контекстов определяет набор потенциальных функций-членов и список аргументов по своему собственному уникальному способу, как подробно описано в разделах, перечисленных выше.Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way, as described in detail in the sections listed above. Например, набор кандидатов для вызова метода не включает методы, помеченные override (Уточняющий запрос), а методы в базовом классе не являются кандидатами, если применим любой метод в производном классе (вызовы методов).For example, the set of candidates for a method invocation does not include methods marked override (Member lookup), and methods in a base class are not candidates if any method in a derived class is applicable (Method invocations).

После определения потенциальных функций-членов и списка аргументов выбор наиболее подходящего члена функции одинаков во всех случаях:Once the candidate function members and the argument list have been identified, the selection of the best function member is the same in all cases:

  • Учитывая набор применимых потенциальных функций-членов, находится лучший член функции в этом наборе.Given the set of applicable candidate function members, the best function member in that set is located. Если набор содержит только один член функции, то этот член функции является лучшим членом функции.If the set contains only one function member, then that function member is the best function member. В противном случае лучшим членом функции является один член функции, который лучше, чем все остальные функции-члены с учетом заданного списка аргументов, при условии, что каждый член функции сравнивается с другими функциями, используя правила в более эффективной функции. элемент.Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in Better function member. Если не существует ровно одного члена функции, отличного от всех остальных функций-членов, то вызов функции-члена неоднозначен и возникает ошибка времени привязки.If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a binding-time error occurs.

В следующих разделах определяется точное значение термина применимого члена функции и более подходящего члена функции.The following sections define the exact meanings of the terms applicable function member and better function member.

Применимый член функцииApplicable function member

Член функции считается применимым членом функции по отношению к списку аргументов A, если выполняются все следующие условия:A function member is said to be an applicable function member with respect to an argument list A when all of the following are true:

  • Каждый аргумент в A соответствует параметру в объявлении члена функции, как описано в соответствующих параметрах, а любой параметр, которому не соответствует ни один аргумент, является необязательным параметром.Each argument in A corresponds to a parameter in the function member declaration as described in Corresponding parameters, and any parameter to which no argument corresponds is an optional parameter.
  • Для каждого аргумента в A режим передачи параметра аргумента (т. е. значение, ref или out) идентичен режиму передачи параметра соответствующего параметра.For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical to the parameter passing mode of the corresponding parameter, and
    • для параметра значения или массива параметров существует неявное преобразование (неявные преобразования) из аргумента в тип соответствующего параметра илиfor a value parameter or a parameter array, an implicit conversion (Implicit conversions) exists from the argument to the type of the corresponding parameter, or
    • для параметра ref или out тип аргумента идентичен типу соответствующего параметра.for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter. В конце концов, параметр ref или out является псевдонимом для передаваемого аргумента.After all, a ref or out parameter is an alias for the argument passed.

Для члена функции, включающего массив параметров, если член функции применим к приведенным выше правилам, он считается применимым в нормальной форме.For a function member that includes a parameter array, if the function member is applicable by the above rules, it is said to be applicable in its normal form. Если член функции, включающий массив параметров, не применим в своей нормальной форме, функция-член может быть применима в ее развернутой форме:If a function member that includes a parameter array is not applicable in its normal form, the function member may instead be applicable in its expanded form:

  • Развернутая форма создается путем замены массива параметров в объявлении члена функции на ноль или более параметров значений типа элемента массива параметров, что число аргументов в списке аргументов A соответствует общему числу параметров.The expanded form is constructed by replacing the parameter array in the function member declaration with zero or more value parameters of the element type of the parameter array such that the number of arguments in the argument list A matches the total number of parameters. Если A меньше аргументов, чем число фиксированных параметров в объявлении члена функции, развернутая форма члена функции не может быть создана и поэтому неприменима.If A has fewer arguments than the number of fixed parameters in the function member declaration, the expanded form of the function member cannot be constructed and is thus not applicable.
  • В противном случае развернутая форма применима, если для каждого аргумента в A режим передачи параметра аргумента идентичен режиму передачи параметра соответствующего параметра.Otherwise, the expanded form is applicable if for each argument in A the parameter passing mode of the argument is identical to the parameter passing mode of the corresponding parameter, and
    • для параметра фиксированного значения или параметра значения, созданного расширением, существует неявное преобразование (неявные преобразования) из типа аргумента в тип соответствующего параметра илиfor a fixed value parameter or a value parameter created by the expansion, an implicit conversion (Implicit conversions) exists from the type of the argument to the type of the corresponding parameter, or
    • для параметра ref или out тип аргумента идентичен типу соответствующего параметра.for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter.

Улучшенный член функцииBetter function member

В целях определения лучшего члена функции в результате создается упрощенный список аргументов A, содержащий только выражения аргументов в том порядке, в котором они отображаются в исходном списке аргументов.For the purposes of determining the better function member, a stripped-down argument list A is constructed containing just the argument expressions themselves in the order they appear in the original argument list.

Списки параметров для каждого из потенциальных функций-членов создаются следующим образом:Parameter lists for each of the candidate function members are constructed in the following way:

  • Развернутая форма используется, если функция-член была применима только в расширенной форме.The expanded form is used if the function member was applicable only in the expanded form.
  • Необязательные параметры без соответствующих аргументов удаляются из списка параметровOptional parameters with no corresponding arguments are removed from the parameter list
  • Порядок параметров переупорядочивается, так что они выполняются в той же позиции, что и соответствующий аргумент в списке аргументов.The parameters are reordered so that they occur at the same position as the corresponding argument in the argument list.

При наличии списка аргументов A с набором выражений аргументов {E1, E2, ..., En} и двумя применимыми членами функции Mp и Mq с типами параметров {P1, P2, ..., Pn} и {Q1, Q2, ..., Qn}, Mp определяется как лучший член функции , чем Mq, еслиGiven an argument list A with a set of argument expressions {E1, E2, ..., En} and two applicable function members Mp and Mq with parameter types {P1, P2, ..., Pn} and {Q1, Q2, ..., Qn}, Mp is defined to be a better function member than Mq if

  • для каждого аргумента неявное преобразование из Ex в Qx не лучше, чем неявное преобразование из Ex в Px.for each argument, the implicit conversion from Ex to Qx is not better than the implicit conversion from Ex to Px, and
  • по крайней мере для одного аргумента преобразование из Ex в Px лучше, чем преобразование из Ex в Qx.for at least one argument, the conversion from Ex to Px is better than the conversion from Ex to Qx.

При выполнении этой оценки, если Mp или Mq применимо в расширенной форме, то Px или Qx ссылается на параметр в расширенной форме списка параметров.When performing this evaluation, if Mp or Mq is applicable in its expanded form, then Px or Qx refers to a parameter in the expanded form of the parameter list.

В случае, если последовательность типов параметров @ no__t-0 и {Q1, Q2, ..., Qn} эквивалентны (т. е. Каждый Pi имеет преобразование идентификатора в соответствующий Qi), для определения лучшего члена функции применяются следующие правила разрыва.In case the parameter type sequences {P1, P2, ..., Pn} and {Q1, Q2, ..., Qn} are equivalent (i.e. each Pi has an identity conversion to the corresponding Qi), the following tie-breaking rules are applied, in order, to determine the better function member.

  • Если Mp является неуниверсальным методом, а Mq является универсальным методом, то Mp лучше, чем Mq.If Mp is a non-generic method and Mq is a generic method, then Mp is better than Mq.
  • В противном случае, если Mp применяется в нормальной форме, а Mq имеет массив params и применим только в его расширенной форме, Mp лучше, чем Mq.Otherwise, if Mp is applicable in its normal form and Mq has a params array and is applicable only in its expanded form, then Mp is better than Mq.
  • В противном случае, если Mp имеет больше объявленных параметров, чем Mq, то Mp лучше, чем Mq.Otherwise, if Mp has more declared parameters than Mq, then Mp is better than Mq. Это может произойти, если оба метода имеют массивы params и применимы только в их расширенных формах.This can occur if both methods have params arrays and are applicable only in their expanded forms.
  • В противном случае, если все параметры Mp имеют соответствующий аргумент, тогда как аргументы по умолчанию необходимо заменить по крайней мере одним необязательным параметром в Mq, Mp лучше, чем Mq.Otherwise if all parameters of Mp have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in Mq then Mp is better than Mq.
  • В противном случае, если Mp имеет более конкретные типы параметров, чем Mq, то Mp лучше, чем Mq.Otherwise, if Mp has more specific parameter types than Mq, then Mp is better than Mq. Let {R1, R2, ..., Rn} и {S1, S2, ..., Sn} представляют нерасширяемые и нераскрытые типы параметров Mp и Mq.Let {R1, R2, ..., Rn} and {S1, S2, ..., Sn} represent the uninstantiated and unexpanded parameter types of Mp and Mq. типы параметров Mp более специфичны, чем Mq, если для каждого параметра Rx не меньше, чем Sx, а по крайней мере для одного параметра Rx является более конкретным, чем Sx:Mp's parameter types are more specific than Mq's if, for each parameter, Rx is not less specific than Sx, and, for at least one parameter, Rx is more specific than Sx:
    • Параметр типа является менее конкретным, чем параметр, не являющийся типом.A type parameter is less specific than a non-type parameter.
    • Рекурсивно сконструированный тип является более конкретным, чем другой сконструированный тип (с тем же количеством аргументов-типов), если по крайней мере один аргумент типа более специфичен, а аргумент типа не является менее конкретным, чем соответствующий аргумент типа в другом.Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
    • Тип массива более специфичен, чем другой тип массива (с тем же количеством измерений), если тип элемента первого является более конкретным, чем тип элемента второго.An array type is more specific than another array type (with the same number of dimensions) if the element type of the first is more specific than the element type of the second.
  • В противном случае, если один из элементов является оператором без приподнятия, а другой — оператором с нулификацией, то один из них лучше.Otherwise if one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better.
  • В противном случае ни одна из функций не будет лучше.Otherwise, neither function member is better.

Лучшее преобразование из выраженияBetter conversion from expression

При неявном преобразовании C1, преобразующем выражение E в тип T1, и неявное преобразование C2, преобразующее выражение E в тип T2, C1 является лучшим преобразованием , чем C2, если @no__ t-9 не полностью соответствует 0 и по крайней мере одно из следующих:Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if E does not exactly match T2 and at least one of the following holds:

Выражение, совпадающее с точнымExactly matching Expression

При наличии выражения E и типа T E точно соответствует T, если один из следующих содержит:Given an expression E and a type T, E exactly matches T if one of the following holds:

  • E имеет тип S, а преобразование удостоверения существует из S в TE has a type S, and an identity conversion exists from S to T
  • E является анонимной функцией, T является либо типом делегата D, либо типом дерева выражения Expression<D> и одним из следующих:E is an anonymous function, T is either a delegate type D or an expression tree type Expression<D> and one of the following holds:
    • Выводимый тип возвращаемого значения X существует для E в контексте списка параметров D (выводимый тип возвращаемогозначения), а преобразование удостоверения существует из X в тип возвращаемого значения DAn inferred return type X exists for E in the context of the parameter list of D (Inferred return type), and an identity conversion exists from X to the return type of D
    • @No__t-0 не является асинхронным, а D имеет тип возвращаемого значения Y или E является асинхронным, а D имеет тип возвращаемого значения Task<Y>, и одно из следующих данных содержит:Either E is non-async and D has a return type Y or E is async and D has a return type Task<Y>, and one of the following holds:
      • Тело E является выражением, точно соответствующим YThe body of E is an expression that exactly matches Y
      • Тело E является блоком операторов, в котором каждая инструкция return возвращает выражение, которое точно соответствует YThe body of E is a statement block where every return statement returns an expression that exactly matches Y

Улучшенный целевой объект преобразованияBetter conversion target

Учитывая два разных типа T1 и T2, T1 является более эффективным объектом преобразования, чем T2, если не существует неявного преобразования из T2 в T1, и по крайней мере одно из следующих объектов содержит:Given two different types T1 and T2, T1 is a better conversion target than T2 if no implicit conversion from T2 to T1 exists, and at least one of the following holds:

  • Неявное преобразование из T1 в T2 существуетAn implicit conversion from T1 to T2 exists
  • T1 является либо типом делегата D1, либо типом дерева выражения Expression<D1>, T2 является либо типом делегата D2, либо типом дерева выражения Expression<D2>, D1 имеет тип возвращаемого значения S1, а один из следующих содержит :T1 is either a delegate type D1 or an expression tree type Expression<D1>, T2 is either a delegate type D2 or an expression tree type Expression<D2>, D1 has a return type S1 and one of the following holds:
    • D2 возвращает значение voidD2 is void returning
    • D2 имеет тип возвращаемого значения S2, а S1 — более подходящий целевой объект преобразования, чем S2.D2 has a return type S2, and S1 is a better conversion target than S2
  • T1Task<S1>, T2Task<S2>, а S1 — более подходящий целевой объект преобразования, чем S2.T1 is Task<S1>, T2 is Task<S2>, and S1 is a better conversion target than S2
  • T1S1 или S1?, где S1 — это целочисленный тип со знаком, а T2S2 или S2?, где S2 — это целочисленный тип без знака.T1 is S1 or S1? where S1 is a signed integral type, and T2 is S2 or S2? where S2 is an unsigned integral type. В частности:Specifically:
    • S1 sbyte, а S2byte, ushort, uint или ulong.S1 is sbyte and S2 is byte, ushort, uint, or ulong
    • S1 short, а S2ushort, uint или ulong.S1 is short and S2 is ushort, uint, or ulong
    • S1 int, а S2uint или ulong.S1 is int and S2 is uint, or ulong
    • S1 long, а S2ulong.S1 is long and S2 is ulong

Перегрузка в универсальных классахOverloading in generic classes

Хотя сигнатуры, как объявленные, должны быть уникальными, возможно, замена аргументов типа приводит к идентичным сигнатурам.While signatures as declared must be unique, it is possible that substitution of type arguments results in identical signatures. В таких случаях правила привязки к перегрузке, описанные выше, будут выбирать наиболее конкретный элемент.In such cases, the tie-breaking rules of overload resolution above will pick the most specific member.

В следующих примерах показаны перегрузки, которые являются допустимыми и недопустимыми в соответствии с этим правилом.The following examples show overloads that are valid and invalid according to this rule:

interface I1<T> {...}

interface I2<T> {...}

class G1<U>
{
    int F1(U u);                  // Overload resolution for G<int>.F1
    int F1(int i);                // will pick non-generic

    void F2(I1<U> a);             // Valid overload
    void F2(I2<U> a);
}

class G2<U,V>
{
    void F3(U u, V v);            // Valid, but overload resolution for
    void F3(V v, U u);            // G2<int,int>.F3 will fail

    void F4(U u, I1<V> v);        // Valid, but overload resolution for    
    void F4(I1<V> v, U u);        // G2<I1<int>,int>.F4 will fail

    void F5(U u1, I1<V> v2);      // Valid overload
    void F5(V v1, U u2);

    void F6(ref U u);             // valid overload
    void F6(out V v);
}

Проверка разрешения динамической перегрузки во время компиляцииCompile-time checking of dynamic overload resolution

Для большинства динамически связанных операций набор возможных кандидатов для разрешения неизвестен во время компиляции.For most dynamically bound operations the set of possible candidates for resolution is unknown at compile-time. Однако в некоторых случаях набор кандидатов известен во время компиляции:In certain cases, however the candidate set is known at compile-time:

  • Вызовы статических методов с динамическими аргументамиStatic method calls with dynamic arguments
  • Вызовы метода экземпляра, где получатель не является динамическим выражениемInstance method calls where the receiver is not a dynamic expression
  • Индексатор вызывает, где получатель не является динамическим выражениемIndexer calls where the receiver is not a dynamic expression
  • Вызовы конструктора с динамическими аргументамиConstructor calls with dynamic arguments

В таких случаях проверка ограниченного времени компиляции выполняется для каждого кандидата, чтобы определить, могут ли какие-либо из них применяться во время выполнения. Эта проверка состоит из следующих шагов.In these cases a limited compile-time check is performed for each candidate to see if any of them could possibly apply at run-time.This check consists of the following steps:

  • Определение разделяемого типа: Любой аргумент типа, который не зависит непосредственно или косвенно от аргумента типа dynamic, выводится с использованием правил вывода типа.Partial type inference: Any type argument that does not depend directly or indirectly on an argument of type dynamic is inferred using the rules of Type inference. Остальные аргументы типа неизвестны.The remaining type arguments are unknown.
  • Частичная проверка применимости: Применимость проверяется в соответствии с применимым членом функции, но игнорируются параметры, типы которых неизвестны.Partial applicability check: Applicability is checked according to Applicable function member, but ignoring parameters whose types are unknown.
  • Если ни один из кандидатов не прошел этот тест, возникает ошибка времени компиляции.If no candidate passes this test, a compile-time error occurs.

Вызов функции членаFunction member invocation

В этом разделе описывается процесс, который выполняется во время выполнения для вызова определенного члена функции.This section describes the process that takes place at run-time to invoke a particular function member. Предполагается, что процесс времени привязки уже определил конкретный член для вызова, возможно, путем применения разрешения перегрузки к набору членов-функций-кандидатов.It is assumed that a binding-time process has already determined the particular member to invoke, possibly by applying overload resolution to a set of candidate function members.

В целях описания процесса вызова функции делятся на две категории:For purposes of describing the invocation process, function members are divided into two categories:

  • Члены статических функций.Static function members. Это конструкторы экземпляров, статические методы, свойства статических методов доступа к свойству и определяемые пользователем операторы.These are instance constructors, static methods, static property accessors, and user-defined operators. Статические функции-члены всегда являются невиртуальными.Static function members are always non-virtual.
  • Члены функции экземпляра.Instance function members. Это методы экземпляра, метод доступа к свойству экземпляра и методы доступа индексатора.These are instance methods, instance property accessors, and indexer accessors. Члены функции экземпляра либо не являются виртуальными, либо виртуальными, и всегда вызываются для конкретного экземпляра.Instance function members are either non-virtual or virtual, and are always invoked on a particular instance. Экземпляр выдается выражением экземпляра и становится доступным в члене функции как this (этот доступ).The instance is computed by an instance expression, and it becomes accessible within the function member as this (This access).

Обработка вызова члена функции во время выполнения состоит из следующих шагов, где M является членом функции и, если M является членом экземпляра, то E является выражением экземпляра:The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:

  • Если M является статическим членом функции:If M is a static function member:

  • Если M является членом функции экземпляра, объявленным в value_type:If M is an instance function member declared in a value_type:

    • E вычисляется.E is evaluated. Если эта оценка вызывает исключение, дальнейшие действия не выполняются.If this evaluation causes an exception, then no further steps are executed.
    • Если E не классифицируется как переменная, создается временная локальная переменная типа E, а для этой переменной назначается значение E.If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. затем E классифицируется как ссылка на эту временную локальную переменную.E is then reclassified as a reference to that temporary local variable. Временная переменная доступна как this в M, но не наоборот.The temporary variable is accessible as this within M, but not in any other way. Таким образом, только если E является истинной переменной, вызывающий может наблюдать изменения, вносимые M в this.Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.
    • Список аргументов вычисляется, как описано в списках аргументов.The argument list is evaluated as described in Argument lists.
    • вызывается M.M is invoked. Переменная, на которую ссылается E, преобразуется в переменную, на которую ссылается this.The variable referenced by E becomes the variable referenced by this.
  • Если M является членом функции экземпляра, объявленным в reference_type:If M is an instance function member declared in a reference_type:

    • E вычисляется.E is evaluated. Если эта оценка вызывает исключение, дальнейшие действия не выполняются.If this evaluation causes an exception, then no further steps are executed.
    • Список аргументов вычисляется, как описано в списках аргументов.The argument list is evaluated as described in Argument lists.
    • Если тип E является value_type, для преобразования E в тип object выполняется преобразование упаковки (преобразованиеупаковки), а E считается типом object в следующих шагах.If the type of E is a value_type, a boxing conversion (Boxing conversions) is performed to convert E to type object, and E is considered to be of type object in the following steps. В этом случае M может быть только членом System.Object.In this case, M could only be a member of System.Object.
    • Значение E проверяется как допустимое.The value of E is checked to be valid. Если значение E равно null, выдается System.NullReferenceException и дальнейшие действия не выполняются.If the value of E is null, a System.NullReferenceException is thrown and no further steps are executed.
    • Определяется реализация члена функции для вызова:The function member implementation to invoke is determined:
      • Если тип времени привязки E является интерфейсом, вызываемая функция-член является реализацией M, предоставляемой типом времени выполнения экземпляра, на который ссылается E.If the binding-time type of E is an interface, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. Этот член функции определяется путем применения правил сопоставления интерфейса (сопоставление интерфейса) для определения реализации M, предоставляемой типом времени выполнения экземпляра, на который ссылается E.This function member is determined by applying the interface mapping rules (Interface mapping) to determine the implementation of M provided by the run-time type of the instance referenced by E.
      • В противном случае, если M является членом виртуальной функции, вызываемая функция является реализацией M, предоставляемой типом времени выполнения экземпляра, на который ссылается E.Otherwise, if M is a virtual function member, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. Этот член функции определяется путем применения правил для определения наиболее производной реализации (виртуальных методов) M относительно типа времени выполнения экземпляра, на который ссылается E.This function member is determined by applying the rules for determining the most derived implementation (Virtual methods) of M with respect to the run-time type of the instance referenced by E.
      • В противном случае M является невиртуальным членом функции, а вызываемая функция — M.Otherwise, M is a non-virtual function member, and the function member to invoke is M itself.
    • Вызывается реализация члена функции, определенная на предыдущем шаге.The function member implementation determined in the step above is invoked. Объект, на который ссылается E, превращается в объект, на который ссылается this.The object referenced by E becomes the object referenced by this.

Вызовы в упакованных экземплярахInvocations on boxed instances

Член функции, реализованный в value_type , может вызываться через упакованный экземпляр, value_type в следующих ситуациях:A function member implemented in a value_type can be invoked through a boxed instance of that value_type in the following situations:

  • Если член функции является override метода, унаследованного от типа object, и вызывается с помощью выражения экземпляра типа object.When the function member is an override of a method inherited from type object and is invoked through an instance expression of type object.
  • Если функция-член является реализацией члена функции интерфейса и вызывается через выражение экземпляра interface_type.When the function member is an implementation of an interface function member and is invoked through an instance expression of an interface_type.
  • Когда функция-член вызывается через делегат.When the function member is invoked through a delegate.

В таких ситуациях упакованный экземпляр считается, что он содержит переменную value_type, а эта переменная становится переменной, на которую ссылается this в вызове члена функции.In these situations, the boxed instance is considered to contain a variable of the value_type, and this variable becomes the variable referenced by this within the function member invocation. В частности, это означает, что при вызове функции в упакованном экземпляре функция-член может изменить значение, содержащееся в упакованном экземпляре.In particular, this means that when a function member is invoked on a boxed instance, it is possible for the function member to modify the value contained in the boxed instance.

Основные выраженияPrimary expressions

Первичные выражения включают в себя простейшие формы выражений.Primary expressions include the simplest forms of expressions.

primary_expression
    : primary_no_array_creation_expression
    | array_creation_expression
    ;

primary_no_array_creation_expression
    : literal
    | interpolated_string_expression
    | simple_name
    | parenthesized_expression
    | member_access
    | invocation_expression
    | element_access
    | this_access
    | base_access
    | post_increment_expression
    | post_decrement_expression
    | object_creation_expression
    | delegate_creation_expression
    | anonymous_object_creation_expression
    | typeof_expression
    | checked_expression
    | unchecked_expression
    | default_value_expression
    | nameof_expression
    | anonymous_method_expression
    | primary_no_array_creation_expression_unsafe
    ;

Первичные выражения делятся между array_creation_expressionи primary_no_array_creation_expressions.Primary expressions are divided between array_creation_expressions and primary_no_array_creation_expressions. Использование выражения создания массива таким образом, вместо того чтобы перечислять его вместе с другими простыми формами выражений, позволяет грамматике запретить потенциально непонятный код, такой какTreating array-creation-expression in this way, rather than listing it along with the other simple expression forms, enables the grammar to disallow potentially confusing code such as

object o = new int[3][1];

который в противном случае будет интерпретироваться какwhich would otherwise be interpreted as

object o = (new int[3])[1];

ЛитералыLiterals

Primary_expression , состоящий из литерала (литералов), классифицируется как значение.A primary_expression that consists of a literal (Literals) is classified as a value.

Интерполированные строкиInterpolated strings

Interpolated_string_expression состоит из знака $, за которым следует обычный или Буквальный строковый литерал, где отверстия, разделенные { и }, заключаются в выражения и спецификации форматирования.An interpolated_string_expression consists of a $ sign followed by a regular or verbatim string literal, wherein holes, delimited by { and }, enclose expressions and formatting specifications. Интерполяция строкового выражения — это результат interpolated_string_literal , который был разбит на отдельные маркеры, как описано в статье интерполяция строковых литералов.An interpolated string expression is the result of an interpolated_string_literal that has been broken up into individual tokens, as described in Interpolated string literals.

interpolated_string_expression
    : '$' interpolated_regular_string
    | '$' interpolated_verbatim_string
    ;

interpolated_regular_string
    : interpolated_regular_string_whole
    | interpolated_regular_string_start interpolated_regular_string_body interpolated_regular_string_end
    ;

interpolated_regular_string_body
    : interpolation (interpolated_regular_string_mid interpolation)*
    ;

interpolation
    : expression
    | expression ',' constant_expression
    ;

interpolated_verbatim_string
    : interpolated_verbatim_string_whole
    | interpolated_verbatim_string_start interpolated_verbatim_string_body interpolated_verbatim_string_end
    ;

interpolated_verbatim_string_body
    : interpolation (interpolated_verbatim_string_mid interpolation)+
    ;

Constant_expression в интерполяции должны иметь неявное преобразование в int.The constant_expression in an interpolation must have an implicit conversion to int.

Interpolated_string_expression классифицируется как значение.An interpolated_string_expression is classified as a value. Если он сразу же преобразуется в System.IFormattable или System.FormattableString с неявным преобразованием строк с интерполяцией (неявные преобразования строк с интерполяцией), то строковое выражение с интерполяцией имеет этот тип.If it is immediately converted to System.IFormattable or System.FormattableString with an implicit interpolated string conversion (Implicit interpolated string conversions), the interpolated string expression has that type. В противном случае он имеет тип string.Otherwise, it has the type string.

Если тип интерполяции строки — System.IFormattable или System.FormattableString, то это означает вызов System.Runtime.CompilerServices.FormattableStringFactory.Create.If the type of an interpolated string is System.IFormattable or System.FormattableString, the meaning is a call to System.Runtime.CompilerServices.FormattableStringFactory.Create. Если тип имеет значение string, то значение выражения является вызовом string.Format.If the type is string, the meaning of the expression is a call to string.Format. В обоих случаях список аргументов вызова состоит из строкового литерала формата с заполнителями для каждой интерполяции и аргумента для каждого выражения, соответствующего заполнители.In both cases, the argument list of the call consists of a format string literal with placeholders for each interpolation, and an argument for each expression corresponding to the place holders.

Литерал строки формата формируется следующим образом, где N — число интерполяций в interpolated_string_expression:The format string literal is constructed as follows, where N is the number of interpolations in the interpolated_string_expression:

  • Если interpolated_regular_string_whole или interpolated_verbatim_string_whole следует за знаком $, то строковый литерал формата — это токен.If an interpolated_regular_string_whole or an interpolated_verbatim_string_whole follows the $ sign, then the format string literal is that token.
  • В противном случае строковый литерал формата состоит из следующих компонентов:Otherwise, the format string literal consists of:
    • Сначала interpolated_regular_string_start или interpolated_verbatim_string_startFirst the interpolated_regular_string_start or interpolated_verbatim_string_start
    • Затем для каждого числа I от 0 до N-1:Then for each number I from 0 to N-1:
      • Десятичное представление IThe decimal representation of I
      • Затем, если соответствующая интерполяция имеет constant_expression, то , (запятая), за которой следует десятичное представление значения constant_expressionThen, if the corresponding interpolation has a constant_expression, a , (comma) followed by the decimal representation of the value of the constant_expression
      • Затем interpolated_regular_string_mid, interpolated_regular_string_end, interpolated_verbatim_string_mid или interpolated_verbatim_string_end сразу после соответствующей интерполяции.Then the interpolated_regular_string_mid, interpolated_regular_string_end, interpolated_verbatim_string_mid or interpolated_verbatim_string_end immediately following the corresponding interpolation.

Последующие аргументы — это просто выражения из интерполяций (если они есть), по порядку.The subsequent arguments are simply the expressions from the interpolations (if any), in order.

TODO: примеры.TODO: examples.

Простые именаSimple names

Simple_name состоит из идентификатора, при необходимости за которым следует список аргументов типа:A simple_name consists of an identifier, optionally followed by a type argument list:

simple_name
    : identifier type_argument_list?
    ;

Simple_name — это либо форма I, либо форма I<A1,...,Ak>, где I — это один идентификатор, а <A1,...,Ak> — необязательный type_argument_list.A simple_name is either of the form I or of the form I<A1,...,Ak>, where I is a single identifier and <A1,...,Ak> is an optional type_argument_list. Если type_argument_list не указан, рассмотрите возможность K равным нулю.When no type_argument_list is specified, consider K to be zero. Simple_name вычисляется и классифицируется следующим образом:The simple_name is evaluated and classified as follows:

  • Если K равно нулю и simple_name встречается в блоке , аобласть объявления (или охватывающего блока) содержитлокальную переменную, параметр или константу с Name @ no__t-6, то simple_name ссылается на эту локальную переменную, параметр или константу и классифицируется как переменная или значение.If K is zero and the simple_name appears within a block and if the block's (or an enclosing block's) local variable declaration space (Declarations) contains a local variable, parameter or constant with name I, then the simple_name refers to that local variable, parameter or constant and is classified as a variable or value.

  • Если K равно нулю и simple_name отображается в теле объявления универсального метода, и если это объявление содержит параметр типа с именем @ no__t-2, то simple_name ссылается на этот параметр типа.If K is zero and the simple_name appears within the body of a generic method declaration and if that declaration includes a type parameter with name I, then the simple_name refers to that type parameter.

  • В противном случае для каждого экземпляра типа @ no__t-0 (тип экземпляра), начиная с типа экземпляра непосредственно включающего объявления типа и продолжая с типом экземпляра каждого включающего класса или объявления структуры (если таковые имеются):Otherwise, for each instance type T (The instance type), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):

    • Если K равно нулю, а объявление T включает параметр типа с именем @ no__t-2, то simple_name ссылается на этот параметр типа.If K is zero and the declaration of T includes a type parameter with name I, then the simple_name refers to that type parameter.
    • В противном случае, если поиск члена (Уточняющий запрос члена) I в T с аргументами K @ no__t-4type, приведет к совпадению:Otherwise, if a member lookup (Member lookup) of I in T with K type arguments produces a match:
      • Если T — тип экземпляра непосредственно вмещающего класса или типа структуры, а Уточняющий запрос определяет один или несколько методов, результатом является группа методов со связанным выражением экземпляра this.If T is the instance type of the immediately enclosing class or struct type and the lookup identifies one or more methods, the result is a method group with an associated instance expression of this. Если указан список аргументов типа, он используется при вызове универсального метода (вызовов метода).If a type argument list was specified, it is used in calling a generic method (Method invocations).
      • В противном случае, если T является типом экземпляра непосредственно вмещающего класса или типа структуры, если Уточняющий запрос определяет член экземпляра и если ссылка возникает в теле конструктора экземпляра, метода экземпляра или метода доступа к экземпляру, результат тот же, что и доступ к члену (доступ к члену) формы this.I.Otherwise, if T is the instance type of the immediately enclosing class or struct type, if the lookup identifies an instance member, and if the reference occurs within the body of an instance constructor, an instance method, or an instance accessor, the result is the same as a member access (Member access) of the form this.I. Это может произойти, только если значение K равно нулю.This can only happen when K is zero.
      • В противном случае результат будет таким же, как доступ к члену (доступ к члену) в форме T.I или T.I<A1,...,Ak>.Otherwise, the result is the same as a member access (Member access) of the form T.I or T.I<A1,...,Ak>. В этом случае это ошибка времени привязки, когда simple_name ссылается на член экземпляра.In this case, it is a binding-time error for the simple_name to refer to an instance member.
  • В противном случае для каждого пространства имен @ no__t-0, начиная с пространства имен, в котором происходит simple_name , продолжая работу с каждым включающим пространством имен (если таковое имеется) и заканчивая глобальным пространством имен, следующие шаги оцениваются до тех пор, пока не будет найдена сущность:Otherwise, for each namespace N, starting with the namespace in which the simple_name occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:

    • Если K равно нулю, а I — имя пространства имен в @ no__t-2, то:If K is zero and I is the name of a namespace in N, then:
      • Если расположение, в котором происходит simple_name , заключено в объявление пространства имен для N, а объявление пространства имен содержит extern_alias_directive или using_alias_directive , связывающие имя @ no__t-4 с пространство имен или тип, simple_name является неоднозначным и возникает ошибка времени компиляции.If the location where the simple_name occurs is enclosed by a namespace declaration for N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.
      • В противном случае simple_name ссылается на пространство имен с именем I в N.Otherwise, the simple_name refers to the namespace named I in N.
    • В противном случае, если N содержит доступный тип с параметрами name @ no__t-1 и K @ no__t-3type, то:Otherwise, if N contains an accessible type having name I and K type parameters, then:
      • Если K равно нулю и расположение, где происходит simple_name , заключается в объявлении пространства имен для N, а объявление пространства имен содержит extern_alias_directive или using_alias_directive , связывающие Name @ no__t-5 с пространством имен или типом, simple_name является неоднозначным и возникает ошибка времени компиляции.If K is zero and the location where the simple_name occurs is enclosed by a namespace declaration for N and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with a namespace or type, then the simple_name is ambiguous and a compile-time error occurs.
      • В противном случае namespace_or_type_name ссылается на тип, сформированный с заданными аргументами типа.Otherwise, the namespace_or_type_name refers to the type constructed with the given type arguments.
    • В противном случае, если место, где происходит simple_name , заключено в объявление пространства имен для @ no__t-1:Otherwise, if the location where the simple_name occurs is enclosed by a namespace declaration for N:
      • Если K равно нулю, а объявление пространства имен содержит extern_alias_directive или using_alias_directive , связывающие имя @ no__t-3 с импортированным пространством имен или типом, то simple_name ссылается на это пространство имен или Тип.If K is zero and the namespace declaration contains an extern_alias_directive or using_alias_directive that associates the name I with an imported namespace or type, then the simple_name refers to that namespace or type.
      • В противном случае, если пространства имен и объявления типов, импортированные с помощью using_namespace_directives и using_static_directives в объявление пространства имен, содержат только один доступный тип или статический член без расширения с именем @ no__ Параметры t-2 и K @ no__t-4type, то simple_name ссылается на этот тип или член, созданный с заданными аргументами типа.Otherwise, if the namespaces and type declarations imported by the using_namespace_directives and using_static_directives of the namespace declaration contain exactly one accessible type or non-extension static member having name I and K type parameters, then the simple_name refers to that type or member constructed with the given type arguments.
      • В противном случае, если пространства имен и типы, импортированные с помощью using_namespace_directives в объявление пространства имен, содержат более одного доступного типа или статического члена без метода расширения с именем @ no__t-1 и K @ no__t-3type Parameters, затем simple_name является неоднозначным и возникает ошибка.Otherwise, if the namespaces and types imported by the using_namespace_directives of the namespace declaration contain more than one accessible type or non-extension-method static member having name I and K type parameters, then the simple_name is ambiguous and an error occurs.

    Обратите внимание, что весь шаг полностью параллельен соответствующему шагу в обработке namespace_or_type_name (имена пространств имен и типов).Note that this entire step is exactly parallel to the corresponding step in the processing of a namespace_or_type_name (Namespace and type names).

  • В противном случае simple_name не определен и возникает ошибка времени компиляции.Otherwise, the simple_name is undefined and a compile-time error occurs.

Выражения в скобкахParenthesized expressions

Parenthesized_expression состоит из выражения , заключенного в круглые скобки.A parenthesized_expression consists of an expression enclosed in parentheses.

parenthesized_expression
    : '(' expression ')'
    ;

Parenthesized_expression вычисляется путем вычисления выражения в круглых скобках.A parenthesized_expression is evaluated by evaluating the expression within the parentheses. Если выражение в круглых скобках обозначает пространство имен или тип, возникает ошибка времени компиляции.If the expression within the parentheses denotes a namespace or type, a compile-time error occurs. В противном случае результат parenthesized_expression является результатом вычисления содержащегося выражения.Otherwise, the result of the parenthesized_expression is the result of the evaluation of the contained expression.

Доступ к членамMember access

Member_access состоит из primary_expression, predefined_typeили qualified_alias_member, за которым следует токен ".", за которым следует идентификатор, при необходимости за которым следует type_argument_list .A member_access consists of a primary_expression, a predefined_type, or a qualified_alias_member, followed by a "." token, followed by an identifier, optionally followed by a type_argument_list.

member_access
    : primary_expression '.' identifier type_argument_list?
    | predefined_type '.' identifier type_argument_list?
    | qualified_alias_member '.' identifier
    ;

predefined_type
    : 'bool'   | 'byte'  | 'char'  | 'decimal' | 'double' | 'float' | 'int' | 'long'
    | 'object' | 'sbyte' | 'short' | 'string'  | 'uint'   | 'ulong' | 'ushort'
    ;

Qualified_alias_member производство определено в квалификаторах псевдонимов пространств имен.The qualified_alias_member production is defined in Namespace alias qualifiers.

Member_access является либо формой E.I, либо формой E.I<A1, ..., Ak>, где E — это первичное выражение, I — один идентификатор, а <A1, ..., Ak> — необязательный type_argument_list.A member_access is either of the form E.I or of the form E.I<A1, ..., Ak>, where E is a primary-expression, I is a single identifier and <A1, ..., Ak> is an optional type_argument_list. Если type_argument_list не указан, рассмотрите возможность K равным нулю.When no type_argument_list is specified, consider K to be zero.

Member_access с primary_expression типа dynamic динамически привязана (Динамическая привязка).A member_access with a primary_expression of type dynamic is dynamically bound (Dynamic binding). В этом случае компилятор классифицирует доступ к члену как доступ к свойству типа dynamic.In this case the compiler classifies the member access as a property access of type dynamic. Правила, приведенные ниже для определения значения member_access , затем применяются во время выполнения, используя тип времени выполнения вместо типа primary_expressionна этапе компиляции.The rules below to determine the meaning of the member_access are then applied at run-time, using the run-time type instead of the compile-time type of the primary_expression. Если эта классификация времени выполнения ведет к группе методов, то доступ к члену должен быть primary_expressionом invocation_expression.If this run-time classification leads to a method group, then the member access must be the primary_expression of an invocation_expression.

Member_access вычисляется и классифицируется следующим образом:The member_access is evaluated and classified as follows:

  • Если K равно нулю и E является пространством имен, а E содержит вложенное пространство имен с именем @ no__t-3, то результатом является это пространство имен.If K is zero and E is a namespace and E contains a nested namespace with name I, then the result is that namespace.
  • В противном случае, если E является пространством имен, а E содержит доступный тип с параметрами name @ no__t-2 и K @ no__t-4type, то результатом будет тип, созданный с заданными аргументами типа.Otherwise, if E is a namespace and E contains an accessible type having name I and K type parameters, then the result is that type constructed with the given type arguments.
  • Если E является predefined_type или primary_expression классифицируется как тип, если E не является параметром типа, и если поиск члена (уточняющий запрос элемента) I в E с параметрами K @ no__t-8type приведет к совпадению, затем E.I вычисляется и классифицируется следующим образом:If E is a predefined_type or a primary_expression classified as a type, if E is not a type parameter, and if a member lookup (Member lookup) of I in E with K type parameters produces a match, then E.I is evaluated and classified as follows:
    • Если I определяет тип, результатом будет тип, сформированный с заданными аргументами типа.If I identifies a type, then the result is that type constructed with the given type arguments.
    • Если I определяет один или несколько методов, то результатом будет группа методов без связанного выражения экземпляра.If I identifies one or more methods, then the result is a method group with no associated instance expression. Если указан список аргументов типа, он используется при вызове универсального метода (вызовов метода).If a type argument list was specified, it is used in calling a generic method (Method invocations).
    • Если I определяет свойство static, результатом будет доступ к свойству без связанного выражения экземпляра.If I identifies a static property, then the result is a property access with no associated instance expression.
    • Если I определяет поле static:If I identifies a static field:
      • Если поле имеет значение readonly, а ссылка находится за пределами статического конструктора класса или структуры, в которой объявляется поле, результат является значением, а именно значением статического поля @ no__t-1 в @ no__t-2.If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.
      • В противном случае результатом является переменная, а именно статическое поле @ no__t-0 в @ no__t-1.Otherwise, the result is a variable, namely the static field I in E.
    • Если I определяет событие static:If I identifies a static event:
      • Если ссылка возникает в классе или структуре, в которой объявлено событие, а событие было объявлено без event_accessor_declarations (Events), то E.I обрабатывается точно так же, как если бы I было статическим полем.If the reference occurs within the class or struct in which the event is declared, and the event was declared without event_accessor_declarations (Events), then E.I is processed exactly as if I were a static field.
      • В противном случае результатом является доступ к событию без соответствующего выражения экземпляра.Otherwise, the result is an event access with no associated instance expression.
    • Если I определяет константу, то результатом является значение, а именно значение этой константы.If I identifies a constant, then the result is a value, namely the value of that constant.
    • Если I определяет член перечисления, результатом является значение, а именно значение этого члена перечисления.If I identifies an enumeration member, then the result is a value, namely the value of that enumeration member.
    • В противном случае E.I является недопустимой ссылкой на элемент, и возникает ошибка времени компиляции.Otherwise, E.I is an invalid member reference, and a compile-time error occurs.
  • Если E — доступ к свойству, доступ к индексатору, переменная или значение, тип, равный @ no__t-1, и Уточняющий запрос члена (Уточняющий запрос члена) I в T с K @ no__t-6type, выдает соответствие, затем вычисляется E.I. классифицировать следующим образом:If E is a property access, indexer access, variable, or value, the type of which is T, and a member lookup (Member lookup) of I in T with K type arguments produces a match, then E.I is evaluated and classified as follows:
    • Во-первых, если E является доступом к свойству или индексатору, то получается значение доступа к свойству или индексатору (значения выражений), а E — как значение.First, if E is a property or indexer access, then the value of the property or indexer access is obtained (Values of expressions) and E is reclassified as a value.
    • Если I определяет один или несколько методов, то результатом будет группа методов со связанным выражением экземпляра E.If I identifies one or more methods, then the result is a method group with an associated instance expression of E. Если указан список аргументов типа, он используется при вызове универсального метода (вызовов метода).If a type argument list was specified, it is used in calling a generic method (Method invocations).
    • Если I определяет свойство экземпляра,If I identifies an instance property,
      • Если E равно this, I определяет автоматически реализуемое свойство (автоматически реализуемые свойства) без метода задания, а ссылка возникает в конструкторе экземпляра для типа класса или структуры T, а результат — это переменная, а именно скрытое резервное поле для автоматического свойства, заданное I в экземпляре T, заданное в this.If E is this, I identifies an automatically implemented property (Automatically implemented properties) without a setter, and the reference occurs within an instance constructor for a class or struct type T, then the result is a variable, namely the hidden backing field for the auto-property given by I in the instance of T given by this.
      • В противном случае результатом является доступ к свойству со связанным выражением экземпляра @ no__t-0.Otherwise, the result is a property access with an associated instance expression of E.
    • Если T является class_type , а I определяет поле экземпляра этого class_type:If T is a class_type and I identifies an instance field of that class_type:
      • Если значение E равно null, создается исключение System.NullReferenceException.If the value of E is null, then a System.NullReferenceException is thrown.
      • В противном случае, если поле имеет значение readonly, а ссылка находится за пределами конструктора экземпляра класса, в котором объявлено поле, результат является значением, а именно значением поля @ no__t-1 в объекте, на который ссылается @ no__t-2.Otherwise, if the field is readonly and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is a value, namely the value of the field I in the object referenced by E.
      • В противном случае результатом является переменная, а именно поле @ no__t-0 в объекте, на который ссылается @ no__t-1.Otherwise, the result is a variable, namely the field I in the object referenced by E.
    • Если T является struct_type , а I определяет поле экземпляра этого struct_type:If T is a struct_type and I identifies an instance field of that struct_type:
      • Если E является значением или поле имеет значение readonly, а ссылка находится вне конструктора экземпляра структуры, в которой объявлено поле, результатом является значение, а именно значение поля @ no__t-2 в экземпляре структуры, заданном параметром @ no__t-3.If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.
      • В противном случае результатом является переменная, а именно поле @ no__t-0 в экземпляре структуры, заданном параметром @ no__t-1.Otherwise, the result is a variable, namely the field I in the struct instance given by E.
    • Если I определяет событие экземпляра:If I identifies an instance event:
      • Если ссылка возникает в классе или структуре, в которой объявлено событие, а событие было объявлено без event_accessor_declarations (Events), а ссылка не выполняется в левой части оператора += или -=. , то E.I обрабатывается точно так же, как если бы I было полем экземпляра.If the reference occurs within the class or struct in which the event is declared, and the event was declared without event_accessor_declarations (Events), and the reference does not occur as the left-hand side of a += or -= operator, then E.I is processed exactly as if I was an instance field.
      • В противном случае результатом является доступ к событию со связанным выражением экземпляра @ no__t-0.Otherwise, the result is an event access with an associated instance expression of E.
  • В противном случае выполняется попытка обработать E.I как вызов метода расширения (вызовы метода расширения).Otherwise, an attempt is made to process E.I as an extension method invocation (Extension method invocations). Если это не удается, E.I является недопустимой ссылкой на элемент и возникает ошибка времени привязки.If this fails, E.I is an invalid member reference, and a binding-time error occurs.

Идентичные простые имена и имена типовIdentical simple names and type names

В доступе к члену формы E.I, если E является одним идентификатором, и если значение E в качестве simple_name (простые имена) — константа, поле, свойство, локальная переменная или параметр с тем же типом, что и значение E в качестве TYPE_NAME (пространство имен и имена типов), то разрешены оба значения E.In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple_name (Simple names) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type_name (Namespace and type names), then both possible meanings of E are permitted. Два возможных значения E.I никогда не являются неоднозначными, так как I обязательно должен быть членом типа E в обоих случаях.The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. Иными словами, правило просто разрешает доступ к статическим членам и вложенным типам E, где в противном случае возникает ошибка времени компиляции.In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred. Пример:For example:

struct Color
{
    public static readonly Color White = new Color(...);
    public static readonly Color Black = new Color(...);

    public Color Complement() {...}
}

class A
{
    public Color Color;                // Field Color of type Color

    void F() {
        Color = Color.Black;           // References Color.Black static member
        Color = Color.Complement();    // Invokes Complement() on Color field
    }

    static void G() {
        Color c = Color.White;         // References Color.White static member
    }
}

Грамматические неоднозначностиGrammar ambiguities

Производства для simple_name (простые имена) и member_access (доступ к членам) могут привести к неоднозначности в грамматике для выражений.The productions for simple_name (Simple names) and member_access (Member access) can give rise to ambiguities in the grammar for expressions. Например, инструкция:For example, the statement:

F(G<A,B>(7));

может интерпретироваться как вызов F с двумя аргументами: G < A и B > (7).could be interpreted as a call to F with two arguments, G < A and B > (7). Кроме того, его можно интерпретировать как вызов F с одним аргументом, который является вызовом универсального метода @ no__t-1 с двумя аргументами типа и одним обычным аргументом.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.

Если последовательность токенов может быть проанализирована (в контексте) как simple_name (простые имена), member_access (доступ к члену) или pointer_member_access (доступ к члену указателя), завершающейся type_argument_ List (аргументы типа) проверяется токен, следующий сразу за закрывающим маркером >.If a sequence of tokens can be parsed (in context) as a simple_name (Simple names), member_access (Member access), or pointer_member_access (Pointer member access) ending with a type_argument_list (Type arguments), the token immediately following the closing > token is examined. Если это один изIf it is one of

(  )  ]  }  :  ;  ,  .  ?  ==  !=  |  ^

затем type_argument_list сохраняется как часть simple_name, member_access или pointer_member_access и любой другой возможный синтаксический анализ последовательности токенов отбрасывается.then the type_argument_list is retained as part of the simple_name, member_access or pointer_member_access and any other possible parse of the sequence of tokens is discarded. В противном случае type_argument_list не считается частью simple_name, member_access или pointer_member_access, даже если отсутствует другой возможный синтаксический анализ последовательности токенов.Otherwise, the type_argument_list is not considered to be part of the simple_name, member_access or pointer_member_access, even if there is no other possible parse of the sequence of tokens. Обратите внимание, что эти правила не применяются при анализе type_argument_list в namespace_or_type_name (имена пространств имен и типов).Note that these rules are not applied when parsing a type_argument_list in a namespace_or_type_name (Namespace and type names). ОператорThe statement

F(G<A,B>(7));

в соответствии с этим правилом будет интерпретироваться как вызов F с одним аргументом, который является вызовом универсального метода G с двумя аргументами типа и одним обычным аргументом.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

F(G < A, B > 7);
F(G < A, B >> 7);

Каждый из них будет интерпретирован как вызов F с двумя аргументами.will each be interpreted as a call to F with two arguments. ОператорThe statement

x = F < A > +y;

будет интерпретирован как оператор "меньше чем", оператор "больше" и "унарный плюс", как если бы инструкция была записана x = (F < A) > (+y), а не как simple_name с type_argument_list , за которым следует оператор бинарного плюса.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

x = y is C<T> + z;

маркеры C<T> обрабатываются как namespace_or_type_name с type_argument_list.the tokens C<T> are interpreted as a namespace_or_type_name with a type_argument_list.

Выражения вызоваInvocation expressions

Для вызова метода используется invocation_expression .An invocation_expression is used to invoke a method.

invocation_expression
    : primary_expression '(' argument_list? ')'
    ;

Invocation_expression динамически привязана (Динамическая привязка), если хотя бы одна из следующих служб содержит:An invocation_expression is dynamically bound (Dynamic binding) if at least one of the following holds:

  • Primary_expression имеет тип времени компиляции dynamic.The primary_expression has compile-time type dynamic.
  • По крайней мере один аргумент необязательного argument_list имеет тип времени компиляции dynamic, а primary_expression не имеет типа делегата.At least one argument of the optional argument_list has compile-time type dynamic and the primary_expression does not have a delegate type.

В этом случае компилятор классифицирует invocation_expression как значение типа dynamic.In this case the compiler classifies the invocation_expression as a value of type dynamic. Приведенные ниже правила определяют значение invocation_expression во время выполнения, используя тип времени выполнения, а не тип времени компиляции для primary_expression и аргументов, которые имеют тип времени компиляции @no_ _T-2.The rules below to determine the meaning of the invocation_expression are then applied at run-time, using the run-time type instead of the compile-time type of those of the primary_expression and arguments which have the compile-time type dynamic. Если primary_expression не имеет типа времени компиляции dynamic, то вызов метода будет иметь ограниченную проверку времени компиляции, как описано в статье Проверка разрешения динамической перегрузки во время компиляции.If the primary_expression does not have compile-time type dynamic, then the method invocation undergoes a limited compile time check as described in Compile-time checking of dynamic overload resolution.

Primary_expression invocation_expression должен быть группой методов или значением delegate_type.The primary_expression of an invocation_expression must be a method group or a value of a delegate_type. Если primary_expression является группой методов, invocation_expression является вызовом метода (вызовов метода).If the primary_expression is a method group, the invocation_expression is a method invocation (Method invocations). Если primary_expression является значением delegate_type, invocation_expression является вызовом делегата (вызовы делегата).If the primary_expression is a value of a delegate_type, the invocation_expression is a delegate invocation (Delegate invocations). Если primary_expression не является ни группой методов, ни значением delegate_type, возникает ошибка времени привязки.If the primary_expression is neither a method group nor a value of a delegate_type, a binding-time error occurs.

Необязательные argument_list (списки аргументов) предоставляют значения или ссылки на переменные для параметров метода.The optional argument_list (Argument lists) provides values or variable references for the parameters of the method.

Результат вычисления invocation_expression классифицируется следующим образом:The result of evaluating an invocation_expression is classified as follows:

  • Если invocation_expression вызывает метод или делегат, который возвращает void, результат будет Nothing.If the invocation_expression invokes a method or delegate that returns void, the result is nothing. Выражение, классифицированное как Nothing, разрешено только в контексте statement_expression (Операторы выражения) или в качестве тела lambda_expression (выражения анонимных функций).An expression that is classified as nothing is permitted only in the context of a statement_expression (Expression statements) or as the body of a lambda_expression (Anonymous function expressions). В противном случае возникает ошибка времени привязки.Otherwise a binding-time error occurs.
  • В противном случае результатом является значение типа, возвращаемого методом или делегатом.Otherwise, the result is a value of the type returned by the method or delegate.

Вызовы методовMethod invocations

Для вызова метода primary_expression invocation_expression должен быть группой методов.For a method invocation, the primary_expression of the invocation_expression must be a method group. Группа методов определяет один вызываемый метод или набор перегруженных методов, из которых выбирается конкретный метод для вызова.The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. В последнем случае определение конкретного вызываемого метода основывается на контексте, предоставленном типами аргументов в argument_list.In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the argument_list.

Обработка вызова метода в форме во время привязки M(A), где M — это группа методов (возможно, включая type_argument_list), а A — необязательный argument_list, состоит из следующих шагов.The binding-time processing of a method invocation of the form M(A), where M is a method group (possibly including a type_argument_list), and A is an optional argument_list, consists of the following steps:

  • Создается набор методов-кандидатов для вызова метода.The set of candidate methods for the method invocation is constructed. Для каждого метода F, связанного с группой методов M:For each method F associated with the method group M:
    • Если F не является универсальным, F является кандидатом в следующих случаях:If F is non-generic, F is a candidate when:
    • Если F является универсальным и M не имеет списка аргументов типа, F является кандидатом в следующих случаях:If F is generic and M has no type argument list, F is a candidate when:
      • Вывод типа (вывод типа) выполнен, выводит список аргументов типа для вызова иType inference (Type inference) succeeds, inferring a list of type arguments for the call, and
      • После замены аргументов выводимого типа для соответствующих параметров типа метода все сконструированные типы в списке параметров F соответствуют их ограничениям (удовлетворяющим ограничениям), а список параметров F применим к по отношению к A (применимый член функции).Once the inferred type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (Satisfying constraints), and the parameter list of F is applicable with respect to A (Applicable function member).
    • Если F является универсальным и M включает список аргументов типа, F является кандидатом в следующих случаях:If F is generic and M includes a type argument list, F is a candidate when:
      • F имеет то же количество параметров типа метода, что и было указано в списке аргументов типа.F has the same number of method type parameters as were supplied in the type argument list, and
      • После замены аргументов типа для соответствующих параметров типа метода все сконструированные типы в списке параметров F соответствуют их ограничениям (удовлетворяющим ограничениям), а список параметров F применим по отношению к A (применимый член функции).Once the type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (Satisfying constraints), and the parameter list of F is applicable with respect to A (Applicable function member).
  • Набор методов-кандидатов сокращается, чтобы содержать только методы из самых производных типов: Для каждого метода C.F в наборе, где C — это тип, в котором объявлен метод F, все методы, объявленные в базовом типе C, удаляются из набора.The set of candidate methods is reduced to contain only methods from the most derived types: For each method C.F in the set, where C is the type in which the method F is declared, all methods declared in a base type of C are removed from the set. Более того, если C является типом класса, отличным от object, все методы, объявленные в типе интерфейса, удаляются из набора.Furthermore, if C is a class type other than object, all methods declared in an interface type are removed from the set. (Это последнее правило влияет только на то, что группа методов была результатом уточняющего запроса для параметра типа, имеющего эффективный базовый класс, отличный от Object, и непустой эффективный набор интерфейсов.)(This latter rule only has affect when the method group was the result of a member lookup on a type parameter having an effective base class other than object and a non-empty effective interface set.)
  • Если результирующий набор методов-кандидатов пуст, дальнейшая обработка на следующих этапах будет прервана, а вместо этого будет предпринята попытка обработать вызов как вызов метода расширения (вызовы метода расширения).If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned, and instead an attempt is made to process the invocation as an extension method invocation (Extension method invocations). Если это не удается, то применимые методы не существуют и возникает ошибка времени привязки.If this fails, then no applicable methods exist, and a binding-time error occurs.
  • Лучший метод набора потенциальных методов определяется с помощью правил разрешения перегрузки для разрешения перегрузки.The best method of the set of candidate methods is identified using the overload resolution rules of Overload resolution. Если не удается определить один лучший метод, вызов метода неоднозначен и возникает ошибка времени привязки.If a single best method cannot be identified, the method invocation is ambiguous, and a binding-time error occurs. При выполнении разрешения перегрузки параметры универсального метода рассматриваются после замены аргументов типа (предоставляемых или выводимых) для соответствующих параметров типа метода.When performing overload resolution, the parameters of a generic method are considered after substituting the type arguments (supplied or inferred) for the corresponding method type parameters.
  • Окончательная проверка выбранного лучшего метода выполняется:Final validation of the chosen best method is performed:
    • Метод проверяется в контексте группы методов: Если лучшим методом является статический метод, группа методов должна быть в результате simple_name или member_access через тип.The method is validated in the context of the method group: If the best method is a static method, the method group must have resulted from a simple_name or a member_access through a type. Если лучшим методом является метод экземпляра, группа методов должна быть вызвана из simple_name, member_access с помощью переменной или значения или base_access.If the best method is an instance method, the method group must have resulted from a simple_name, a member_access through a variable or value, or a base_access. Если ни одно из этих условий не выполняется, возникает ошибка времени привязки.If neither of these requirements is true, a binding-time error occurs.
    • Если лучшим методом является универсальный метод, аргументы типа (предоставляемые или выводимые) проверяются на соответствие ограничениям (удовлетворяющему ограничениям), объявленным в универсальном методе.If the best method is a generic method, the type arguments (supplied or inferred) are checked against the constraints (Satisfying constraints) declared on the generic method. Если какой-либо аргумент типа не удовлетворяет соответствующим ограничениям в параметре типа, возникает ошибка времени привязки.If any type argument does not satisfy the corresponding constraint(s) on the type parameter, a binding-time error occurs.

После того как метод был выбран и проверен во время привязки с помощью описанных выше шагов, фактический вызов во время выполнения обрабатывается в соответствии с правилами вызова члена функции, описанным в разделе Проверка разрешения динамической перегрузки во время компиляции.Once a method has been selected and validated at binding-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in Compile-time checking of dynamic overload resolution.

Ниже приведен интуитивно понятный результат правил разрешения, описанных выше. Чтобы найти конкретный метод, вызываемый вызовом метода, начните с типа, указанного в вызове метода, и продолжайте цепочку наследования, пока не будет найдено по крайней мере одно применимое, доступное объявление метода без переопределения.The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Затем выполните определение типа и разрешающее разрешение перегрузки для набора применимых, доступных, не переопределяющих методов, объявленных в этом типе, и вызовите метод, который выбран.Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected. Если метод не найден, попробуйте вместо этого обработать вызов как вызов метода расширения.If no method was found, try instead to process the invocation as an extension method invocation.

Вызовы методов расширенияExtension method invocations

При вызове метода (вызовы в упакованных экземплярах) одной из формIn a method invocation (Invocations on boxed instances) of one of the forms

expr . identifier ( )

expr . identifier ( args )

expr . identifier < typeargs > ( )

expr . identifier < typeargs > ( args )

Если нормальная обработка вызова не обнаруживает применимых методов, предпринимается попытка обработать конструкцию как вызов метода расширения.if the normal processing of the invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation. Если выражение expr или любой из аргументов имеет тип времени компиляции dynamic, методы расширения не будут применяться.If expr or any of the args has compile-time type dynamic, extension methods will not apply.

Цель — найти лучший type_name C, чтобы соответствующий статический вызов метода мог выполняться:The objective is to find the best type_name C, so that the corresponding static method invocation can take place:

C . identifier ( expr )

C . identifier ( expr , args )

C . identifier < typeargs > ( expr )

C . identifier < typeargs > ( expr , args )

Метод расширения @no__t- 0 подходит, если:An extension method Ci.Mj is eligible if:

  • Ci не является универсальным, не вложенным классомCi is a non-generic, non-nested class
  • Имя Mj является идентификаторомThe name of Mj is identifier
  • Mj доступен и применяется при применении к аргументам в качестве статического метода, как показано вышеMj is accessible and applicable when applied to the arguments as a static method as shown above
  • Неявное преобразование идентификатора, ссылки или упаковки существует из выражения expr в тип первого параметра Mj.An implicit identity, reference or boxing conversion exists from expr to the type of the first parameter of Mj.

Поиск C продолжается следующим образом:The search for C proceeds as follows:

  • Начиная с ближайшего включающего объявления пространства имен, продолжая с каждым из объявленных объявлений пространства имен и заканчивая содержащимй единицей компиляции, выполняются последовательные попытки поиска набора кандидатов методов расширения:Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to find a candidate set of extension methods:
    • Если заданное пространство имен или единица компиляции непосредственно содержит объявления неуниверсального типа Ci с допустимыми методами расширения Mj, то набором этих методов расширения является набор кандидатов.If the given namespace or compilation unit directly contains non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
    • Если типы Ci, импортированные с помощью using_static_declarations и непосредственно объявленные в пространствах имен, импортированных using_namespace_directives в заданном пространстве имен или блоке компиляции, непосредственно содержат доступные методы расширения Mj, то набор этих методов расширения является набором кандидатов.If types Ci imported by using_static_declarations and directly declared in namespaces imported by using_namespace_directives in the given namespace or compilation unit directly contain eligible extension methods Mj, then the set of those extension methods is the candidate set.
  • Если набор кандидатов не найден ни в одном из вложенных объявлений пространства имен или в единице компиляции, возникает ошибка времени компиляции.If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs.
  • В противном случае к набору кандидатов применяется разрешение перегрузки, как описано в разделе (разрешение перегрузки).Otherwise, overload resolution is applied to the candidate set as described in (Overload resolution). Если один лучший метод не найден, возникает ошибка времени компиляции.If no single best method is found, a compile-time error occurs.
  • C — это тип, в котором наилучший метод объявляется как метод расширения.C is the type within which the best method is declared as an extension method.

При использовании C в качестве целевого объекта вызов метода обрабатывается как вызов статического метода (Проверка разрешения динамической перегрузки во время компиляции).Using C as a target, the method call is then processed as a static method invocation (Compile-time checking of dynamic overload resolution).

Предыдущие правила означают, что методы экземпляра имеют приоритет над методами расширения, что методы расширения, доступные во внутренних объявлениях пространств имен, имеют приоритет над методами расширения, доступными во внешних объявлениях пространств имен, и это расширение методы, объявляемые непосредственно в пространстве имен, имеют более высокий приоритет, чем методы расширения, импортированные в то же пространство имен с помощью директивы пространства имен.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. Пример:For example:

public static class E
{
    public static void F(this object obj, int i) { }

    public static void F(this object obj, string s) { }
}

class A { }

class B
{
    public void F(int i) { }
}

class C
{
    public void F(object obj) { }
}

class X
{
    static void Test(A a, B b, C c) {
        a.F(1);              // E.F(object, int)
        a.F("hello");        // E.F(object, string)

        b.F(1);              // B.F(int)
        b.F("hello");        // E.F(object, string)

        c.F(1);              // C.F(object)
        c.F("hello");        // C.F(object)
    }
}

В этом примере метод B имеет приоритет над первым методом расширения, а метод C имеет приоритет над обоими методами расширения.In the example, B's method takes precedence over the first extension method, and C's method takes precedence over both extension methods.

public static class C
{
    public static void F(this int i) { Console.WriteLine("C.F({0})", i); }
    public static void G(this int i) { Console.WriteLine("C.G({0})", i); }
    public static void H(this int i) { Console.WriteLine("C.H({0})", i); }
}

namespace N1
{
    public static class D
    {
        public static void F(this int i) { Console.WriteLine("D.F({0})", i); }
        public static void G(this int i) { Console.WriteLine("D.G({0})", i); }
    }
}

namespace N2
{
    using N1;

    public static class E
    {
        public static void F(this int i) { Console.WriteLine("E.F({0})", i); }
    }

    class Test
    {
        static void Main(string[] args)
        {
            1.F();
            2.G();
            3.H();
        }
    }
}

Выходные данные этого примера:The output of this example is:

E.F(1)
D.G(2)
C.H(3)

D.G имеет приоритет над C.G, а E.F имеет приоритет над D.F и C.F.D.G takes precedence over C.G, and E.F takes precedence over both D.F and C.F.

Вызовы делегатовDelegate invocations

Для вызова делегата primary_expression invocation_expression должен быть значением delegate_type.For a delegate invocation, the primary_expression of the invocation_expression must be a value of a delegate_type. Кроме того, учитывая, что delegate_type является членом функции с тем же списком параметров, что и delegate_type, Delegate_type должен быть применимым (применимый член функции) относительно argument_ Список invocation_expression.Furthermore, considering the delegate_type to be a function member with the same parameter list as the delegate_type, the delegate_type must be applicable (Applicable function member) with respect to the argument_list of the invocation_expression.

Обработка во время выполнения вызова делегата формы D(A), где D — это primary_expression delegate_type , а A — необязательный argument_list, состоит из следующих шагов:The run-time processing of a delegate invocation of the form D(A), where D is a primary_expression of a delegate_type and A is an optional argument_list, consists of the following steps:

  • D вычисляется.D is evaluated. Если эта оценка вызывает исключение, дальнейшие действия не выполняются.If this evaluation causes an exception, no further steps are executed.
  • Значение D проверяется как допустимое.The value of D is checked to be valid. Если значение D равно null, выдается System.NullReferenceException и дальнейшие действия не выполняются.If the value of D is null, a System.NullReferenceException is thrown and no further steps are executed.
  • В противном случае D является ссылкой на экземпляр делегата.Otherwise, D is a reference to a delegate instance. Вызовы функции-члена (Проверка разрешения динамической перегрузки во время компиляции) выполняются для каждой из вызываемых сущностей в списке вызовов делегата.Function member invocations (Compile-time checking of dynamic overload resolution) are performed on each of the callable entities in the invocation list of the delegate. Для вызываемых сущностей, состоящих из экземпляра и метода экземпляра, экземпляром для вызова является экземпляр, содержащийся в вызываемой сущности.For callable entities consisting of an instance and instance method, the instance for the invocation is the instance contained in the callable entity.

Доступ к элементамElement access

Element_access состоит из primary_no_array_creation_expression, за которым следует маркер "[", за которым следует argument_list, за которым следует токен "]".An element_access consists of a primary_no_array_creation_expression, followed by a "[" token, followed by an argument_list, followed by a "]" token. Argument_list состоит из одного или нескольких аргументов, разделенных запятыми.The argument_list consists of one or more arguments, separated by commas.

element_access
    : primary_no_array_creation_expression '[' expression_list ']'
    ;

Argument_list element_access не может содержать аргументы ref или out.The argument_list of an element_access is not allowed to contain ref or out arguments.

Element_access динамически привязана (Динамическая привязка), если хотя бы одна из следующих служб содержит:An element_access is dynamically bound (Dynamic binding) if at least one of the following holds:

  • Primary_no_array_creation_expression имеет тип времени компиляции dynamic.The primary_no_array_creation_expression has compile-time type dynamic.
  • По крайней мере одно выражение argument_list имеет тип времени компиляции dynamic, а primary_no_array_creation_expression не имеет типа массива.At least one expression of the argument_list has compile-time type dynamic and the primary_no_array_creation_expression does not have an array type.

В этом случае компилятор классифицирует element_access как значение типа dynamic.In this case the compiler classifies the element_access as a value of type dynamic. Приведенные ниже правила определяют значение element_access , которые затем применяются во время выполнения, используя тип времени выполнения, а не тип времени компиляции primary_no_array_creation_expression и argument_list выражения, имеющие тип времени компиляции dynamic.The rules below to determine the meaning of the element_access are then applied at run-time, using the run-time type instead of the compile-time type of those of the primary_no_array_creation_expression and argument_list expressions which have the compile-time type dynamic. Если primary_no_array_creation_expression не имеет типа времени компиляции dynamic, то доступ к элементу будет ограничен во время компиляции, как описано в разделе Проверка динамического разрешения перегрузки во время компиляции.If the primary_no_array_creation_expression does not have compile-time type dynamic, then the element access undergoes a limited compile time check as described in Compile-time checking of dynamic overload resolution.

Если primary_no_array_creation_expression element_access является значением array_type, element_access является доступом к массиву (доступ к массиву).If the primary_no_array_creation_expression of an element_access is a value of an array_type, the element_access is an array access (Array access). В противном случае primary_no_array_creation_expression должен быть переменной или значением класса, структуры или типа интерфейса, имеющего один или несколько элементов индексатора, в этом случае element_access является доступом к индексатору (доступ к индексатору).Otherwise, the primary_no_array_creation_expression must be a variable or value of a class, struct, or interface type that has one or more indexer members, in which case the element_access is an indexer access (Indexer access).

Доступ к массивуArray access

Для доступа к массиву primary_no_array_creation_expression element_access должен быть значением array_type.For an array access, the primary_no_array_creation_expression of the element_access must be a value of an array_type. Более того, argument_list доступа к массиву не может содержать именованные аргументы. Число выражений в argument_list должно совпадать с рангом array_type, и каждое выражение должно иметь тип int, uint, long, ulong или быть неявно преобразуемым в один или несколько этих типов.Furthermore, the argument_list of an array access is not allowed to contain named arguments.The number of expressions in the argument_list must be the same as the rank of the array_type, and each expression must be of type int, uint, long, ulong, or must be implicitly convertible to one or more of these types.

Результатом вычисления доступа к массиву является переменная типа элемента массива, а именно элемент массива, выбираемый значениями выражений в argument_list(-ов).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.

Обработка доступа к массиву в форме во время выполнения P[A], где P — это primary_no_array_creation_expression array_type , а Aargument_list, состоит из следующих шагов:The run-time processing of an array access of the form P[A], where P is a primary_no_array_creation_expression of an array_type and A is an argument_list, consists of the following steps:

  • P вычисляется.P is evaluated. Если эта оценка вызывает исключение, дальнейшие действия не выполняются.If this evaluation causes an exception, no further steps are executed.
  • Индексные выражения argument_list оцениваются по порядку слева направо.The index expressions of the argument_list are evaluated in order, from left to right. После вычисления каждого выражения индекса выполняется неявное преобразование (неявные преобразования) к одному из следующих типов: int, uint, long, ulong.Following evaluation of each index expression, an implicit conversion (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. Например, если выражение индекса имеет тип short, то выполняется неявное преобразование в int, поскольку неявное преобразование из short в int и из short в long возможно.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.
  • Значение P проверяется как допустимое.The value of P is checked to be valid. Если значение P равно null, выдается System.NullReferenceException и дальнейшие действия не выполняются.If the value of P is null, a System.NullReferenceException is thrown and no further steps are executed.
  • Значение каждого выражения в argument_list проверяется относительно фактических границ каждого измерения экземпляра массива, на который ссылается P.The value of each expression in the argument_list is checked against the actual bounds of each dimension of the array instance referenced by P. Если одно или несколько значений выходят за пределы диапазона, выдается System.IndexOutOfRangeException и дальнейшие действия не выполняются.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.

Доступ к индексаторуIndexer access

Для доступа к индексатору primary_no_array_creation_expression element_access должен быть переменной или значением класса, структуры или типа интерфейса, и этот тип должен реализовывать один или несколько индексаторов, применимых в отношении к argument_list element_access.For an indexer access, the primary_no_array_creation_expression of the element_access must be a variable or value of a class, struct, or interface type, and this type must implement one or more indexers that are applicable with respect to the argument_list of the element_access.

Обработка индексатора во время привязки к форме P[A], где P — это primary_no_array_creation_expression класса, структуры или типа интерфейса T, а A — это argument_list, который состоит из следующих выполненыThe binding-time processing of an indexer access of the form P[A], where P is a primary_no_array_creation_expression of a class, struct, or interface type T, and A is an argument_list, consists of the following steps:

  • Набор индексаторов, предоставляемых T, создается.The set of indexers provided by T is constructed. Набор состоит из всех индексаторов, объявленных в T, или базового типа T, которые не являются объявлениями override и доступны в текущем контексте (доступ к членам).The set consists of all indexers declared in T or a base type of T that are not override declarations and are accessible in the current context (Member access).
  • Набор уменьшается до тех индексаторов, которые применимы и не скрываются другими индексаторами.The set is reduced to those indexers that are applicable and not hidden by other indexers. К каждому индексатору, S.I в наборе, применяются следующие правила, где S — это тип, в котором объявлен индексатор I:The following rules are applied to each indexer S.I in the set, where S is the type in which the indexer I is declared:
    • Если I не применяется по отношению к A (применимый член функции), то I удаляется из набора.If I is not applicable with respect to A (Applicable function member), then I is removed from the set.
    • Если I применимо по отношению к A (применимый член функции), то все индексаторы, объявленные в базовом типе S, удаляются из набора.If I is applicable with respect to A (Applicable function member), then all indexers declared in a base type of S are removed from the set.
    • Если I применимо к A (применимый член функции) и S является типом класса, отличного от object, все индексаторы, объявленные в интерфейсе, удаляются из набора.If I is applicable with respect to A (Applicable function member) and S is a class type other than object, all indexers declared in an interface are removed from the set.
  • Если результирующий набор индексаторов-кандидатов пуст, то применимые Индексаторы не существуют и возникает ошибка времени привязки.If the resulting set of candidate indexers is empty, then no applicable indexers exist, and a binding-time error occurs.
  • Лучший индексатор набора индексаторов-кандидатов определяется с помощью правил разрешения перегрузки для разрешения перегрузки.The best indexer of the set of candidate indexers is identified using the overload resolution rules of Overload resolution. Если не удается определить один лучший индексатор, доступ к индексатору неоднозначен и возникает ошибка времени привязки.If a single best indexer cannot be identified, the indexer access is ambiguous, and a binding-time error occurs.
  • Индексные выражения argument_list оцениваются по порядку слева направо.The index expressions of the argument_list are evaluated in order, from left to right. Результатом обработки доступа к индексатору является выражение, классифицированное как доступ к индексатору.The result of processing the indexer access is an expression classified as an indexer access. Выражение доступа к индексатору ссылается на индексатор, определенный на предыдущем шаге, и имеет связанное выражение экземпляра P и связанный список аргументов A.The indexer access expression references the indexer determined in the step above, and has an associated instance expression of P and an associated argument list of A.

В зависимости от контекста, в котором он используется, доступ к индексатору вызывает вызов метода доступа Get или метода доступа set индексатора.Depending on the context in which it is used, an indexer access causes invocation of either the get accessor or the set accessor of the indexer. Если доступ к индексатору является целевым объектом назначения, метод доступа set вызывается для назначения нового значения (простое присваивание).If the indexer access is the target of an assignment, the set accessor is invoked to assign a new value (Simple assignment). Во всех остальных случаях вызывается метод доступа Get для получения текущего значения (значений выражений).In all other cases, the get accessor is invoked to obtain the current value (Values of expressions).

Этот доступThis access

This_access состоит из зарезервированного слова this.A this_access consists of the reserved word this.

this_access
    : 'this'
    ;

This_access разрешается только в блоке конструктора экземпляра, метода экземпляра или метода доступа к экземпляру.A this_access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. Он имеет одно из следующих значений:It has one of the following meanings:

  • Если this используется в primary_expression в конструкторе экземпляра класса, он классифицируется как значение.When this is used in a primary_expression within an instance constructor of a class, it is classified as a value. Тип значения — это тип экземпляра (тип экземпляра) класса, в котором происходит использование, а значение является ссылкой на создаваемый объект.The type of the value is the instance type (The instance type) of the class within which the usage occurs, and the value is a reference to the object being constructed.
  • Если this используется в primary_expression в экземпляре метода экземпляра или методе доступа к экземпляру класса, он классифицируется как значение.When this is used in a primary_expression within an instance method or instance accessor of a class, it is classified as a value. Тип значения — это тип экземпляра (тип экземпляра) класса, в котором происходит использование, а значение является ссылкой на объект, для которого был вызван метод или метод доступа.The type of the value is the instance type (The instance type) of the class within which the usage occurs, and the value is a reference to the object for which the method or accessor was invoked.
  • Если this используется в primary_expression в конструкторе экземпляра структуры, он классифицируется как переменная.When this is used in a primary_expression within an instance constructor of a struct, it is classified as a variable. Тип переменной — это тип экземпляра (тип экземпляра) структуры, в которой происходит использование, а переменная представляет создаваемую структуру.The type of the variable is the instance type (The instance type) of the struct within which the usage occurs, and the variable represents the struct being constructed. Переменная this конструктора экземпляра структуры ведет себя точно так же, как параметр out типа структуры — в частности это означает, что переменная должна быть определенно назначена в каждом пути выполнения конструктора экземпляра.The this variable of an instance constructor of a struct behaves exactly the same as an out parameter of the struct type—in particular, this means that the variable must be definitely assigned in every execution path of the instance constructor.
  • Если this используется в primary_expression в экземпляре метода экземпляра или методе доступа к экземпляру структуры, он классифицируется как переменная.When this is used in a primary_expression within an instance method or instance accessor of a struct, it is classified as a variable. Тип переменной — это тип экземпляра (тип экземпляра) структуры, в которой происходит использование.The type of the variable is the instance type (The instance type) of the struct within which the usage occurs.
    • Если метод или метод доступа не является итератором (итераторами), то переменная this представляет структуру, для которой был вызван метод или метод доступа, и ведет себя точно так же, как параметр ref типа структуры.If the method or accessor is not an iterator (Iterators), the this variable represents the struct for which the method or accessor was invoked, and behaves exactly the same as a ref parameter of the struct type.
    • Если метод или метод доступа является итератором, то переменная this представляет копию структуры, для которой был вызван метод или метод доступа, и ведет себя точно так же, как параметр значения типа структуры.If the method or accessor is an iterator, the this variable represents a copy of the struct for which the method or accessor was invoked, and behaves exactly the same as a value parameter of the struct type.

Использование this в primary_expression в контексте, отличном от перечисленных выше, является ошибкой времени компиляции.Use of this in a primary_expression in a context other than the ones listed above is a compile-time error. В частности, невозможно ссылаться на this в статическом методе, методе доступа к статическим свойствам или в variable_initializer объявления поля.In particular, it is not possible to refer to this in a static method, a static property accessor, or in a variable_initializer of a field declaration.

Базовый доступBase access

Base_access состоит из зарезервированного слова base, за которым следует маркер "." и идентификатор или argument_list , заключенные в квадратные скобки:A base_access consists of the reserved word base followed by either a "." token and an identifier or an argument_list enclosed in square brackets:

base_access
    : 'base' '.' identifier
    | 'base' '[' expression_list ']'
    ;

Base_access используется для доступа к членам базового класса, которые скрыты с помощью элементов с одинаковыми именами в текущем классе или структуре.A base_access is used to access base class members that are hidden by similarly named members in the current class or struct. Base_access разрешается только в блоке конструктора экземпляра, метода экземпляра или метода доступа к экземпляру.A base_access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. Если base.I встречается в классе или структуре, I должен Отметим член базового класса этого класса или структуры.When base.I occurs in a class or struct, I must denote a member of the base class of that class or struct. Аналогично, когда base[E] встречается в классе, в базовом классе должен существовать применимый индексатор.Likewise, when base[E] occurs in a class, an applicable indexer must exist in the base class.

Во время привязки выражения base_access в форме base.I и base[E] оцениваются точно так же, как если бы они были написаны ((B)this).I и ((B)this)[E], где B является базовым классом класса или структуры, в которой встречается конструкция.At binding-time, base_access expressions of the form base.I and base[E] are evaluated exactly as if they were written ((B)this).I and ((B)this)[E], where B is the base class of the class or struct in which the construct occurs. Таким образом, base.I и base[E] соответствуют this.I и this[E], за исключением this, просматривается как экземпляр базового класса.Thus, base.I and base[E] correspond to this.I and this[E], except this is viewed as an instance of the base class.

Когда base_access ссылается на виртуальную функцию-член (метод, свойство или индексатор), определение того, какой из вызываемых функций должен вызываться во время выполнения (Проверка разрешения динамической перегрузки), изменяется.When a base_access references a virtual function member (a method, property, or indexer), the determination of which function member to invoke at run-time (Compile-time checking of dynamic overload resolution) is changed. Вызываемая функция определяется путем поиска наиболее производной реализации (виртуальных методов) члена функции по отношению к B (а не по отношению к типу времени выполнения this, как обычно в случае не базового доступа). .The function member that is invoked is determined by finding the most derived implementation (Virtual methods) of the function member with respect to B (instead of with respect to the run-time type of this, as would be usual in a non-base access). Поэтому в override члена функции virtual можно использовать base_access , чтобы вызвать наследуемую реализацию члена функции.Thus, within an override of a virtual function member, a base_access can be used to invoke the inherited implementation of the function member. Если функция-член, на которую ссылается base_access , является абстрактной, возникает ошибка времени привязки.If the function member referenced by a base_access is abstract, a binding-time error occurs.

Постфиксные операторы инкремента и декрементаPostfix increment and decrement operators

post_increment_expression
    : primary_expression '++'
    ;

post_decrement_expression
    : primary_expression '--'
    ;

Операндом постфиксной операции инкремента или декремента должно быть выражение, классифицированное как переменная, доступ к свойству или доступ к индексатору.The operand of a postfix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. Результатом операции является значение того же типа, что и у операнда.The result of the operation is a value of the same type as the operand.

Если primary_expression имеет тип времени компиляции dynamic, то оператор динамически привязан (Динамическая привязка), post_increment_expression или post_decrement_expression имеет тип времени компиляции dynamic и во время выполнения применяются следующие правила с использованием типа primary_expression.If the primary_expression has the compile-time type dynamic then the operator is dynamically bound (Dynamic binding), the post_increment_expression or post_decrement_expression has the compile-time type dynamic and the following rules are applied at run-time using the run-time type of the primary_expression.

Если операнд постфиксной операции инкремента или декремента является доступ к свойству или индексатору, то свойство или индексатор должны иметь как get, так и метод доступа set.If the operand of a postfix increment or decrement operation is a property or indexer access, the property or indexer must have both a get and a set accessor. Если это не так, возникает ошибка времени привязки.If this is not the case, a binding-time error occurs.

Разрешение перегрузки унарного оператора (разрешение перегрузки унарного оператора) применяется для выбора конкретной реализации оператора.Unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. Стандартные операторы ++ и -- существуют для следующих типов: sbyte, byte, short, ushort, int, uint, long, ulong, 0, 1, 2, 3 и любого типа перечисления.Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. Предопределенные операторы ++ возвращают значение, полученное при добавлении 1 к операнду, и предопределенные операторы -- возвращают значение, полученное путем вычитания 1 из операнда.The predefined ++ operators return the value produced by adding 1 to the operand, and the predefined -- operators return the value produced by subtracting 1 from the operand. В контексте checked, если результат этого сложения или вычитания находится вне диапазона результирующего типа, а тип результата является целочисленным типом или типом перечисления, выдается System.OverflowException.In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a System.OverflowException is thrown.

Обработка постфиксной операции инкремента или декремента формы x++ или x-- состоит из следующих шагов:The run-time processing of a postfix increment or decrement operation of the form x++ or x-- consists of the following steps:

  • Если x классифицируется как переменная:If x is classified as a variable:
    • x вычисляется для создания переменной.x is evaluated to produce the variable.
    • Значение x сохраняется.The value of x is saved.
    • Выбранный оператор вызывается с сохраненным значением x в качестве аргумента.The selected operator is invoked with the saved value of x as its argument.
    • Значение, возвращаемое оператором, хранится в расположении, заданном вычислением x.The value returned by the operator is stored in the location given by the evaluation of x.
    • Сохраненное значение x станет результатом операции.The saved value of x becomes the result of the operation.
  • Если x классифицируется как доступ к свойству или индексатору:If x is classified as a property or indexer access:
    • Выражение экземпляра (если x не static) и список аргументов (если x — доступ к индексатору), связанный с x, вычисляется и результаты используются в последующих вызовах методов доступа get и set.The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent get and set accessor invocations.
    • Вызывается метод доступа get для x, а возвращаемое значение сохраняется.The get accessor of x is invoked and the returned value is saved.
    • Выбранный оператор вызывается с сохраненным значением x в качестве аргумента.The selected operator is invoked with the saved value of x as its argument.
    • Метод доступа set для x вызывается со значением, возвращаемым оператором в качестве аргумента value.The set accessor of x is invoked with the value returned by the operator as its value argument.
    • Сохраненное значение x станет результатом операции.The saved value of x becomes the result of the operation.

Операторы ++ и -- также поддерживают префиксную нотацию (Операторы инкремента и декремента).The ++ and -- operators also support prefix notation (Prefix increment and decrement operators). Как правило, результатом x++ или x-- является значение x перед операцией, в то время как результатом ++x или --x является значение x после операции.Typically, the result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. В любом случае x имеет то же значение, что и после операции.In either case, x itself has the same value after the operation.

Реализацию operator ++ или operator -- можно вызвать с помощью постфиксной или префиксной нотации.An operator ++ or operator -- implementation can be invoked using either postfix or prefix notation. Нельзя иметь отдельные реализации операторов для двух нотаций.It is not possible to have separate operator implementations for the two notations.

Оператор newThe new operator

Оператор new используется для создания новых экземпляров типов.The new operator is used to create new instances of types.

Существует три формы выражений new:There are three forms of new expressions:

  • Выражения создания объектов используются для создания новых экземпляров типов классов и типов значений.Object creation expressions are used to create new instances of class types and value types.
  • Выражения создания массива используются для создания новых экземпляров типов массивов.Array creation expressions are used to create new instances of array types.
  • Выражения создания делегатов используются для создания новых экземпляров типов делегатов.Delegate creation expressions are used to create new instances of delegate types.

Оператор new подразумевает создание экземпляра типа, но не обязательно подразумевает динамическое выделение памяти.The new operator implies creation of an instance of a type, but does not necessarily imply dynamic allocation of memory. В частности, экземпляры типов значений не нуждаются в дополнительной памяти за переменными, в которых они находятся, а динамическое выделение не происходит при использовании new для создания экземпляров типов значений.In particular, instances of value types require no additional memory beyond the variables in which they reside, and no dynamic allocations occur when new is used to create instances of value types.

Выражения создания объектовObject creation expressions

Object_creation_expression используется для создания нового экземпляра class_type или value_type.An object_creation_expression is used to create a new instance of a class_type or a value_type.

object_creation_expression
    : 'new' type '(' argument_list? ')' object_or_collection_initializer?
    | 'new' type object_or_collection_initializer
    ;

object_or_collection_initializer
    : object_initializer
    | collection_initializer
    ;

Тип object_creation_expression должен быть class_type, value_type или type_parameter.The type of an object_creation_expression must be a class_type, a value_type or a type_parameter. Тип не может быть abstract class_type.The type cannot be an abstract class_type.

Необязательные argument_list (списки аргументов) разрешены только в том случае, если тип является class_type или struct_type.The optional argument_list (Argument lists) is permitted only if the type is a class_type or a struct_type.

Выражение создания объекта может опускать список аргументов конструктора и заключать круглые скобки, если оно содержит инициализатор объекта или инициализатор коллекции.An object creation expression can omit the constructor argument list and enclosing parentheses provided it includes an object initializer or collection initializer. Пропуск списка аргументов конструктора и заключенных в круглые скобки эквивалентно указанию пустого списка аргументов.Omitting the constructor argument list and enclosing parentheses is equivalent to specifying an empty argument list.

Обработка выражения создания объекта, включающего инициализатор объекта или инициализатор коллекции, состоит из первой обработки конструктора экземпляра и последующей обработки инициализации элемента или элемента, заданного инициализатором объекта ( Инициализаторы объектов) или инициализатор коллекции (Инициализаторы коллекций).Processing of an object creation expression that includes an object initializer or collection initializer consists of first processing the instance constructor and then processing the member or element initializations specified by the object initializer (Object initializers) or collection initializer (Collection initializers).

Если любой из аргументов необязательного argument_list имеет тип времени компиляции dynamic, то object_creation_expression динамически привязан (Динамическая привязка), а следующие правила применяются во время выполнения с использованием времени выполнения. тип этих аргументов argument_list , которые имеют тип времени компиляции dynamic.If any of the arguments in the optional argument_list has the compile-time type dynamic then the object_creation_expression is dynamically bound (Dynamic binding) and the following rules are applied at run-time using the run-time type of those arguments of the argument_list that have the compile time type dynamic. Однако при создании объекта будет выполнена ограниченная проверка времени компиляции, как описано в разделе Проверка динамической перегрузки во время компиляции.However, the object creation undergoes a limited compile time check as described in Compile-time checking of dynamic overload resolution.

Обработка object_creation_expression в форме времени привязки формы new T(A), где T — это class_type или value_type , а A — необязательный argument_list, состоит из следующих шагов:The binding-time processing of an object_creation_expression of the form new T(A), where T is a class_type or a value_type and A is an optional argument_list, consists of the following steps:

  • Если T является value_type и A отсутствует:If T is a value_type and A is not present:
    • Object_creation_expression является вызовом конструктора по умолчанию.The object_creation_expression is a default constructor invocation. Результатом object_creation_expression является значение типа T, а именно значение по умолчанию для T в соответствии с определением в типе System. ValueType.The result of the object_creation_expression is a value of type T, namely the default value for T as defined in The System.ValueType type.
  • В противном случае, если T является type_parameter , а A отсутствует:Otherwise, if T is a type_parameter and A is not present:
    • Если ограничение типа значения или ограничение конструктора (ограничения параметра типа) заданы для T, возникает ошибка времени привязки.If no value type constraint or constructor constraint (Type parameter constraints) has been specified for T, a binding-time error occurs.
    • Результатом object_creation_expression является значение типа времени выполнения, к которому привязан параметр типа, а именно результат вызова конструктора по умолчанию этого типа.The result of the object_creation_expression is a value of the run-time type that the type parameter has been bound to, namely the result of invoking the default constructor of that type. Тип времени выполнения может быть ссылочным типом или типом значения.The run-time type may be a reference type or a value type.
  • В противном случае, если T является class_type или struct_type:Otherwise, if T is a class_type or a struct_type:
    • Если T является abstract class_type, возникает ошибка времени компиляции.If T is an abstract class_type, a compile-time error occurs.
    • Конструктор экземпляра для вызова определяется с помощью правил разрешения перегрузки для разрешения перегрузки.The instance constructor to invoke is determined using the overload resolution rules of Overload resolution. Набор конструкторов экземпляров кандидатов состоит из всех доступных конструкторов экземпляров, объявленных в T, которые применимы по отношению к A (применимый член функции).The set of candidate instance constructors consists of all accessible instance constructors declared in T which are applicable with respect to A (Applicable function member). Если набор конструкторов экземпляров кандидатов пуст или не удается определить один лучший конструктор экземпляров, возникает ошибка времени привязки.If the set of candidate instance constructors is empty, or if a single best instance constructor cannot be identified, a binding-time error occurs.
    • Результатом object_creation_expression является значение типа T, а именно значение, созданное путем вызова конструктора экземпляра, определенного на предыдущем шаге.The result of the object_creation_expression is a value of type T, namely the value produced by invoking the instance constructor determined in the step above.
  • В противном случае object_creation_expression является недопустимым и возникает ошибка времени привязки.Otherwise, the object_creation_expression is invalid, and a binding-time error occurs.

Даже если object_creation_expression имеет динамическую привязку, тип времени компиляции по-прежнему T.Even if the object_creation_expression is dynamically bound, the compile-time type is still T.

Обработка object_creation_expression формы во время выполнения new T(A), где Tclass_type или struct_type , а A — необязательное argument_list, состоит из следующих этапов.The run-time processing of an object_creation_expression of the form new T(A), where T is class_type or a struct_type and A is an optional argument_list, consists of the following steps:

  • Если T является class_type:If T is a class_type:
    • Новый экземпляр класса T выделен.A new instance of class T is allocated. Если недостаточно памяти для выделения нового экземпляра, выдается System.OutOfMemoryException и дальнейшие действия не выполняются.If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
    • Все поля нового экземпляра инициализируются значениями по умолчанию (значения по умолчанию).All fields of the new instance are initialized to their default values (Default values).
    • Конструктор экземпляра вызывается в соответствии с правилами вызова функции-члена (Проверка динамического разрешения перегрузки во время компиляции).The instance constructor is invoked according to the rules of function member invocation (Compile-time checking of dynamic overload resolution). Ссылка на вновь выделенный экземпляр автоматически передается конструктору экземпляра, и к экземпляру можно получить доступ из этого конструктора, как this.A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this.
  • Если T является struct_type:If T is a struct_type:
    • Экземпляр типа T создается путем выделения временной локальной переменной.An instance of type T is created by allocating a temporary local variable. Поскольку конструктор экземпляра struct_type требуется для того, чтобы определенно присвоить значение каждому полю создаваемого экземпляра, инициализация временной переменной не требуется.Since an instance constructor of a struct_type is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary.
    • Конструктор экземпляра вызывается в соответствии с правилами вызова функции-члена (Проверка динамического разрешения перегрузки во время компиляции).The instance constructor is invoked according to the rules of function member invocation (Compile-time checking of dynamic overload resolution). Ссылка на вновь выделенный экземпляр автоматически передается конструктору экземпляра, и к экземпляру можно получить доступ из этого конструктора, как this.A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this.

Инициализаторы объектовObject initializers

Инициализатор объекта задает значения для одного или нескольких полей, свойств или индексированных элементов объекта.An object initializer specifies values for zero or more fields, properties or indexed elements of an object.

object_initializer
    : '{' member_initializer_list? '}'
    | '{' member_initializer_list ',' '}'
    ;

member_initializer_list
    : member_initializer (',' member_initializer)*
    ;

member_initializer
    : initializer_target '=' initializer_value
    ;

initializer_target
    : identifier
    | '[' argument_list ']'
    ;

initializer_value
    : expression
    | object_or_collection_initializer
    ;

Инициализатор объекта состоит из последовательности инициализаторов элементов, заключенной в { и } токенов и разделенных запятыми.An object initializer consists of a sequence of member initializers, enclosed by { and } tokens and separated by commas. Каждый member_initializer обозначает целевой объект для инициализации.Each member_initializer designates a target for the initialization. Идентификатору должно быть присвоено имя доступного поля или свойства инициализируемого объекта, тогда как argument_list , заключенные в квадратные скобки, должны указывать аргументы для доступного индексатора для инициализируемого объекта.An identifier must name an accessible field or property of the object being initialized, whereas an argument_list enclosed in square brackets must specify arguments for an accessible indexer on the object being initialized. Для инициализатора объектов необходимо включить несколько инициализаторов членов для одного поля или свойства.It is an error for an object initializer to include more than one member initializer for the same field or property.

За каждым initializer_target следует знак равенства и либо выражение, либо инициализатор объекта, либо инициализатор коллекции.Each initializer_target is followed by an equals sign and either an expression, an object initializer or a collection initializer. Выражения в инициализаторе объекта невозможно ссылаться на только что созданный объект, который он инициализирует.It is not possible for expressions within the object initializer to refer to the newly created object it is initializing.

Инициализатор члена, указывающий выражение после того, как знак равенства обрабатывается так же, как назначение (простое присваивание) целевому объекту.A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (Simple assignment) to the target.

Инициализатор элемента, указывающий инициализатор объекта после знака равенства, — это инициализатор вложенного объекта, т. е. Инициализация внедренного объекта.A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Вместо присвоения нового значения полю или свойству назначения в инициализаторе вложенного объекта обрабатываются как назначения членам поля или свойства.Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Инициализаторы вложенных объектов не могут применяться к свойствам с типом значения или к полям только для чтения с типом значения.Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.

Инициализатор элемента, указывающий инициализатор коллекции после знака равенства, — это инициализация внедренной коллекции.A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Вместо присвоения новой коллекции целевому полю, свойству или индексатору элементы, заданные в инициализаторе, добавляются в коллекцию, на которую ссылается целевой объект.Instead of assigning a new collection to the target field, property or indexer, the elements given in the initializer are added to the collection referenced by the target. Целевой объект должен иметь тип коллекции, удовлетворяющий требованиям, указанным в инициализаторах коллекций.The target must be of a collection type that satisfies the requirements specified in Collection initializers.

Аргументы для инициализатора индекса всегда будут оцениваться ровно один раз.The arguments to an index initializer will always be evaluated exactly once. Таким же, даже если аргументы никогда не используются (например, из-за пустого вложенного инициализатора), они будут оцениваться для их побочных эффектов.Thus, even if the arguments end up never getting used (e.g. because of an empty nested initializer), they will be evaluated for their side effects.

Следующий класс представляет точку с двумя координатами:The following class represents a point with two coordinates:

public class Point
{
    int x, y;

    public int X { get { return x; } set { x = value; } }
    public int Y { get { return y; } set { y = value; } }
}

Экземпляр Point можно создать и инициализировать следующим образом:An instance of Point can be created and initialized as follows:

Point a = new Point { X = 0, Y = 1 };

который имеет тот же результат, что иwhich has the same effect as

Point __a = new Point();
__a.X = 0;
__a.Y = 1; 
Point a = __a;

где __a — невидимая и недоступная временная переменная.where __a is an otherwise invisible and inaccessible temporary variable. Следующий класс представляет прямоугольник, созданный из двух точек:The following class represents a rectangle created from two points:

public class Rectangle
{
    Point p1, p2;

    public Point P1 { get { return p1; } set { p1 = value; } }
    public Point P2 { get { return p2; } set { p2 = value; } }
}

Экземпляр Rectangle можно создать и инициализировать следующим образом:An instance of Rectangle can be created and initialized as follows:

Rectangle r = new Rectangle {
    P1 = new Point { X = 0, Y = 1 },
    P2 = new Point { X = 2, Y = 3 }
};

который имеет тот же результат, что иwhich has the same effect as

Rectangle __r = new Rectangle();
Point __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
__r.P1 = __p1;
Point __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
__r.P2 = __p2; 
Rectangle r = __r;

где __r, __p1 и __p2 являются временными переменными, которые в противном случае невидимы и недоступны.where __r, __p1 and __p2 are temporary variables that are otherwise invisible and inaccessible.

Если конструктор Rectangle выделяет два внедренных экземпляра PointIf Rectangle's constructor allocates the two embedded Point instances

public class Rectangle
{
    Point p1 = new Point();
    Point p2 = new Point();

    public Point P1 { get { return p1; } }
    public Point P2 { get { return p2; } }
}

следующую конструкцию можно использовать для инициализации внедренных экземпляров Point вместо назначения новых экземпляров:the following construct can be used to initialize the embedded Point instances instead of assigning new instances:

Rectangle r = new Rectangle {
    P1 = { X = 0, Y = 1 },
    P2 = { X = 2, Y = 3 }
};

который имеет тот же результат, что иwhich has the same effect as

Rectangle __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
Rectangle r = __r;

При наличии соответствующего определения C, в следующем примере:Given an appropriate definition of C, the following example:

var c = new C {
    x = true,
    y = { a = "Hello" },
    z = { 1, 2, 3 },
    ["x"] = 5,
    [0,0] = { "a", "b" },
    [1,2] = {}
};

эквивалентна этой серии назначений:is equivalent to this series of assignments:

C __c = new C();
__c.x = true;
__c.y.a = "Hello";
__c.z.Add(1); 
__c.z.Add(2);
__c.z.Add(3);
string __i1 = "x";
__c[__i1] = 5;
int __i2 = 0, __i3 = 0;
__c[__i2,__i3].Add("a");
__c[__i2,__i3].Add("b");
int __i4 = 1, __i5 = 2;
var c = __c;

там, где __c и т. д., создаются переменные, невидимые и недоступные для исходного кода.where __c, etc., are generated variables that are invisible and inaccessible to the source code. Обратите внимание, что аргументы для [0,0] вычисляются только один раз, а аргументы для [1,2] вычисляются один раз, даже если они никогда не используются.Note that the arguments for [0,0] are evaluated only once, and the arguments for [1,2] are evaluated once even though they are never used.

Инициализаторы коллекцийCollection initializers

Инициализатор коллекции задает элементы коллекции.A collection initializer specifies the elements of a collection.

collection_initializer
    : '{' element_initializer_list '}'
    | '{' element_initializer_list ',' '}'
    ;

element_initializer_list
    : element_initializer (',' element_initializer)*
    ;

element_initializer
    : non_assignment_expression
    | '{' expression_list '}'
    ;

expression_list
    : expression (',' expression)*
    ;

Инициализатор коллекции состоит из последовательности инициализаторов элементов, заключенной в { и } токенов и разделенных запятыми.A collection initializer consists of a sequence of element initializers, enclosed by { and } tokens and separated by commas. Каждый инициализатор элемента указывает элемент, добавляемый в инициализируемый объект коллекции, и состоит из списка выражений, заключенных в { и } токенов и разделенных запятыми.Each element initializer specifies an element to be added to the collection object being initialized, and consists of a list of expressions enclosed by { and } tokens and separated by commas. Инициализатор элемента с одним выражением может быть написан без скобок, но не может быть выражением присваивания, чтобы избежать неоднозначности инициализаторов членов.A single-expression element initializer can be written without braces, but cannot then be an assignment expression, to avoid ambiguity with member initializers. Non_assignment_expression производство определяется в выражении.The non_assignment_expression production is defined in Expression.

Ниже приведен пример выражения создания объекта, включающего инициализатор набора:The following is an example of an object creation expression that includes a collection initializer:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Объект коллекции, к которому применяется инициализатор коллекции, должен иметь тип, реализующий System.Collections.IEnumerable, или ошибка времени компиляции.The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable or a compile-time error occurs. Для каждого указанного элемента по порядку инициализатор коллекции вызывает метод Add для целевого объекта со списком выражений инициализатора элементов в качестве списка аргументов, применяя обычные поиски членов и разрешение перегрузки для каждого вызова.For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal member lookup and overload resolution for each invocation. Таким способом, объект коллекции должен иметь применимый экземпляр или метод расширения с именем Add для каждого инициализатора элемента.Thus, the collection object must have an applicable instance or extension method with the name Add for each element initializer.

Следующий класс представляет контакт с именем и списком номеров телефонов:The following class represents a contact with a name and a list of phone numbers:

public class Contact
{
    string name;
    List<string> phoneNumbers = new List<string>();

    public string Name { get { return name; } set { name = value; } }

    public List<string> PhoneNumbers { get { return phoneNumbers; } }
}

@No__t-0 можно создать и инициализировать следующим образом:A List<Contact> can be created and initialized as follows:

var contacts = new List<Contact> {
    new Contact {
        Name = "Chris Smith",
        PhoneNumbers = { "206-555-0101", "425-882-8080" }
    },
    new Contact {
        Name = "Bob Harris",
        PhoneNumbers = { "650-555-0199" }
    }
};

который имеет тот же результат, что иwhich has the same effect as

var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;

где __clist, __c1 и __c2 являются временными переменными, которые в противном случае невидимы и недоступны.where __clist, __c1 and __c2 are temporary variables that are otherwise invisible and inaccessible.

Выражения создания массиваArray creation expressions

Array_creation_expression используется для создания нового экземпляра array_type.An array_creation_expression is used to create a new instance of an array_type.

array_creation_expression
    : 'new' non_array_type '[' expression_list ']' rank_specifier* array_initializer?
    | 'new' array_type array_initializer
    | 'new' rank_specifier array_initializer
    ;

Выражение создания массива первой формы выделяет экземпляр массива типа, полученный в результате удаления каждого из отдельных выражений из списка выражений.An array creation expression of the first form allocates an array instance of the type that results from deleting each of the individual expressions from the expression list. Например, выражение создания массива new int[10,20] создает экземпляр массива типа int[,], а выражение создания массива new int[10][,] создает массив типа int[][,].For example, the array creation expression new int[10,20] produces an array instance of type int[,], and the array creation expression new int[10][,] produces an array of type int[][,]. Каждое выражение в списке выражений должно иметь тип int, uint, long или ulong или неявно преобразовать в один или несколько этих типов.Each expression in the expression list must be of type int, uint, long, or ulong, or implicitly convertible to one or more of these types. Значение каждого выражения определяет длину соответствующего измерения в новом выделенном экземпляре массива.The value of each expression determines the length of the corresponding dimension in the newly allocated array instance. Поскольку длина измерения массива должна быть неотрицательной, ошибка времени компиляции может быть constant_expression с отрицательным значением в списке выражений.Since the length of an array dimension must be nonnegative, it is a compile-time error to have a constant_expression with a negative value in the expression list.

За исключением ненадежного контекста (незащищенныеконтексты), компоновка массивов не определена.Except in an unsafe context (Unsafe contexts), the layout of arrays is unspecified.

Если выражение создания массива в первой форме содержит инициализатор массива, каждое выражение в списке выражений должно быть константой, а ранг и длина измерения, указанные в списке выражений, должны соответствовать значениям инициализатора массива.If an array creation expression of the first form includes an array initializer, each expression in the expression list must be a constant and the rank and dimension lengths specified by the expression list must match those of the array initializer.

В выражении создания массива во второй или третьей форме ранг указанного типа массива или описателя ранжирования должен соответствовать значению инициализатора массива.In an array creation expression of the second or third form, the rank of the specified array type or rank specifier must match that of the array initializer. Отдельные длины измерений выводятся из числа элементов в каждом из соответствующих уровней вложенности инициализатора массива.The individual dimension lengths are inferred from the number of elements in each of the corresponding nesting levels of the array initializer. Таким же выражениеThus, the expression

new int[,] {{0, 1}, {2, 3}, {4, 5}}

точно соответствуетexactly corresponds to

new int[3, 2] {{0, 1}, {2, 3}, {4, 5}}

Выражение создания массива третьей формы называется выражением создания массива, неявно типизированным.An array creation expression of the third form is referred to as an implicitly typed array creation expression. Он аналогичен второй форме, за исключением того, что тип элемента массива не указывается явно, но определяется как наиболее общий тип (Поиск наиболее общего типа набора выражений) набора выражений в инициализаторе массива.It is similar to the second form, except that the element type of the array is not explicitly given, but determined as the best common type (Finding the best common type of a set of expressions) of the set of expressions in the array initializer. Для многомерного массива, т. е. в котором rank_specifier содержит по крайней мере одну запятую, этот набор состоит из всех выраженийs, найденных во вложенных array_initializers.For a multidimensional array, i.e., one where the rank_specifier contains at least one comma, this set comprises all expressions found in nested array_initializers.

Инициализаторы массивов описаны далее в разделе Инициализаторы массивов.Array initializers are described further in Array initializers.

Результат вычисления выражения создания массива классифицируется как значение, а именно ссылку на только что выделенный экземпляр массива.The result of evaluating an array creation expression is classified as a value, namely a reference to the newly allocated array instance. Обработка выражения создания массива во время выполнения состоит из следующих шагов:The run-time processing of an array creation expression consists of the following steps:

  • Выражения длины измерения expression_list оцениваются по порядку слева направо.The dimension length expressions of the expression_list are evaluated in order, from left to right. После вычисления каждого выражения выполняется неявное преобразование (неявные преобразования) к одному из следующих типов: int, uint, long, ulong.Following evaluation of each expression, an implicit conversion (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. Если вычисление выражения или последующее неявное преобразование вызывает исключение, дальнейшие выражения не вычисляются и дальнейшие действия не выполняются.If evaluation of an expression or the subsequent implicit conversion causes an exception, then no further expressions are evaluated and no further steps are executed.
  • Вычисленные значения для длин измерений проверяются следующим образом.The computed values for the dimension lengths are validated as follows. Если одно или несколько значений меньше нуля, выдается System.OverflowException и дальнейшие действия не выполняются.If one or more of the values are less than zero, a System.OverflowException is thrown and no further steps are executed.
  • Выделяется экземпляр массива с заданной длиной измерений.An array instance with the given dimension lengths is allocated. Если недостаточно памяти для выделения нового экземпляра, выдается System.OutOfMemoryException и дальнейшие действия не выполняются.If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
  • Все элементы нового экземпляра массива инициализируются значениями по умолчанию (значения по умолчанию).All elements of the new array instance are initialized to their default values (Default values).
  • Если выражение создания массива содержит инициализатор массива, то каждое выражение в инициализаторе массива вычисляется и назначается соответствующему элементу массива.If the array creation expression contains an array initializer, then each expression in the array initializer is evaluated and assigned to its corresponding array element. Вычисления и присваивания выполняются в том порядке, в котором выражения записываются в инициализаторе массива — иными словами, элементы инициализируются в возрастающем порядке индексов, причем сначала возрастает измерение самого правого размера.The evaluations and assignments are performed in the order the expressions are written in the array initializer—in other words, elements are initialized in increasing index order, with the rightmost dimension increasing first. Если при вычислении заданного выражения или последующего присваивания соответствующему элементу массива вызывается исключение, последующие элементы не инициализируются (а остальные элементы, таким образом, имеют значения по умолчанию).If evaluation of a given expression or the subsequent assignment to the corresponding array element causes an exception, then no further elements are initialized (and the remaining elements will thus have their default values).

Выражение создания массива позволяет создать экземпляр массива с элементами типа массива, но элементы такого массива необходимо инициализировать вручную.An array creation expression permits instantiation of an array with elements of an array type, but the elements of such an array must be manually initialized. Например, операторFor example, the statement

int[][] a = new int[100][];

создает одномерный массив с 100 элементами типа int[].creates a single-dimensional array with 100 elements of type int[]. Начальным значением каждого элемента является null.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

int[][] a = new int[100][5];        // Error

приводит к ошибке во время компиляции.results in a compile-time error. Создание экземпляров подмассивов вместо этого необходимо выполнять вручную, как вInstantiation of the sub-arrays must instead be performed manually, as in

int[][] a = new int[100][];
for (int i = 0; i < 100; i++) a[i] = new int[5];

Если массив массивов имеет «прямоугольную» фигуру, то есть если вложенные массивы имеют одинаковую длину, более эффективно использовать многомерный массив.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. В приведенном выше примере создание массива массивов создает 101 объектов — один внешний массив и подмассивы 100.In the example above, instantiation of the array of arrays creates 101 objects—one outer array and 100 sub-arrays. Напротив,In contrast,

int[,] = new int[100, 5];

создает только один объект, двухмерный массив и выполняет выделение в одной инструкции.creates only a single object, a two-dimensional array, and accomplishes the allocation in a single statement.

Ниже приведены примеры выражений для создания неявно типизированных массивов.The following are examples of implicitly typed array creation expressions:

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

Последнее выражение вызывает ошибку во время компиляции, поскольку ни int, ни string не могут быть неявно преобразованы в другой, и поэтому наиболее распространенный тип не существует.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. В этом случае необходимо использовать выражение создания явно типизированного массива, например, указав тип object[].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.

Выражения создания неявно типизированных массивов можно сочетать с инициализаторами анонимных объектов (выражениями создания анонимных объектов) для создания анонимно типизированных структур данных.Implicitly typed array creation expressions can be combined with anonymous object initializers (Anonymous object creation expressions) to create anonymously typed data structures. Пример:For example:

var contacts = new[] {
    new {
        Name = "Chris Smith",
        PhoneNumbers = new[] { "206-555-0101", "425-882-8080" }
    },
    new {
        Name = "Bob Harris",
        PhoneNumbers = new[] { "650-555-0199" }
    }
};

Выражения создания делегатовDelegate creation expressions

Delegate_creation_expression используется для создания нового экземпляра delegate_type.A delegate_creation_expression is used to create a new instance of a delegate_type.

delegate_creation_expression
    : 'new' delegate_type '(' expression ')'
    ;

Аргументом выражения создания делегата должна быть группа методов, анонимная функция или значение типа времени компиляции dynamic или delegate_type.The argument of a delegate creation expression must be a method group, an anonymous function or a value of either the compile time type dynamic or a delegate_type. Если аргумент является группой методов, он идентифицирует метод и, для метода экземпляра, объект, для которого создается делегат.If the argument is a method group, it identifies the method and, for an instance method, the object for which to create a delegate. Если аргумент является анонимной функцией, он непосредственно определяет параметры и тело метода целевого объекта делегата.If the argument is an anonymous function it directly defines the parameters and method body of the delegate target. Если аргумент является значением, он определяет экземпляр делегата, для которого создается копия.If the argument is a value it identifies a delegate instance of which to create a copy.

Если выражение имеет тип времени компиляции dynamic, то delegate_creation_expression является динамически привязанным (динамической привязкой), а приведенные ниже правила применяются во время выполнения с использованием типа выраженияво время выполнения.If the expression has the compile-time type dynamic, the delegate_creation_expression is dynamically bound (Dynamic binding), and the rules below are applied at run-time using the run-time type of the expression. В противном случае правила применяются во время компиляции.Otherwise the rules are applied at compile-time.

Обработка delegate_creation_expression на этапе привязки формы new D(E), где D является delegate_type , а Eвыражением, состоит из следующих шагов:The binding-time processing of a delegate_creation_expression of the form new D(E), where D is a delegate_type and E is an expression, consists of the following steps:

  • Если E является группой методов, выражение создания делегата обрабатывается так же, как преобразование группы методов (преобразования групп методов) из E в D.If E is a method group, the delegate creation expression is processed in the same way as a method group conversion (Method group conversions) from E to D.
  • Если E является анонимной функцией, выражение создания делегата обрабатывается таким же образом, как и преобразование анонимных функций (преобразования анонимных функций) из E в D.If E is an anonymous function, the delegate creation expression is processed in the same way as an anonymous function conversion (Anonymous function conversions) from E to D.
  • Если E является значением, E должно быть совместимо (объявления делегата) с D, а результатом является ссылка на только что созданный делегат типа D, который ссылается на тот же список вызовов, что и E.If E is a value, E must be compatible (Delegate declarations) with D, and the result is a reference to a newly created delegate of type D that refers to the same invocation list as E. Если E несовместим с D, возникает ошибка времени компиляции.If E is not compatible with D, a compile-time error occurs.

Обработка delegate_creation_expression @no__t формы во время выполнения, где D является delegate_type , а Eвыражение, состоит из следующих шагов:The run-time processing of a delegate_creation_expression of the form new D(E), where D is a delegate_type and E is an expression, consists of the following steps:

  • Если E является группой методов, выражение создания делегата вычисляется как преобразование группы методов (преобразования групп методов) из E в D.If E is a method group, the delegate creation expression is evaluated as a method group conversion (Method group conversions) from E to D.
  • Если E является анонимной функцией, создание делегата вычисляется как преобразование анонимной функции из E в D (преобразования анонимных функций).If E is an anonymous function, the delegate creation is evaluated as an anonymous function conversion from E to D (Anonymous function conversions).
  • Если E является значением delegate_type:If E is a value of a delegate_type:
    • E вычисляется.E is evaluated. Если эта оценка вызывает исключение, дальнейшие действия не выполняются.If this evaluation causes an exception, no further steps are executed.
    • Если значение E равно null, выдается System.NullReferenceException и дальнейшие действия не выполняются.If the value of E is null, a System.NullReferenceException is thrown and no further steps are executed.
    • Выделен новый экземпляр типа делегата D.A new instance of the delegate type D is allocated. Если недостаточно памяти для выделения нового экземпляра, выдается System.OutOfMemoryException и дальнейшие действия не выполняются.If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
    • Новый экземпляр делегата инициализируется с тем же списком вызовов, что и экземпляр делегата, заданный E.The new delegate instance is initialized with the same invocation list as the delegate instance given by E.

Список вызовов делегата определяется при создании экземпляра делегата, а затем остается постоянным для всего времени существования делегата.The invocation list of a delegate is determined when the delegate is instantiated and then remains constant for the entire lifetime of the delegate. Иными словами, невозможно изменить целевые вызываемые сущности делегата после его создания.In other words, it is not possible to change the target callable entities of a delegate once it has been created. Если два делегата объединены или один из них удаляется из другого (объявления делегата), то новые результаты делегата; содержимое существующего делегата не изменилось.When two delegates are combined or one is removed from another (Delegate declarations), a new delegate results; no existing delegate has its contents changed.

Невозможно создать делегат, который ссылается на свойство, индексатор, определяемый пользователем оператор, конструктор экземпляра, деструктор или статический конструктор.It is not possible to create a delegate that refers to a property, indexer, user-defined operator, instance constructor, destructor, or static constructor.

Как описано выше, при создании делегата из группы методов список формальных параметров и тип возвращаемого значения делегата определяют, какой из перегруженных методов следует выбрать.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

delegate double DoubleFunc(double x);

class A
{
    DoubleFunc f = new DoubleFunc(Square);

    static float Square(float x) {
        return x * x;
    }

    static double Square(double x) {
        return x * x;
    }
}

поле A.f инициализируется делегатом, который ссылается на второй метод Square, так как этот метод точно соответствует списку формальных параметров и возвращаемому типу DoubleFunc.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. Если второй метод Square отсутствует, произошла ошибка времени компиляции.Had the second Square method not been present, a compile-time error would have occurred.

Выражения создания анонимных объектовAnonymous object creation expressions

Anonymous_object_creation_expression используется для создания объекта анонимного типа.An anonymous_object_creation_expression is used to create an object of an anonymous type.

anonymous_object_creation_expression
    : 'new' anonymous_object_initializer
    ;

anonymous_object_initializer
    : '{' member_declarator_list? '}'
    | '{' member_declarator_list ',' '}'
    ;

member_declarator_list
    : member_declarator (',' member_declarator)*
    ;

member_declarator
    : simple_name
    | member_access
    | base_access
    | null_conditional_member_access
    | identifier '=' expression
    ;

Инициализатор анонимного объекта объявляет анонимный тип и возвращает экземпляр этого типа.An anonymous object initializer declares an anonymous type and returns an instance of that type. Анонимный тип — это тип класса без имени, который наследуется непосредственно от object.An anonymous type is a nameless class type that inherits directly from object. Члены анонимного типа являются последовательностью свойств только для чтения, выводимых из инициализатора анонимного объекта, используемого для создания экземпляра типа.The members of an anonymous type are a sequence of read-only properties inferred from the anonymous object initializer used to create an instance of the type. В частности, инициализатор анонимного объекта в формеSpecifically, an anonymous object initializer of the form

new { p1 = e1, p2 = e2, ..., pn = en }

объявляет анонимный тип формыdeclares an anonymous type of the form

class __Anonymous1
{
    private readonly T1 f1;
    private readonly T2 f2;
    ...
    private readonly Tn fn;

    public __Anonymous1(T1 a1, T2 a2, ..., Tn an) {
        f1 = a1;
        f2 = a2;
        ...
        fn = an;
    }

    public T1 p1 { get { return f1; } }
    public T2 p2 { get { return f2; } }
    ...
    public Tn pn { get { return fn; } }

    public override bool Equals(object __o) { ... }
    public override int GetHashCode() { ... }
}

где каждый Tx является типом соответствующего выражения ex.where each Tx is the type of the corresponding expression ex. Выражение, используемое в member_declarator , должно иметь тип.The expression used in a member_declarator must have a type. Таким является ошибка времени компиляции, если выражение в member_declarator имеет значение null или является анонимной функцией.Thus, it is a compile-time error for an expression in a member_declarator to be null or an anonymous function. Также возникает ошибка времени компиляции, если выражение имеет ненадежный тип.It is also a compile-time error for the expression to have an unsafe type.

Имена анонимного типа и параметра для его метода Equals автоматически создаются компилятором и на него нельзя ссылаться в тексте программы.The names of an anonymous type and of the parameter to its Equals method are automatically generated by the compiler and cannot be referenced in program text.

В той же программе два инициализатора анонимных объектов, которые задают последовательность свойств одних и тех же имен и типы времени компиляции в том же порядке, будут создавать экземпляры одного и того же анонимного типа.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.

В примереIn the example

var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

назначение в последней строке разрешено, так как p1 и p2 имеют один и тот же анонимный тип.the assignment on the last line is permitted because p1 and p2 are of the same anonymous type.

Методы Equals и GetHashcode для анонимных типов переопределяют методы, унаследованные от object, и определяются в терминах Equals и GetHashcode свойств, чтобы два экземпляра одного и того же анонимного типа были равны только в том случае, если все их свойства равномерно.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.

Декларатор члена можно сократить до простого имени (вывод типа), доступа к элементу (Проверка разрешения динамической перегрузки), базовый доступ (базовый доступ) или член с условным доступом null ( Условные выражения NULL в качестве инициализаторов проекций).A member declarator can be abbreviated to a simple name (Type inference), a member access (Compile-time checking of dynamic overload resolution), a base access (Base access) or a null-conditional member access (Null-conditional expressions as projection initializers). Это называется инициализатором проекции и является сокращением для объявления и присваивания свойству с тем же именем.This is called a projection initializer and is shorthand for a declaration of and assignment to a property with the same name. В частности, деклараторы членов формSpecifically, member declarators of the forms

identifier
expr.identifier

в точности эквивалентны следующему, соответственно:are precisely equivalent to the following, respectively:

identifier = identifier
identifier = expr.identifier

Таким же, в инициализаторе проекции идентификатор выбирает и значение, и поле или свойство, которым присваивается значение.Thus, in a projection initializer the identifier selects both the value and the field or property to which the value is assigned. Интуитивно, проект инициализатора проекции не только является значением, но и именем значения.Intuitively, a projection initializer projects not just a value, but also the name of the value.

Оператор typeofThe typeof operator

Оператор typeof используется для получения объекта System.Type для типа.The typeof operator is used to obtain the System.Type object for a type.

typeof_expression
    : 'typeof' '(' type ')'
    | 'typeof' '(' unbound_type_name ')'
    | 'typeof' '(' 'void' ')'
    ;

unbound_type_name
    : identifier generic_dimension_specifier?
    | identifier '::' identifier generic_dimension_specifier?
    | unbound_type_name '.' identifier generic_dimension_specifier?
    ;

generic_dimension_specifier
    : '<' comma* '>'
    ;

comma
    : ','
    ;

Первая форма typeof_expression состоит из ключевого слова typeof, за которым следует тип, заключенный в круглые скобки.The first form of typeof_expression consists of a typeof keyword followed by a parenthesized type. Результатом выражения этой формы является объект System.Type для указанного типа.The result of an expression of this form is the System.Type object for the indicated type. Для любого заданного типа существует только один объект System.Type.There is only one System.Type object for any given type. Это означает, что для типа @ no__t-0 typeof(T) == typeof(T) всегда имеет значение true.This means that for a type T, typeof(T) == typeof(T) is always true. Тип не может быть dynamic.The type cannot be dynamic.

Вторая форма typeof_expression состоит из ключевого слова typeof, за которым следует unbound_type_nameв круглых скобках.The second form of typeof_expression consists of a typeof keyword followed by a parenthesized unbound_type_name. Unbound_type_name очень похож на TYPE_NAME (имена пространств имен и типов), за исключением того, что unbound_type_name содержит generic_dimension_specifiers, где TYPE_NAME содержит TYPE_ argument_lists.An unbound_type_name is very similar to a type_name (Namespace and type names) except that an unbound_type_name contains generic_dimension_specifiers where a type_name contains type_argument_lists. Если операнд typeof_expression представляет собой последовательность токенов, которая удовлетворяет грамматикам как unbound_type_name , так и TYPE_NAME, а именно, если она не содержит ни generic_dimension_specifier , ни type_argument _list, последовательность токенов считается TYPE_NAME.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. Значение unbound_type_name определяется следующим образом:The meaning of an unbound_type_name is determined as follows:

  • Преобразуйте последовательность токенов в TYPE_NAME , заменив каждый generic_dimension_specifier на type_argument_list с одинаковым количеством запятых и ключевым словом object как каждое 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.
  • Оцените результирующую TYPE_NAME, игнорируя все ограничения параметров типа.Evaluate the resulting type_name, while ignoring all type parameter constraints.
  • Unbound_type_name разрешается в непривязанный универсальный тип, связанный с результирующим сконструированным типом (привязанные и несвязанные типы).The unbound_type_name resolves to the unbound generic type associated with the resulting constructed type (Bound and unbound types).

Результатом typeof_expression является объект System.Type для результирующего несвязанного универсального типа.The result of the typeof_expression is the System.Type object for the resulting unbound generic type.

Третья форма typeof_expression состоит из ключевого слова typeof, за которым следует ключевое слово void в круглых скобках.The third form of typeof_expression consists of a typeof keyword followed by a parenthesized void keyword. Результатом выражения этой формы является объект System.Type, который представляет отсутствие типа.The result of an expression of this form is the System.Type object that represents the absence of a type. Объект Type, возвращаемый typeof(void), отличается от объекта типа, возвращаемого для любого типа.The type object returned by typeof(void) is distinct from the type object returned for any type. Этот специальный объект типа полезен в библиотеках классов, которые допускают отражение на методы на языке, где эти методы должны иметь способ представления возвращаемого типа любого метода, включая методы void, с экземпляром System.Type.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.

Оператор typeof можно использовать для параметра типа.The typeof operator can be used on a type parameter. Результатом является объект System.Type для типа времени выполнения, привязанного к параметру типа.The result is the System.Type object for the run-time type that was bound to the type parameter. Оператор typeof также можно использовать для сконструированного типа или непривязанного универсального типа (привязанных и непривязанных типов).The typeof operator can also be used on a constructed type or an unbound generic type (Bound and unbound types). Объект System.Type для несвязанного универсального типа не совпадает с объектом System.Type типа экземпляра.The System.Type object for an unbound generic type is not the same as the System.Type object of the instance type. Тип экземпляра всегда является закрытым сконструированным типом во время выполнения, поэтому его System.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, while the unbound generic type has no type arguments.

ПримерThe example

using System;

class X<T>
{
    public static void PrintTypes() {
        Type[] t = {
            typeof(int),
            typeof(System.Int32),
            typeof(string),
            typeof(double[]),
            typeof(void),
            typeof(T),
            typeof(X<T>),
            typeof(X<X<T>>),
            typeof(X<>)
        };
        for (int i = 0; i < t.Length; i++) {
            Console.WriteLine(t[i]);
        }
    }
}

class Test
{
    static void Main() {
        X<int>.PrintTypes();
    }
}

создает следующие выходные данные:produces the following output:

System.Int32
System.Int32
System.String
System.Double[]
System.Void
System.Int32
X`1[System.Int32]
X`1[X`1[System.Int32]]
X`1[T]

Обратите внимание, что int и System.Int32 имеют один и тот же тип.Note that int and System.Int32 are the same type.

Также обратите внимание, что результат typeof(X<>) не зависит от аргумента типа, но результат typeof(X<T>) имеет значение.Also note that the result of typeof(X<>) does not depend on the type argument but the result of typeof(X<T>) does.

Операторы checked и uncheckedThe checked and unchecked operators

Операторы checked и unchecked используются для управления контекстом проверки переполнения для арифметических операций и преобразований целочисленного типа.The checked and unchecked operators are used to control the overflow checking context for integral-type arithmetic operations and conversions.

checked_expression
    : 'checked' '(' expression ')'
    ;

unchecked_expression
    : 'unchecked' '(' expression ')'
    ;

Оператор checked вычисляет содержащееся выражение в проверяемом контексте, а оператор unchecked вычисляет содержащееся выражение в непроверяемом контексте.The checked operator evaluates the contained expression in a checked context, and the unchecked operator evaluates the contained expression in an unchecked context. Checked_expression или unchecked_expression точно соответствует parenthesized_expression (выражения, заключенные в скобки), за исключением того, что содержащееся выражение вычисляется в заданном контексте проверки переполнения. .A checked_expression or unchecked_expression corresponds exactly to a parenthesized_expression (Parenthesized expressions), except that the contained expression is evaluated in the given overflow checking context.

Контекст проверки переполнения также можно контролировать с помощью инструкций checked и unchecked (Операторы checked и unchecked).The overflow checking context can also be controlled through the checked and unchecked statements (The checked and unchecked statements).

Контекст проверки переполнения, установленный с помощью операторов и инструкций checked и unchecked, влияет на следующие операции:The following operations are affected by the overflow checking context established by the checked and unchecked operators and statements:

Когда одна из указанных выше операций создает результат, который слишком велик для представления в целевом типе, контекст, в котором выполняется операция, управляет полученным поведением:When one of the above operations produce a result that is too large to represent in the destination type, the context in which the operation is performed controls the resulting behavior:

  • В контексте checked, если операция является константным выражением (константными выражениями), возникает ошибка времени компиляции.In a checked context, if the operation is a constant expression (Constant expressions), a compile-time error occurs. В противном случае при выполнении операции во время выполнения выдается исключение System.OverflowException.Otherwise, when the operation is performed at run-time, a System.OverflowException is thrown.
  • В контексте unchecked результат усекается путем удаления всех старших битов, не попадающие в целевой тип.In an unchecked context, the result is truncated by discarding any high-order bits that do not fit in the destination type.

Для неконстантных выражений (выражений, вычисляемых во время выполнения), которые не заключены в операторы или операторы checked или unchecked, контекст проверки переполнения по умолчанию имеет значение unchecked, если только внешние факторы (такие как параметры компилятора и конфигурация среды выполнения) вызов для оценки checked.For non-constant expressions (expressions that are evaluated at run-time) that are not enclosed by any checked or unchecked operators or statements, the default overflow checking context is unchecked unless external factors (such as compiler switches and execution environment configuration) call for checked evaluation.

Для константных выражений (выражений, которые могут быть полностью вычислены во время компиляции) контекст проверки переполнения по умолчанию всегда checked.For constant expressions (expressions that can be fully evaluated at compile-time), the default overflow checking context is always checked. Если константное выражение явно не помещено в контекст unchecked, переполнения, происходящие во время вычисления выражения, всегда вызывают ошибки во время компиляции.Unless a constant expression is explicitly placed in an unchecked context, overflows that occur during the compile-time evaluation of the expression always cause compile-time errors.

На тело анонимной функции не влияет checked или unchecked контексты, в которых выполняется анонимная функция.The body of an anonymous function is not affected by checked or unchecked contexts in which the anonymous function occurs.

В примереIn the example

class Test
{
    static readonly int x = 1000000;
    static readonly int y = 1000000;

    static int F() {
        return checked(x * y);      // Throws OverflowException
    }

    static int G() {
        return unchecked(x * y);    // Returns -727379968
    }

    static int H() {
        return x * y;               // Depends on default
    }
}

ошибки времени компиляции не выводятся, так как ни одно из выражений не может быть вычислено во время компиляции.no compile-time errors are reported since neither of the expressions can be evaluated at compile-time. Во время выполнения метод F выдает System.OverflowException, а метод G Возвращает-727379968 (более низкие 32 бит выходного результата).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). Поведение метода H зависит от контекста проверки переполнения по умолчанию для компиляции, но это то же самое, что F или так же, как G.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.

В примереIn the example

class Test
{
    const int x = 1000000;
    const int y = 1000000;

    static int F() {
        return checked(x * y);      // Compile error, overflow
    }

    static int G() {
        return unchecked(x * y);    // Returns -727379968
    }

    static int H() {
        return x * y;               // Compile error, overflow
    }
}

переполнения, возникающие при вычислении константных выражений в F и H, вызывают ошибки во время компиляции, так как выражения оцениваются в контексте checked.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. Переполнение также возникает при вычислении константного выражения в G, но поскольку вычисление происходит в контексте unchecked, то переполнение не сообщается.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.

Операторы checked и unchecked влияют только на контекст проверки переполнения для операций, которые в текстовом виде содержатся в маркерах "(" и ")".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. В примереIn the example

class Test
{
    static int Multiply(int x, int y) {
        return x * y;
    }

    static int F() {
        return checked(Multiply(1000000, 1000000));
    }
}

Использование checked в F не влияет на оценку x * y в Multiply, поэтому в контексте проверки переполнения по умолчанию вычисляется x * y.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.

Оператор unchecked удобно использовать при записи констант целочисленных типов со знаком в шестнадцатеричном формате.The unchecked operator is convenient when writing constants of the signed integral types in hexadecimal notation. Пример:For example:

class Test
{
    public const int AllBits = unchecked((int)0xFFFFFFFF);

    public const int HighBit = unchecked((int)0x80000000);
}

Обе шестнадцатеричные константы выше имеют тип uint.Both of the hexadecimal constants above are of type uint. Поскольку константы выходят за пределы диапазона int, без оператора unchecked, приведения к int выдают ошибки времени компиляции.Because the constants are outside the int range, without the unchecked operator, the casts to int would produce compile-time errors.

Операторы checked и unchecked позволяют программистам управлять определенными аспектами некоторых числовых вычислений.The checked and unchecked operators and statements allow programmers to control certain aspects of some numeric calculations. Однако поведение некоторых числовых операторов зависит от их типов данных операндов.However, the behavior of some numeric operators depends on their operands' data types. Например, умножение двух десятичных знаков всегда приводит к возникновению исключения в случае переполнения даже в явной конструкции unchecked.For example, multiplying two decimals always results in an exception on overflow even within an explicitly unchecked construct. Аналогично, умножение двух чисел с плавающей запятой никогда не приводит к возникновению исключения в случае переполнения даже в явной конструкции checked.Similarly, multiplying two floats never results in an exception on overflow even within an explicitly checked construct. Кроме того, другие операторы никогда не затрагиваются режимом проверки, является ли по умолчанию или явным.In addition, other operators are never affected by the mode of checking, whether default or explicit.

Выражения значений по умолчаниюDefault value expressions

Выражение значения по умолчанию используется для получения значения по умолчанию (значений по умолчанию) для типа.A default value expression is used to obtain the default value (Default values) of a type. Обычно для параметров типа используется выражение значения по умолчанию, так как оно может быть неизвестным, если параметр типа является типом значения или ссылочным типом.Typically a default value expression is used for type parameters, since it may not be known if the type parameter is a value type or a reference type. (Преобразование из литерала null в параметр типа не существует, если не известно, что параметр типа является ссылочным типом.)(No conversion exists from the null literal to a type parameter unless the type parameter is known to be a reference type.)

default_value_expression
    : 'default' '(' type ')'
    ;

Если тип в default_value_expression вычисляется во время выполнения на ссылочный тип, то результатом будет null, преобразованный в этот тип.If the type in a default_value_expression evaluates at run-time to a reference type, the result is null converted to that type. Если тип в default_value_expression вычисляется во время выполнения до типа значения, результатом является значение value_typeпо умолчанию (конструкторы по умолчанию).If the type in a default_value_expression evaluates at run-time to a value type, the result is the value_type's default value (Default constructors).

Default_value_expression — это константное выражение (константные выражения), если тип является ссылочным типом или параметр типа, известный как ссылочный тип (ограничения параметра типа).A default_value_expression is a constant expression (Constant expressions) if the type is a reference type or a type parameter that is known to be a reference type (Type parameter constraints). Кроме того, default_value_expression является константным выражением, если тип является одним из следующих типов значений: sbyte, byte, short, ushort, int, uint, long, 0, 1, 2, @no__t – 13 или любой тип перечисления.In addition, a default_value_expression is a constant expression if the type is one of the following value types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, or any enumeration type.

Выражения NameOfNameof expressions

Nameof_expression используется для получения имени сущности программы в виде константной строки.A nameof_expression is used to obtain the name of a program entity as a constant string.

nameof_expression
    : 'nameof' '(' named_entity ')'
    ;

named_entity
    : simple_name
    | named_entity_target '.' identifier type_argument_list?
    ;

named_entity_target
    : 'this'
    | 'base'
    | named_entity 
    | predefined_type 
    | qualified_alias_member
    ;

Грамматически говоря, операнд named_entity всегда является выражением.Grammatically speaking, the named_entity operand is always an expression. Поскольку nameof не является зарезервированным ключевым словом, выражение NameOf всегда является синтаксически неоднозначным с вызовом простого имени nameof.Because nameof is not a reserved keyword, a nameof expression is always syntactically ambiguous with an invocation of the simple name nameof. В целях обеспечения совместимости, если поиск имени (простые имена) в имени nameof выполнен, выражение обрабатывается как invocation_expression , независимо от того, является ли вызов допустимым.For compatibility reasons, if a name lookup (Simple names) of the name nameof succeeds, the expression is treated as an invocation_expression -- regardless of whether the invocation is legal. В противном случае это nameof_expression.Otherwise it is a nameof_expression.

Значение параметра named_entity nameof_expression — это значение в качестве выражения. то есть как simple_name, base_access или member_access.The meaning of the named_entity of a nameof_expression is the meaning of it as an expression; that is, either as a simple_name, a base_access or a member_access. Однако, если Уточняющий запрос, описанный в разделе простые имена и доступ к члену , приводит к ошибке, поскольку член экземпляра был обнаружен в статическом контексте, nameof_expression не создает такую ошибку.However, where the lookup described in Simple names and Member access results in an error because an instance member was found in a static context, a nameof_expression produces no such error.

Ошибка времени компиляции для named_entity , указывающая группе методов на type_argument_list.It is a compile-time error for a named_entity designating a method group to have a type_argument_list. Это ошибка времени компиляции, когда named_entity_target имеет тип dynamic.It is a compile time error for a named_entity_target to have the type dynamic.

Nameof_expression является константным выражением типа string и не влияет на время выполнения.A nameof_expression is a constant expression of type string, and has no effect at runtime. В частности, его named_entity не вычисляется и игнорируется в целях анализа определенного назначения (Общие правила для простых выражений).Specifically, its named_entity is not evaluated, and is ignored for the purposes of definite assignment analysis (General rules for simple expressions). Его значение — это последний идентификатор named_entity перед необязательным окончательным type_argument_list, преобразованный следующим образом:Its value is the last identifier of the named_entity before the optional final type_argument_list, transformed in the following way:

  • Префикс "@", если он используется, удаляется.The prefix "@", if used, is removed.
  • Каждый unicode_escape_sequence преобразуется в соответствующий символ Юникода.Each unicode_escape_sequence is transformed into its corresponding Unicode character.
  • Удаляются все formatting_characters .Any formatting_characters are removed.

Это те же преобразования, которые применяются в идентификаторах при проверке равенства между идентификаторами.These are the same transformations applied in Identifiers when testing equality between identifiers.

TODO: примерыTODO: examples

Выражения анонимных методовAnonymous method expressions

Anonymous_method_expression — это один из двух способов определения анонимной функции.An anonymous_method_expression is one of two ways of defining an anonymous function. Они описаны далее в разделе выражения анонимных функций.These are further described in Anonymous function expressions.

Унарные операторыUnary operators

Операторы ?, +, -, !, ~, ++, @no__t – 6, CAST и await называются унарными операторами.The ?, +, -, !, ~, ++, --, cast, and await operators are called the unary operators.

unary_expression
    : primary_expression
    | null_conditional_expression
    | '+' unary_expression
    | '-' unary_expression
    | '!' unary_expression
    | '~' unary_expression
    | pre_increment_expression
    | pre_decrement_expression
    | cast_expression
    | await_expression
    | unary_expression_unsafe
    ;

Если операнд unary_expression имеет тип времени компиляции dynamic, он динамически привязан (Динамическая привязка).If the operand of a unary_expression has the compile-time type dynamic, it is dynamically bound (Dynamic binding). В этом случае тип времени компиляции unary_expressiondynamic, а разрешение, описанное ниже, будет происходить во время выполнения с использованием типа операнда во время выполнения.In this case the compile-time type of the unary_expression is dynamic, and the resolution described below will take place at run-time using the run-time type of the operand.

Условный оператор nullNull-conditional operator

Условный оператор NULL применяет к операнду список операций, только если этот операнд не равен null.The null-conditional operator applies a list of operations to its operand only if that operand is non-null. В противном случае результат применения оператора — null.Otherwise the result of applying the operator is null.

null_conditional_expression
    : primary_expression null_conditional_operations
    ;

null_conditional_operations
    : null_conditional_operations? '?' '.' identifier type_argument_list?
    | null_conditional_operations? '?' '[' argument_list ']'
    | null_conditional_operations '.' identifier type_argument_list?
    | null_conditional_operations '[' argument_list ']'
    | null_conditional_operations '(' argument_list? ')'
    ;

Список операций может включать операции доступа к членам и доступа к элементам (которые могут сами по себе иметь условные значения NULL), а также вызов.The list of operations can include member access and element access operations (which may themselves be null-conditional), as well as invocation.

Например, выражение a.b?[0]?.c() — это null_conditional_expression с primary_expression a.b и null_conditional_operations ?[0] (доступ к условным элементам со значением NULL), ?.c (NULL — условный член доступ) и () (вызов).For example, the expression a.b?[0]?.c() is a null_conditional_expression with a primary_expression a.b and null_conditional_operations ?[0] (null-conditional element access), ?.c (null-conditional member access) and () (invocation).

Для null_conditional_expression E с primary_expression P, позвольте E0 быть выражением, полученным с помощью текстового удаления первого ? из null_conditional_operations E, Он есть.For a null_conditional_expression E with a primary_expression P, let E0 be the expression obtained by textually removing the leading ? from each of the null_conditional_operations of E that have one. По сути, E0 — это выражение, которое будет вычисляться, если ни одна из проверок null, представленных в ?S, не находит null.Conceptually, E0 is the expression that will be evaluated if none of the null checks represented by the ?s do find a null.

Кроме того, позвольте E1 быть выражением, полученным с помощью текстового удаления начального ? из первого из null_conditional_operations в E.Also, let E1 be the expression obtained by textually removing the leading ? from just the first of the null_conditional_operations in E. Это может привести к появлению первичного выражения (если было только одно ?) или на другой null_conditional_expression.This may lead to a primary-expression (if there was just one ?) or to another null_conditional_expression.

Например, если E является выражением a.b?[0]?.c(), то E0 — выражение a.b[0].c(), а E1 — выражение a.b[0]?.c().For example, if E is the expression a.b?[0]?.c(), then E0 is the expression a.b[0].c() and E1 is the expression a.b[0]?.c().

Если E0 классифицируется как Nothing, то E классифицируется как Nothing.If E0 is classified as nothing, then E is classified as nothing. В противном случае E классифицируется как значение.Otherwise E is classified as a value.

E0 и E1 используются для определения значения E:E0 and E1 are used to determine the meaning of E:

  • Если E происходит как statement_expression , значение E совпадает с операторомIf E occurs as a statement_expression the meaning of E is the same as the statement

    if ((object)P != null) E1;
    

    за исключением того, что P вычисляется только один раз.except that P is evaluated only once.

  • В противном случае, если E0 классифицируется как Nothing, возникает ошибка времени компиляции.Otherwise, if E0 is classified as nothing a compile-time error occurs.

  • В противном случае параметр Let T0 будет иметь тип E0.Otherwise, let T0 be the type of E0.

    • Если T0 является параметром типа, который не является ссылочным типом или типом значения, не допускающим значения NULL, возникает ошибка времени компиляции.If T0 is a type parameter that is not known to be a reference type or a non-nullable value type, a compile-time error occurs.

    • Если T0 является типом значения, не допускающим значения NULL, типом E является T0?, а значение E совпадает сIf T0 is a non-nullable value type, then the type of E is T0?, and the meaning of E is the same as

      ((object)P == null) ? (T0?)null : E1
      

      за исключением того, что P вычисляется только один раз.except that P is evaluated only once.

    • В противном случае тип E — T0, а значение E — то же, что иOtherwise the type of E is T0, and the meaning of E is the same as

      ((object)P == null) ? null : E1
      

      за исключением того, что P вычисляется только один раз.except that P is evaluated only once.

Если E1 является null_conditional_expression, то эти правила применяются снова, вложение тестов для null до тех пор, пока не будет больше ?, а выражение было уменьшено до основного выражения E0.If E1 is itself a null_conditional_expression, then these rules are applied again, nesting the tests for null until there are no further ?'s, and the expression has been reduced all the way down to the primary-expression E0.

Например, если выражение a.b?[0]?.c() встречается в качестве выражения инструкции, как в инструкции:For example, if the expression a.b?[0]?.c() occurs as a statement-expression, as in the statement:

a.b?[0]?.c();

его значение эквивалентно следующему:its meaning is equivalent to:

if (a.b != null) a.b[0]?.c();

что опять же эквивалентно следующему:which again is equivalent to:

if (a.b != null) if (a.b[0] != null) a.b[0].c();

За исключением того, что a.b и a.b[0] вычисляются только один раз.Except that a.b and a.b[0] are evaluated only once.

Значение, если оно происходит в контексте, в котором используется его свойство, как в:If it occurs in a context where its value is used, as in:

var x = a.b?[0]?.c();

Если тип окончательного вызова не является типом значения, не допускающим значения NULL, его значение эквивалентно следующему:and assuming that the type of the final invocation is not a non-nullable value type, its meaning is equivalent to:

var x = (a.b == null) ? null : (a.b[0] == null) ? null : a.b[0].c();

за исключением того, что a.b и a.b[0] вычисляются только один раз.except that a.b and a.b[0] are evaluated only once.

Условные выражения со значением NULL в качестве инициализаторов проекцийNull-conditional expressions as projection initializers

Условное выражение, равное NULL, разрешено использовать в качестве member_declarator в anonymous_object_creation_expression (выражения создания анонимных объектов), если оно заканчивается с доступом к члену (при необходимости с условным значением NULL).A null-conditional expression is only allowed as a member_declarator in an anonymous_object_creation_expression (Anonymous object creation expressions) if it ends with an (optionally null-conditional) member access. Грамматически, это требование можно выразить следующим образом:Grammatically, this requirement can be expressed as:

null_conditional_member_access
    : primary_expression null_conditional_operations? '?' '.' identifier type_argument_list?
    | primary_expression null_conditional_operations '.' identifier type_argument_list?
    ;

Это особый случай использования грамматики для null_conditional_expression выше.This is a special case of the grammar for null_conditional_expression above. Рабочий процесс для member_declarator в выражениях создания анонимных объектов включает только null_conditional_member_access.The production for member_declarator in Anonymous object creation expressions then includes only null_conditional_member_access.

Условные выражения NULL в качестве выражений инструкцийNull-conditional expressions as statement expressions

Условное выражение, равное NULL, допускается только в качестве statement_expression (операторов выражения), если оно заканчивается вызовом.A null-conditional expression is only allowed as a statement_expression (Expression statements) if it ends with an invocation. Грамматически, это требование можно выразить следующим образом:Grammatically, this requirement can be expressed as:

null_conditional_invocation_expression
    : primary_expression null_conditional_operations '(' argument_list? ')'
    ;

Это особый случай использования грамматики для null_conditional_expression выше.This is a special case of the grammar for null_conditional_expression above. Затем Рабочая среда для statement_expression в инструкциях Expression включает только null_conditional_invocation_expression.The production for statement_expression in Expression statements then includes only null_conditional_invocation_expression.

Оператор унарного сложенияUnary plus operator

Для операции с формой +x, разрешение перегрузки унарного оператора (разрешение перегрузки унарного оператора) применяется для выбора конкретной реализации оператора.For an operation of the form +x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. Операнд преобразуется в тип параметра выбранного оператора, а тип результата — на тип возвращаемого значения оператора.The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Предопределенные унарные операторы сложения:The predefined unary plus operators are:

int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);

Для каждого из этих операторов результатом является просто значение операнда.For each of these operators, the result is simply the value of the operand.

Оператор унарного минусаUnary minus operator

Для операции с формой -x, разрешение перегрузки унарного оператора (разрешение перегрузки унарного оператора) применяется для выбора конкретной реализации оператора.For an operation of the form -x, unary operator overload resolution (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 negation operators are:

  • Отрицание целых чисел:Integer negation:

    int operator -(int x);
    long operator -(long x);
    

    Результат вычисляется путем вычитания x из нуля.The result is computed by subtracting x from zero. Если значение x является наименьшим значением для типа операнда (-2 ^ 31 для int или-2 ^ 63 для long), то математическое отрицание x не может быть представлено в типе операнда.If the value of x is the smallest representable value of the operand type (-2^31 for int or -2^63 for long), then the mathematical negation of x is not representable within the operand type. Если это происходит в контексте checked, возникает исключение System.OverflowException; Если это происходит в контексте unchecked, результатом является значение операнда, а переполнение не сообщается.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.

    Если операнд оператора отрицания имеет тип uint, он преобразуется в тип long, а тип результата — на long.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. Исключением является правило, которое позволяет записывать int value-2147483648 (-2 ^ 31) как десятичный целочисленный литерал (целочисленные литералы).An exception is the rule that permits the int value -2147483648 (-2^31) to be written as a decimal integer literal (Integer literals).

    Если операнд оператора отрицания имеет тип ulong, возникает ошибка времени компиляции.If the operand of the negation operator is of type ulong, a compile-time error occurs. Исключением является правило, которое позволяет записывать значения long – 9223372036854775808 (-2 ^ 63) в десятичный целочисленный литерал (целочисленные литералы).An exception is the rule that permits the long value -9223372036854775808 (-2^63) to be written as a decimal integer literal (Integer literals).

  • Отрицание чисел с плавающей запятой:Floating-point negation:

    float operator -(float x);
    double operator -(double x);
    

    Результатом является значение x с обратным знаком.The result is the value of x with its sign inverted. Если x равно NaN, результат также равен NaN.If x is NaN, the result is also NaN.

  • Отрицание десятичного числа:Decimal negation:

    decimal operator -(decimal x);
    

    Результат вычисляется путем вычитания x из нуля.The result is computed by subtracting x from zero. Десятичное отрицание эквивалентно использованию оператора унарного минуса типа System.Decimal.Decimal negation is equivalent to using the unary minus operator of type System.Decimal.

Оператор логического отрицания:Logical negation operator

Для операции с формой !x, разрешение перегрузки унарного оператора (разрешение перегрузки унарного оператора) применяется для выбора конкретной реализации оператора.For an operation of the form !x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. Операнд преобразуется в тип параметра выбранного оператора, а тип результата — на тип возвращаемого значения оператора.The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Существует только один предопределенный оператор логического отрицания:Only one predefined logical negation operator exists:

bool operator !(bool x);

Этот оператор вычислит логическое отрицание операнда: Если операнд имеет значение true, то результатом является false.This operator computes the logical negation of the operand: If the operand is true, the result is false. Если операнд имеет значение false, то результатом является true.If the operand is false, the result is true.

Оператор побитового дополненияBitwise complement operator

Для операции с формой ~x, разрешение перегрузки унарного оператора (разрешение перегрузки унарного оператора) применяется для выбора конкретной реализации оператора.For an operation of the form ~x, unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. Операнд преобразуется в тип параметра выбранного оператора, а тип результата — на тип возвращаемого значения оператора.The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Предопределенные операторы побитового дополнения:The predefined bitwise complement operators are:

int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);

Для каждого из этих операторов результатом операции является побитовое дополнение x.For each of these operators, the result of the operation is the bitwise complement of x.

Каждый тип перечисления E неявно предоставляет следующий оператор побитового дополнения:Every enumeration type E implicitly provides the following bitwise complement operator:

E operator ~(E x);

Результат вычисления ~x, где x является выражением типа перечисления E с базовым типом U, является точно таким же, как и при вычислении (E)(~(U)x), за исключением того, что преобразование в E всегда выполняется как в контексте unchecked ( Помеченные и непроверенные операторы).The result of evaluating ~x, where x is an expression of an enumeration type E with an underlying type U, is exactly the same as evaluating (E)(~(U)x), except that the conversion to E is always performed as if in an unchecked context (The checked and unchecked operators).

Префиксные операторы инкремента и декрементаPrefix increment and decrement operators

pre_increment_expression
    : '++' unary_expression
    ;

pre_decrement_expression
    : '--' unary_expression
    ;

Операндом операции инкремента или уменьшения префикса должно быть выражение, классифицированное как переменная, доступ к свойству или доступ к индексатору.The operand of a prefix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. Результатом операции является значение того же типа, что и у операнда.The result of the operation is a value of the same type as the operand.

Если операндом операции инкремента или уменьшения префикса является доступ к свойству или индексатору, свойство или индексатор должны иметь как get, так и метод доступа set.If the operand of a prefix increment or decrement operation is a property or indexer access, the property or indexer must have both a get and a set accessor. Если это не так, возникает ошибка времени привязки.If this is not the case, a binding-time error occurs.

Разрешение перегрузки унарного оператора (разрешение перегрузки унарного оператора) применяется для выбора конкретной реализации оператора.Unary operator overload resolution (Unary operator overload resolution) is applied to select a specific operator implementation. Стандартные операторы ++ и -- существуют для следующих типов: sbyte, byte, short, ushort, int, uint, long, ulong, 0, 1, 2, 3 и любого типа перечисления.Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. Предопределенные операторы ++ возвращают значение, полученное при добавлении 1 к операнду, и предопределенные операторы -- возвращают значение, полученное путем вычитания 1 из операнда.The predefined ++ operators return the value produced by adding 1 to the operand, and the predefined -- operators return the value produced by subtracting 1 from the operand. В контексте checked, если результат этого сложения или вычитания находится вне диапазона результирующего типа, а тип результата является целочисленным типом или типом перечисления, выдается System.OverflowException.In a checked context, if the result of this addition or subtraction is outside the range of the result type and the result type is an integral type or enum type, a System.OverflowException is thrown.

Обработка префиксной операции инкремента или декремента в форме ++x или --x состоит из следующих шагов:The run-time processing of a prefix increment or decrement operation of the form ++x or --x consists of the following steps:

  • Если x классифицируется как переменная:If x is classified as a variable:
    • x вычисляется для создания переменной.x is evaluated to produce the variable.
    • Выбранный оператор вызывается со значением x в качестве аргумента.The selected operator is invoked with the value of x as its argument.
    • Значение, возвращаемое оператором, хранится в расположении, заданном вычислением x.The value returned by the operator is stored in the location given by the evaluation of x.
    • Значение, возвращаемое оператором, станет результатом операции.The value returned by the operator becomes the result of the operation.
  • Если x классифицируется как доступ к свойству или индексатору:If x is classified as a property or indexer access:
    • Выражение экземпляра (если x не static) и список аргументов (если x — доступ к индексатору), связанный с x, вычисляется и результаты используются в последующих вызовах методов доступа get и set.The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent get and set accessor invocations.
    • Вызывается метод доступа get для x.The get accessor of x is invoked.
    • Выбранный оператор вызывается со значением, возвращенным методом доступа get в качестве аргумента.The selected operator is invoked with the value returned by the get accessor as its argument.
    • Метод доступа set для x вызывается со значением, возвращаемым оператором в качестве аргумента value.The set accessor of x is invoked with the value returned by the operator as its value argument.
    • Значение, возвращаемое оператором, станет результатом операции.The value returned by the operator becomes the result of the operation.

Операторы ++ и -- также поддерживают постфиксную нотацию (Постфиксные операторы инкремента и декремента).The ++ and -- operators also support postfix notation (Postfix increment and decrement operators). Как правило, результатом x++ или x-- является значение x перед операцией, в то время как результатом ++x или --x является значение x после операции.Typically, the result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. В любом случае x имеет то же значение, что и после операции.In either case, x itself has the same value after the operation.

Реализацию operator++ или operator-- можно вызвать с помощью постфиксной или префиксной нотации.An operator++ or operator-- implementation can be invoked using either postfix or prefix notation. Нельзя иметь отдельные реализации операторов для двух нотаций.It is not possible to have separate operator implementations for the two notations.

Выражения приведенияCast expressions

Cast_expression используется для явного преобразования выражения в заданный тип.A cast_expression is used to explicitly convert an expression to a given type.

cast_expression
    : '(' type ')' unary_expression
    ;

Cast_expression формы (T)E, где T — это тип , а Eunary_expression, выполняет явное преобразование (явное преобразование) значения E в тип T.A cast_expression of the form (T)E, where T is a type and E is a unary_expression, performs an explicit conversion (Explicit conversions) of the value of E to type T. Если не существует явного преобразования из E в T, возникает ошибка времени привязки.If no explicit conversion exists from E to T, a binding-time error occurs. В противном случае результатом является значение, созданное явным преобразованием.Otherwise, the result is the value produced by the explicit conversion. Результат всегда классифицируется как значение, даже если E обозначает переменную.The result is always classified as a value, even if E denotes a variable.

Грамматика cast_expression приводит к определенным синтаксическим неоднозначности.The grammar for a cast_expression leads to certain syntactic ambiguities. Например, выражение (x)-y может интерпретироваться как cast_expression (приведение -y к типу x) или как additive_expression в сочетании с parenthesized_expression (который вычислит значение x - y).For example, the expression (x)-y could either be interpreted as a cast_expression (a cast of -y to type x) or as an additive_expression combined with a parenthesized_expression (which computes the value x - y).

Для разрешения неоднозначностей cast_expression существует следующее правило: Последовательность из одного или нескольких токенов(пробелов), заключенных в круглые скобки, считается началом cast_expression , только если выполняется хотя бы одно из следующих условий.To resolve cast_expression ambiguities, the following rule exists: A sequence of one or more tokens (White space) enclosed in parentheses is considered the start of a cast_expression only if at least one of the following are true:

  • Последовательность токенов представляет собой правильную грамматику для типа, но не для выражения.The sequence of tokens is correct grammar for a type, but not for an expression.
  • Последовательность токенов является правильной грамматикой для типа, а токен, следующий сразу за закрывающей круглой скобкой, является маркером "~", маркером "!", токеном "(", идентификатором (escape-последовательностями символов Юникода ), литерал (литералы) или любое ключевое слово (Ключевые слова), за исключением 0 и 1.The sequence of tokens is correct grammar for a type, and the token immediately following the closing parentheses is the token "~", the token "!", the token "(", an identifier (Unicode character escape sequences), a literal (Literals), or any keyword (Keywords) except as and is.

Термин «правильная грамматика» выше означает, что последовательность токенов должна соответствовать конкретной грамматической рабочей среде.The term "correct grammar" above means only that the sequence of tokens must conform to the particular grammatical production. В частности, это не учитывает фактическое значение всех составляющих идентификаторов.It specifically does not consider the actual meaning of any constituent identifiers. Например, если x и y являются идентификаторами, то x.y — правильная грамматика для типа, даже если в x.y нет фактического обозначения типа.For example, if x and y are identifiers, then x.y is correct grammar for a type, even if x.y doesn't actually denote a type.

В правиле устранения неоднозначности следует, что если x и y являются идентификаторами, (x)y, (x)(y), а (x)(-y)cast_expressions, но (x)-y — нет, даже если x определяет тип.From the disambiguation rule it follows that, if x and y are identifiers, (x)y, (x)(y), and (x)(-y) are cast_expressions, but (x)-y is not, even if x identifies a type. Однако если x является ключевым словом, определяющим предопределенный тип (например, int), то все четыре формы являются cast_expressions (поскольку такое ключевое слово не может быть выражением само по себе).However, if x is a keyword that identifies a predefined type (such as int), then all four forms are cast_expressions (because such a keyword could not possibly be an expression by itself).

Выражения awaitAwait expressions

Оператор await используется для приостановки оценки включающей асинхронной функции до завершения асинхронной операции, представленной операндом.The await operator is used to suspend evaluation of the enclosing async function until the asynchronous operation represented by the operand has completed.

await_expression
    : 'await' unary_expression
    ;

Await_expression допускается только в теле асинхронной функции (итераторов).An await_expression is only allowed in the body of an async function (Iterators). В ближайшей внешней функции Async await_expression может не произойти в следующих местах:Within the nearest enclosing async function, an await_expression may not occur in these places:

  • Внутри вложенной (не асинхронной) анонимной функцииInside a nested (non-async) anonymous function
  • Внутри блока lock_statementInside the block of a lock_statement
  • В ненадежном контекстеIn an unsafe context

Обратите внимание, что await_expression не может встречаться в большинстве мест в query_expression, так как они синтаксически преобразуются для использования неасинхронных лямбда-выражений.Note that an await_expression cannot occur in most places within a query_expression, because those are syntactically transformed to use non-async lambda expressions.

Внутри асинхронной функции await нельзя использовать в качестве идентификатора.Inside of an async function, await cannot be used as an identifier. Следовательно, нет синтаксических неоднозначностей между выражениями await-Expression и различными выражениями, включающими идентификаторы.There is therefore no syntactic ambiguity between await-expressions and various expressions involving identifiers. За пределами асинхронных функций await выступает в качестве обычного идентификатора.Outside of async functions, await acts as a normal identifier.

Операнд await_expression называется задачей.The operand of an await_expression is called the task. Он представляет асинхронную операцию, которая может быть или не завершена во время оценки await_expression .It represents an asynchronous operation that may or may not be complete at the time the await_expression is evaluated. Назначение оператора await заключается в приостановке выполнения включающей асинхронной функции до завершения ожидаемой задачи и получения ее результата.The purpose of the await operator is to suspend execution of the enclosing async function until the awaited task is complete, and then obtain its outcome.

Выражения, ожидающие обработкиAwaitable expressions

Задача выражения await должна быть ожидающей.The task of an await expression is required to be awaitable. Выражение t может ожидать, если один из следующих содержит:An expression t is awaitable if one of the following holds:

  • t относится к типу времени компиляции dynamict is of compile time type dynamic
  • t имеет доступный экземпляр или метод расширения с именем GetAwaiter без параметров и параметров типа, а также тип возвращаемого значения A, для которого все приведенные ниже удержания:t has an accessible instance or extension method called GetAwaiter with no parameters and no type parameters, and a return type A for which all of the following hold:
    • A реализует интерфейс System.Runtime.CompilerServices.INotifyCompletion (далее известный как INotifyCompletion для краткости)A implements the interface System.Runtime.CompilerServices.INotifyCompletion (hereafter known as INotifyCompletion for brevity)
    • A имеет доступное, доступный для чтения свойство экземпляра IsCompleted типа boolA has an accessible, readable instance property IsCompleted of type bool
    • A имеет доступный метод экземпляра GetResult без параметров и параметров типа.A has an accessible instance method GetResult with no parameters and no type parameters

Назначение метода GetAwaiter заключается в получении ожидающего выполнения задачи.The purpose of the GetAwaiter method is to obtain an awaiter for the task. Тип A называется типом ожидаемого типа для выражения await.The type A is called the awaiter type for the await expression.

Свойство IsCompleted определяет, является ли задача уже завершенной.The purpose of the IsCompleted property is to determine if the task is already complete. В этом случае нет необходимости приостанавливать оценку.If so, there is no need to suspend evaluation.

Назначение метода INotifyCompletion.OnCompleted заключается в регистрации «продолжения» в задаче. т. е. делегат (типа System.Action), который будет вызываться после завершения задачи.The purpose of the INotifyCompletion.OnCompleted method is to sign up a "continuation" to the task; i.e. a delegate (of type System.Action) that will be invoked once the task is complete.

Назначение метода GetResult заключается в получении результата задачи после ее завершения.The purpose of the GetResult method is to obtain the outcome of the task once it is complete. Этот результат может быть выполнен успешно, возможно, с результирующим значением или исключением, вызываемым методом GetResult.This outcome may be successful completion, possibly with a result value, or it may be an exception which is thrown by the GetResult method.

Классификация выражений awaitClassification of await expressions

Выражение await t классифицируется так же, как выражение (t).GetAwaiter().GetResult().The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). Таким образом, если тип возвращаемого значения GetResultvoid, await_expression классифицируется как Nothing.Thus, if the return type of GetResult is void, the await_expression is classified as nothing. Если тип возвращаемого значения, отличного от void, T, то await_expression классифицируется как значение типа T.If it has a non-void return type T, the await_expression is classified as a value of type T.

Вычисление выражений await во время выполненияRuntime evaluation of await expressions

Во время выполнения выражение await t вычисляется следующим образом:At runtime, the expression await t is evaluated as follows:

  • Параметр await a получен путем вычисления выражения (t).GetAwaiter().An awaiter a is obtained by evaluating the expression (t).GetAwaiter().
  • @No__t-0 b получается путем вычисления выражения (a).IsCompleted.A bool b is obtained by evaluating the expression (a).IsCompleted.
  • Если b равно false, то вычисление зависит от того, реализует ли a интерфейс System.Runtime.CompilerServices.ICriticalNotifyCompletion (то есть ICriticalNotifyCompletion для краткости).If b is false then evaluation depends on whether a implements the interface System.Runtime.CompilerServices.ICriticalNotifyCompletion (hereafter known as ICriticalNotifyCompletion for brevity). Эта проверка выполняется во время привязки. т. е. в среде выполнения, если a имеет тип времени компиляции dynamic, а во время компиляции — в противном случае.This check is done at binding time; i.e. at runtime if a has the compile time type dynamic, and at compile time otherwise. Позвольте r обозначить делегат возобновления (итераторы):Let r denote the resumption delegate (Iterators):
    • Если a не реализует ICriticalNotifyCompletion, вычисляется выражение (a as (INotifyCompletion)).OnCompleted(r).If a does not implement ICriticalNotifyCompletion, then the expression (a as (INotifyCompletion)).OnCompleted(r) is evaluated.
    • Если a реализует ICriticalNotifyCompletion, вычисляется выражение (a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r).If a does implement ICriticalNotifyCompletion, then the expression (a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r) is evaluated.
    • После этого вычисление приостанавливается, а управление возвращается текущему вызывающему объекту асинхронной функции.Evaluation is then suspended, and control is returned to the current caller of the async function.
  • Либо сразу после (если b было true), либо при последующем вызове делегата возобновления (если b был false), вычисляется выражение (a).GetResult().Either immediately after (if b was true), or upon later invocation of the resumption delegate (if b was false), the expression (a).GetResult() is evaluated. Если он возвращает значение, это значение является результатом await_expression.If it returns a value, that value is the result of the await_expression. В противном случае результат равен Nothing.Otherwise the result is nothing.

Реализация методов интерфейса в методе INotifyCompletion.OnCompleted и ICriticalNotifyCompletion.UnsafeOnCompleted должна приводить к многократному вызову делегата r.An awaiter's implementation of the interface methods INotifyCompletion.OnCompleted and ICriticalNotifyCompletion.UnsafeOnCompleted should cause the delegate r to be invoked at most once. В противном случае поведение включающей асинхронной функции не определено.Otherwise, the behavior of the enclosing async function is undefined.

Арифметические операторыArithmetic operators

Операторы *, /, %, + и - называются арифметическими операторами.The *, /, %, +, and - operators are called the arithmetic operators.

multiplicative_expression
    : unary_expression
    | multiplicative_expression '*' unary_expression
    | multiplicative_expression '/' unary_expression
    | multiplicative_expression '%' unary_expression
    ;

additive_expression
    : multiplicative_expression
    | additive_expression '+' multiplicative_expression
    | additive_expression '-' multiplicative_expression
    ;

Если операнд арифметического оператора имеет тип времени компиляции dynamic, то выражение динамически привязано (Динамическая привязка).If an operand of an arithmetic operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). В этом случае типом выражения во время компиляции является dynamic, и описанное ниже разрешение будет выполняться во время выполнения с использованием типов времени выполнения этих операндов, которые имеют тип времени компиляции dynamic.In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

Оператор умноженияMultiplication operator

Для операции, выполняемой в форме x * y, для выбора конкретной реализации оператора применяется разрешение перегрузки бинарного оператора (разрешение перегрузки бинарного оператора).For an operation of the form x * y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Ниже перечислены предопределенные операторы умножения.The predefined multiplication operators are listed below. Все операторы вычисляют произведение x и y.The operators all compute the product of x and y.

  • Целочисленное умножение:Integer multiplication:

    int operator *(int x, int y);
    uint operator *(uint x, uint y);
    long operator *(long x, long y);
    ulong operator *(ulong x, ulong y);
    

    В контексте checked, если продукт находится за пределами диапазона результирующего типа, выдается System.OverflowException.In a checked context, if the product is outside the range of the result type, a System.OverflowException is thrown. В контексте unchecked сообщения о избыточности не передаются и все значимые старшие биты за пределами диапазона результирующего типа отбрасываются.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:

    float operator *(float x, float y);
    double operator *(double x, double y);
    

    Продукт вычисляются в соответствии с правилами арифметического IEEE 754.The product is computed according to the rules of IEEE 754 arithmetic. В следующей таблице перечислены результаты всех возможных сочетаний ненулевых конечных значений, нулей, бесконечности и NaN.The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. В таблице x и y являются положительными конечными значениями.In the table, x and y are positive finite values. z является результатом x * y.z is the result of x * y. Если результат слишком велик для целевого типа, z — бесконечность.If the result is too large for the destination type, z is infinity. Если результат слишком мал для целевого типа, z равно нулю.If the result is too small for the destination type, z is zero.

    + y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    + x+x + z+z – z-z +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    -x-x – z-z + z+z -0-0 +0+0 -inf-inf +inf+inf NaNNaN
    +0+0 +0+0 -0-0 +0+0 -0-0 NaNNaN NaNNaN NaNNaN
    -0-0 -0-0 +0+0 -0-0 +0+0 NaNNaN NaNNaN NaNNaN
    +inf+inf +inf+inf -inf-inf NaNNaN NaNNaN +inf+inf -inf-inf NaNNaN
    -inf-inf -inf-inf +inf+inf NaNNaN NaNNaN -inf-inf +inf+inf NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • Умножение десятичных значений:Decimal multiplication:

    decimal operator *(decimal x, decimal y);
    

    Если полученное значение слишком велико для представления в формате decimal, выдается System.OverflowException.If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. Если значение результата слишком мало для представления в формате decimal, результат равен нулю.If the result value is too small to represent in the decimal format, the result is zero. Масштаб результата до округления представляет собой сумму шкал двух операндов.The scale of the result, before any rounding, is the sum of the scales of the two operands.

    Десятичное умножение эквивалентно использованию оператора умножения типа System.Decimal.Decimal multiplication is equivalent to using the multiplication operator of type System.Decimal.

Оператор деленияDivision operator

Для операции, выполняемой в форме x / y, для выбора конкретной реализации оператора применяется разрешение перегрузки бинарного оператора (разрешение перегрузки бинарного оператора).For an operation of the form x / y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Ниже перечислены стандартные операторы деления.The predefined division operators are listed below. Все операторы вычисляют частное x и y.The operators all compute the quotient of x and y.

  • Целочисленное деление:Integer division:

    int operator /(int x, int y);
    uint operator /(uint x, uint y);
    long operator /(long x, long y);
    ulong operator /(ulong x, ulong y);
    

    Если значение правого операнда равно нулю, выдается System.DivideByZeroException.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.

    Если левый операнд является наименьшим значением int или long, а правый операнд — -1, возникает переполнение.If the left operand is the smallest representable int or long value and the right operand is -1, an overflow occurs. В контексте checked это приводит к возникновению System.ArithmeticException (или подкласса).In a checked context, this causes a System.ArithmeticException (or a subclass thereof) to be thrown. В контексте unchecked реализация определяется в зависимости от того, выдается ли System.ArithmeticException (или подкласс) или что переполнение не сообщается результирующему значению левого операнда.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:

    float operator /(float x, float y);
    double operator /(double x, double y);
    

    Частное вычисление производится в соответствии с правилами арифметического IEEE 754.The quotient is computed according to the rules of IEEE 754 arithmetic. В следующей таблице перечислены результаты всех возможных сочетаний ненулевых конечных значений, нулей, бесконечности и NaN.The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. В таблице x и y являются положительными конечными значениями.In the table, x and y are positive finite values. z является результатом x / y.z is the result of x / y. Если результат слишком велик для целевого типа, z — бесконечность.If the result is too large for the destination type, z is infinity. Если результат слишком мал для целевого типа, z равно нулю.If the result is too small for the destination type, z is zero.

    + y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    + x+x + z+z – z-z +inf+inf -inf-inf +0+0 -0-0 NaNNaN
    -x-x – z-z + z+z -inf-inf +inf+inf -0-0 +0+0 NaNNaN
    +0+0 +0+0 -0-0 NaNNaN NaNNaN +0+0 -0-0 NaNNaN
    -0-0 -0-0 +0+0 NaNNaN NaNNaN -0-0 +0+0 NaNNaN
    +inf+inf +inf+inf -inf-inf +inf+inf -inf-inf NaNNaN NaNNaN NaNNaN
    -inf-inf -inf-inf +inf+inf -inf-inf +inf+inf NaNNaN NaNNaN NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • Десятичное деление:Decimal division:

    decimal operator /(decimal x, decimal y);
    

    Если значение правого операнда равно нулю, выдается System.DivideByZeroException.If the value of the right operand is zero, a System.DivideByZeroException is thrown. Если полученное значение слишком велико для представления в формате decimal, выдается System.OverflowException.If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. Если значение результата слишком мало для представления в формате decimal, результат равен нулю.If the result value is too small to represent in the decimal format, the result is zero. Масштаб результата является наименьшим масштабом, который сохранит результат, равный ближайшему представлению десятичного значения, равному истинному математическому результату.The scale of the result is the smallest scale that will preserve a result equal to the nearest representable decimal value to the true mathematical result.

    Десятичное деление эквивалентно использованию оператора деления типа System.Decimal.Decimal division is equivalent to using the division operator of type System.Decimal.

Оператор остаткаRemainder operator

Для операции, выполняемой в форме x % y, для выбора конкретной реализации оператора применяется разрешение перегрузки бинарного оператора (разрешение перегрузки бинарного оператора).For an operation of the form x % y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Стандартные операторы остатка перечислены ниже.The predefined remainder operators are listed below. Все операторы вычисляют оставшуюся часть деления между x и y.The operators all compute the remainder of the division between x and y.

  • Целочисленный остаток:Integer remainder:

    int operator %(int x, int y);
    uint operator %(uint x, uint y);
    long operator %(long x, long y);
    ulong operator %(ulong x, ulong y);
    

    Результатом x % y является значение, создаваемое x - (x / y) * y.The result of x % y is the value produced by x - (x / y) * y. Если y равно нулю, выдается System.DivideByZeroException.If y is zero, a System.DivideByZeroException is thrown.

    Если левый операнд является наименьшим значением int или long, а правый операнд — -1, создается System.OverflowException.If the left operand is the smallest int or long value and the right operand is -1, a System.OverflowException is thrown. В этом случае x % y создает исключение, где x / y не создает исключение.In no case does x % y throw an exception where x / y would not throw an exception.

  • Остаток с плавающей запятой:Floating-point remainder:

    float operator %(float x, float y);
    double operator %(double x, double y);
    

    В следующей таблице перечислены результаты всех возможных сочетаний ненулевых конечных значений, нулей, бесконечности и NaN.The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. В таблице x и y являются положительными конечными значениями.In the table, x and y are positive finite values. z является результатом x % y и вычислено как x - n * y, где n — это максимально возможное целое число, которое меньше или равно x / y.z is the result of x % y and is computed as x - n * y, where n is the largest possible integer that is less than or equal to x / y. Этот метод вычисления остатка аналогичен тому, который используется для целочисленных операндов, но отличается от определения IEEE 754 (в котором n — это целое число, ближайшее к x / y).This method of computing the remainder is analogous to that used for integer operands, but differs from the IEEE 754 definition (in which n is the integer closest to x / y).

    + y+y -y-y +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    + x+x + z+z + z+z NaNNaN NaNNaN пx пx NaNNaN
    -x-x – z-z – z-z NaNNaN NaNNaN -x-x -x-x NaNNaN
    +0+0 +0+0 +0+0 NaNNaN NaNNaN +0+0 +0+0 NaNNaN
    -0-0 -0-0 -0-0 NaNNaN NaNNaN -0-0 -0-0 NaNNaN
    +inf+inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
    -inf-inf NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • Остаток от десятичного числа:Decimal remainder:

    decimal operator %(decimal x, decimal y);
    

    Если значение правого операнда равно нулю, выдается System.DivideByZeroException.If the value of the right operand is zero, a System.DivideByZeroException is thrown. Масштаб результата, предшествующий округлению, является большим из масштабов двух операндов, а знак результата, если он не равен нулю, совпадает с x.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.

    Остаток от деления на десятичное значение эквивалентно использованию оператора остатка типа System.Decimal.Decimal remainder is equivalent to using the remainder operator of type System.Decimal.

Оператор сложенияAddition operator

Для операции, выполняемой в форме x + y, для выбора конкретной реализации оператора применяется разрешение перегрузки бинарного оператора (разрешение перегрузки бинарного оператора).For an operation of the form x + y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Ниже перечислены стандартные операторы сложения.The predefined addition operators are listed below. Для числовых типов и перечисления предопределенные операторы сложения вычисляют сумму двух операндов.For numeric and enumeration types, the predefined addition operators compute the sum of the two operands. Если один или оба операнда имеют строковый тип, предопределенные операторы сложения объединяют строковое представление операндов.When one or both operands are of type string, the predefined addition operators concatenate the string representation of the operands.

  • Сложение целых чисел:Integer addition:

    int operator +(int x, int y);
    uint operator +(uint x, uint y);
    long operator +(long x, long y);
    ulong operator +(ulong x, ulong y);
    

    В контексте checked, если сумма находится вне диапазона результирующего типа, выдается System.OverflowException.In a checked context, if the sum is outside the range of the result type, a System.OverflowException is thrown. В контексте unchecked сообщения о избыточности не передаются и все значимые старшие биты за пределами диапазона результирующего типа отбрасываются.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:

    float operator +(float x, float y);
    double operator +(double x, double y);
    

    Сумма вычисляется в соответствии с правилами арифметического IEEE 754.The sum is computed according to the rules of IEEE 754 arithmetic. В следующей таблице перечислены результаты всех возможных сочетаний ненулевых конечных значений, нулей, бесконечности и NaN.The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN's. В таблице x и y являются ненулевыми конечными значениями, а z — результатом x + y.In the table, x and y are nonzero finite values, and z is the result of x + y. Если x и y имеют одинаковую величину, но противоположные знаки, z является положительным нулем.If x and y have the same magnitude but opposite signs, z is positive zero. Если x + y слишком велико для представления в целевом типе, z — бесконечность с тем же знаком, что и x + y.If x + y is too large to represent in the destination type, z is an infinity with the same sign as x + y.

    yy +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    пx зz пx пx +inf+inf -inf-inf NaNNaN
    +0+0 yy +0+0 +0+0 +inf+inf -inf-inf NaNNaN
    -0-0 yy +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    +inf+inf +inf+inf +inf+inf +inf+inf +inf+inf NaNNaN NaNNaN
    -inf-inf -inf-inf -inf-inf -inf-inf NaNNaN -inf-inf NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • Сложение десятичных чисел:Decimal addition:

    decimal operator +(decimal x, decimal y);
    

    Если полученное значение слишком велико для представления в формате decimal, выдается System.OverflowException.If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. Масштаб результата, предшествующий округлению, является большим из масштабов двух операндов.The scale of the result, before any rounding, is the larger of the scales of the two operands.

    Сложение десятичных чисел эквивалентно использованию оператора сложения типа System.Decimal.Decimal addition is equivalent to using the addition operator of type System.Decimal.

  • Добавление перечисления.Enumeration addition. Каждый тип перечисления неявно предоставляет следующие предопределенные операторы, где E является типом перечисления, а U — базовым типом E:Every enumeration type implicitly provides the following predefined operators, where E is the enum type, and U is the underlying type of E:

    E operator +(E x, U y);
    E operator +(U x, E y);
    

    Во время выполнения эти операторы вычисляются точно так же, как (E)((U)x + (U)y).At run-time these operators are evaluated exactly as (E)((U)x + (U)y).

  • Объединение строк:String concatenation:

    string operator +(string x, string y);
    string operator +(string x, object y);
    string operator +(object x, string y);
    

    Эти перегрузки бинарного оператора + выполняют объединение строк.These overloads of the binary + operator perform string concatenation. Если операнд сцепления строк имеет значение null, подставляется пустая строка.If an operand of string concatenation is null, an empty string is substituted. В противном случае любой нестроковый аргумент преобразуется в строковое представление путем вызова метода Virtual ToString, унаследованного от типа object.Otherwise, any non-string argument is converted to its string representation by invoking the virtual ToString method inherited from type object. Если ToString возвращает null, подставляется пустая строка.If ToString returns null, an empty string is substituted.

    using System;
    
    class Test
    {
        static void Main() {
            string s = null;
            Console.WriteLine("s = >" + s + "<");        // displays s = ><
            int i = 1;
            Console.WriteLine("i = " + i);               // displays i = 1
            float f = 1.2300E+15F;
            Console.WriteLine("f = " + f);               // displays f = 1.23E+15
            decimal d = 2.900m;
            Console.WriteLine("d = " + d);               // displays d = 2.900
        }
    }
    

    Результатом оператора объединения строк является строка, состоящая из символов левого операнда, за которыми следуют символы правого операнда. Оператор сцепления строк никогда не возвращает значение null. Если недостаточно памяти для выделения результирующей строки, может возникнуть исключение System.OutOfMemoryException.A System.OutOfMemoryException may be thrown if there is not enough memory available to allocate the resulting string.

  • Сочетание делегатов.Delegate combination. Каждый тип делегата неявно предоставляет следующий предопределенный оператор, где D является типом делегата:Every delegate type implicitly provides the following predefined operator, where D is the delegate type:

    D operator +(D x, D y);
    

    Бинарный оператор + выполняет сочетание делегата, если оба операнда имеют некоторый тип делегата D.The binary + operator performs delegate combination when both operands are of some delegate type D. (Если у операндов разные типы делегатов, возникает ошибка времени привязки.) Если первый операнд имеет null, результатом операции является значение второго операнда (даже если это также null).(If the operands have different delegate types, a binding-time error occurs.) If the first operand is null, the result of the operation is the value of the second operand (even if that is also null). В противном случае, если второй операнд имеет 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 that, when invoked, invokes the first operand and then invokes the second operand. Примеры сочетания делегатов см. в разделе оператор вычитания и вызов делегата.For examples of delegate combination, see Subtraction operator and Delegate invocation. Поскольку System.Delegate не является типом делегата, для него не определен operator @ no__t-2.Since System.Delegate is not a delegate type, operator + is not defined for it.

Оператор вычитанияSubtraction operator

Для операции, выполняемой в форме x - y, для выбора конкретной реализации оператора применяется разрешение перегрузки бинарного оператора (разрешение перегрузки бинарного оператора).For an operation of the form x - y, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Ниже перечислены предопределенные операторы вычитания.The predefined subtraction operators are listed below. Все операторы вычитают y из x.The operators all subtract y from x.

  • Вычитание целых чисел:Integer subtraction:

    int operator -(int x, int y);
    uint operator -(uint x, uint y);
    long operator -(long x, long y);
    ulong operator -(ulong x, ulong y);
    

    В контексте checked, если разница выходит за пределы диапазона результирующего типа, выдается System.OverflowException.In a checked context, if the difference is outside the range of the result type, a System.OverflowException is thrown. В контексте unchecked сообщения о избыточности не передаются и все значимые старшие биты за пределами диапазона результирующего типа отбрасываются.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:

    float operator -(float x, float y);
    double operator -(double x, double y);
    

    Разница вычисляются в соответствии с правилами арифметического IEEE 754.The difference is computed according to the rules of IEEE 754 arithmetic. В следующей таблице перечислены результаты всех возможных сочетаний ненулевых конечных значений, нулей, бесконечности и значений NaN.The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaNs. В таблице x и y являются ненулевыми конечными значениями, а z — результатом x - y.In the table, x and y are nonzero finite values, and z is the result of x - y. Если x и y равны, то z является положительным нулем.If x and y are equal, z is positive zero. Если x - y слишком велико для представления в целевом типе, z — бесконечность с тем же знаком, что и x - y.If x - y is too large to represent in the destination type, z is an infinity with the same sign as x - y.

    yy +0+0 -0-0 +inf+inf -inf-inf NaNNaN
    пx зz пx пx -inf-inf +inf+inf NaNNaN
    +0+0 -y-y +0+0 +0+0 -inf-inf +inf+inf NaNNaN
    -0-0 -y-y -0-0 +0+0 -inf-inf +inf+inf NaNNaN
    +inf+inf +inf+inf +inf+inf +inf+inf NaNNaN +inf+inf NaNNaN
    -inf-inf -inf-inf -inf-inf -inf-inf -inf-inf NaNNaN NaNNaN
    NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN NaNNaN
  • Вычитание десятичных чисел:Decimal subtraction:

    decimal operator -(decimal x, decimal y);
    

    Если полученное значение слишком велико для представления в формате decimal, выдается System.OverflowException.If the resulting value is too large to represent in the decimal format, a System.OverflowException is thrown. Масштаб результата, предшествующий округлению, является большим из масштабов двух операндов.The scale of the result, before any rounding, is the larger of the scales of the two operands.

    Вычитание десятичных чисел эквивалентно использованию оператора вычитания типа System.Decimal.Decimal subtraction is equivalent to using the subtraction operator of type System.Decimal.

  • Вычитание перечисления.Enumeration subtraction. Каждый тип перечисления неявно предоставляет следующий предопределенный оператор, где E является типом перечисления, а U — базовым типом E:Every enumeration type implicitly provides the following predefined operator, where E is the enum type, and U is the underlying type of E:

    U operator -(E x, E y);
    

    Этот оператор вычисляется точно так же, как (U)((U)x - (U)y).This operator is evaluated exactly as (U)((U)x - (U)y). Иными словами, оператор рассчитывает разницу между порядковыми значениями x и 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.

    E operator -(E x, U y);
    

    Этот оператор вычисляется точно так же, как (E)((U)x - y).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. Каждый тип делегата неявно предоставляет следующий предопределенный оператор, где D является типом делегата:Every delegate type implicitly provides the following predefined operator, where D is the delegate type:

    D operator -(D x, D y);
    

    Бинарный оператор - выполняет удаление делегата, если оба операнда имеют некоторый тип делегата D.The binary - operator performs delegate removal when both operands are of some delegate type D. Если у операндов разные типы делегатов, возникает ошибка времени привязки.If the operands have different delegate types, a binding-time error occurs. Если первый операнд — null, результатом операции является null.If the first operand is null, the result of the operation is null. В противном случае, если второй операнд имеет null, результатом операции является значение первого операнда.Otherwise, if the second operand is null, then the result of the operation is the value of the first operand. В противном случае оба операнда представляют списки вызовов (объявления делегатов) с одной или несколькими записями, а результатом является новый список вызовов, состоящий из списка первого операнда с удаленными записями второго операнда, при условии, что второй элемент Список операндов является соответствующим непрерывным подсписком первого элемента.Otherwise, both operands represent invocation lists (Delegate declarations) having one or more entries, and the result is a new invocation list consisting of the first operand's list with the second operand's entries removed from it, provided the second operand's list is a proper contiguous sublist of the first's. (Чтобы определить равенство вложенных списков, соответствующие записи сравниваются как для оператора равенства делегата (Операторы равенства делегата).) В противном случае результатом является значение левого операнда.(To determine sublist equality, corresponding entries are compared as for the delegate equality operator (Delegate equality operators).) Otherwise, the result is the value of the left operand. Ни один из списков операндов не изменяется в процессе.Neither of the operands' lists is changed in the process. Если список второго операнда соответствует нескольким подспискам смежных записей в списке первого операнда, то правый наиболее подходящий подсписок смежных записей удаляется.If the second operand's list matches multiple sublists of contiguous entries in the first operand's list, the right-most matching sublist of contiguous entries is removed. Если удаление приводит к пустому списку, возвращается null.If removal results in an empty list, the result is null. Пример:For example:

    delegate void D(int x);
    
    class C
    {
        public static void M1(int i) { /* ... */ }
        public static void M2(int i) { /* ... */ }
    }
    
    class Test
    {
        static void Main() { 
            D cd1 = new D(C.M1);
            D cd2 = new D(C.M2);
            D cd3 = cd1 + cd2 + cd2 + cd1;   // M1 + M2 + M2 + M1
            cd3 -= cd1;                      // => M1 + M2 + M2
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd1 + cd2;                // => M2 + M1
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd2 + cd2;                // => M1 + M1
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd2 + cd1;                // => M1 + M2
    
            cd3 = cd1 + cd2 + cd2 + cd1;     // M1 + M2 + M2 + M1
            cd3 -= cd1 + cd1;                // => M1 + M2 + M2 + M1
        }
    }
    

Операторы сдвигаShift operators

Операторы << и >> используются для выполнения операций сдвига битов.The << and >> operators are used to perform bit shifting operations.

shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    ;

Если операнд shift_expression имеет тип времени компиляции dynamic, то это выражение динамически привязано (Динамическая привязка).If an operand of a shift_expression has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). В этом случае типом выражения во время компиляции является dynamic, и описанное ниже разрешение будет выполняться во время выполнения с использованием типов времени выполнения этих операндов, которые имеют тип времени компиляции dynamic.In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

Для операции с формой x << count или x >> count для выбора конкретной реализации оператора применяется разрешение перегрузки бинарного оператора (разрешение перегрузки бинарного оператора).For an operation of the form x << count or x >> count, binary operator overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

При объявлении перегруженного оператора сдвига тип первого операнда всегда должен быть классом или структурой, содержащей объявление оператора, а тип второго операнда всегда должен быть int.When declaring an overloaded shift operator, the type of the first operand must always be the class or struct containing the operator declaration, and the type of the second operand must always be int.

Ниже перечислены стандартные операторы сдвига.The predefined shift operators are listed below.

  • Сдвиг влево:Shift left:

    int operator <<(int x, int count);
    uint operator <<(uint x, int count);
    long operator <<(long x, int count);
    ulong operator <<(ulong x, int count);
    

    Оператор << сдвигает x влево на количество вычисленных ниже битов.The << operator shifts x left by a number of bits computed as described below.

    Старшие разряды за пределами диапазона типа результата x отбрасываются, оставшиеся биты сдвигаются влево, а нулевые позиции нижнего порядка — равными нулю.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:

    int operator >>(int x, int count);
    uint operator >>(uint x, int count);
    long operator >>(long x, int count);
    ulong operator >>(ulong x, int count);
    

    Оператор >> сдвигает x вправо на число битов, вычисленное, как описано ниже.The >> operator shifts x right by a number of bits computed as described below.

    Если x имеет тип int или long, младшие биты x отбрасываются, оставшиеся биты сдвигаются вправо, а нулевые позиции с высоким порядком задаются равными нулю, если x не является отрицательным и имеет значение 1, если x является отрицательным.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.

    Если x относится к типу uint или ulong, младшие биты x отбрасываются, оставшиеся биты сдвигаются вправо, а нулевые позиции старших битов устанавливаются в ноль.When x is of type uint or ulong, 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.

Для предопределенных операторов число битов для сдвига вычислено следующим образом:For the predefined operators, the number of bits to shift is computed as follows:

  • Если тип x имеет значение int или uint, счетчик сдвигов задается пятью младшими разрядами count.When the type of x is int or uint, the shift count is given by the low-order five bits of count. Иными словами, число смещений вычислено с count & 0x1F.In other words, the shift count is computed from count & 0x1F.
  • Если тип x имеет значение long или ulong, счетчик сдвигов задается шестью младшими разрядами count.When the type of x is long or ulong, the shift count is given by the low-order six bits of count. Иными словами, число смещений вычислено с count & 0x3F.In other words, the shift count is computed from count & 0x3F.

Если результирующее число сдвигов равно нулю, операторы сдвига просто возвращают значение x.If the resulting shift count is zero, the shift operators simply return the value of x.

Операции сдвига никогда не вызывают избыточность и дают одинаковые результаты в контекстах checked и unchecked.Shift operations never cause overflows and produce the same results in checked and unchecked contexts.

Если левый операнд оператора >> имеет целочисленный тип со знаком, оператор выполняет арифметический сдвиг вправо, где значение наиболее значимого бита (бит знака) операнда передается в позиции нулевого битового индекса высокого порядка.When the left operand of the >> operator is of a signed integral type, the operator performs an arithmetic shift right wherein the value of the most significant bit (the sign bit) of the operand is propagated to the high-order empty bit positions. Если левый операнд оператора >> имеет целочисленный тип без знака, оператор выполняет логическую смену вправо, где позиции пустых битов высокого порядка всегда устанавливаются в ноль.When the left operand of the >> operator is of an unsigned integral type, the operator performs a logical shift right wherein high-order empty bit positions are always set to zero. Для выполнения обратной операции, выводимой из типа операнда, можно использовать явные приведения.To perform the opposite operation of that inferred from the operand type, explicit casts can be used. Например, если x является переменной типа int, операция unchecked((int)((uint)x >> y)) выполняет логическую смену справа от x.For example, if x is a variable of type int, the operation unchecked((int)((uint)x >> y)) performs a logical shift right of x.

Относительные операторы и операторы тестирования типаRelational and type-testing operators

Операторы ==, !=, <, >, @no__t – 4, >=, is и as называются операторами отношения и проверки типа.The ==, !=, <, >, <=, >=, is and as operators are called the relational and type-testing operators.

relational_expression
    : shift_expression
    | relational_expression '<' shift_expression
    | relational_expression '>' shift_expression
    | relational_expression '<=' shift_expression
    | relational_expression '>=' shift_expression
    | relational_expression 'is' type
    | relational_expression 'as' type
    ;

equality_expression
    : relational_expression
    | equality_expression '==' relational_expression
    | equality_expression '!=' relational_expression
    ;

Оператор is описан в операторе is , а оператор as описан в операторе AS.The is operator is described in The is operator and the as operator is described in The as operator.

Операторы ==, !=, <, >, <= и >= являются операторами сравнения.The ==, !=, <, >, <= and >= operators are comparison operators.

Если операнд оператора сравнения имеет тип времени компиляции dynamic, то выражение динамически привязано (Динамическая привязка).If an operand of a comparison operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). В этом случае типом выражения во время компиляции является dynamic, и описанное ниже разрешение будет выполняться во время выполнения с использованием типов времени выполнения этих операндов, которые имеют тип времени компиляции dynamic.In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

Для операции с формой x op y, где Op является оператором сравнения, для выбора конкретной реализации оператора применяется разрешение перегрузки (разрешение перегрузки бинарного оператора).For an operation of the form x op y, where op is a comparison operator, overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Предопределенные операторы сравнения описаны в следующих разделах.The predefined comparison operators are described in the following sections. Все предопределенные операторы сравнения возвращают результат типа bool, как описано в следующей таблице.All predefined comparison operators return a result of type bool, as described in the following table.

ОперациюOperation РезультатResult
x == y true, если x равно y, false в противном случаеtrue if x is equal to y, false otherwise
x != y true, если x не равно y, false в противном случаеtrue if x is not equal to y, false otherwise
x < y true, если x меньше, чем y, false в противном случаеtrue if x is less than y, false otherwise
x > y true, если x больше, чем y, false в противном случаеtrue if x is greater than y, false otherwise
x <= y true, если x меньше или равен y, false в противном случаеtrue if x is less than or equal to y, false otherwise
x >= y true, если x больше или равен y, false в противном случаеtrue if x is greater than or equal to y, false otherwise

Операторы сравнения целых чиселInteger comparison operators

Предопределенные операторы сравнения целых чисел:The predefined integer comparison operators are:

bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);

bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);

bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);

bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);

bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);

bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);

Каждый из этих операторов сравнивает числовые значения двух целочисленных операндов и возвращает значение bool, которое указывает, имеет ли определенное отношение true или false.Each of these operators compares the numeric values of the two integer operands and returns a bool value that indicates whether the particular relation is true or false.

Операторы сравнения чисел с плавающей запятойFloating-point comparison operators

Стандартные операторы сравнения с плавающей запятой:The predefined floating-point comparison operators are:

bool operator ==(float x, float y);
bool operator ==(double x, double y);

bool operator !=(float x, float y);
bool operator !=(double x, double y);

bool operator <(float x, float y);
bool operator <(double x, double y);

bool operator >(float x, float y);
bool operator >(double x, double y);

bool operator <=(float x, float y);
bool operator <=(double x, double y);

bool operator >=(float x, float y);
bool operator >=(double x, double y);

Операторы сравнивают операнды в соответствии с правилами стандарта IEEE 754:The operators compare the operands according to the rules of the IEEE 754 standard:

  • Если один из операндов равен NaN, то результатом является false для всех операторов, кроме !=, для которых результат равен true.If either operand is NaN, the result is false for all operators except !=, for which the result is true. Для любых двух операндов x != y всегда дает тот же результат, что и !(x == y).For any two operands, x != y always produces the same result as !(x == y). Однако если один или оба операнда равны NaN, то операторы <, >, <= и >= не дают те же результаты, что и логическое отрицание противоположного оператора.However, when one or both operands are NaN, the <, >, <=, and >= operators do not produce the same results as the logical negation of the opposite operator. Например, если один из x и y имеет значение NaN, то x < yfalse, а !(x >= y)true.For example, if either of x and y is NaN, then x < y is false, but !(x >= y) is true.

  • Если ни один из операндов не равен NaN, операторы сравнивают значения двух операндов с плавающей запятой относительно порядка.When neither operand is NaN, the operators compare the values of the two floating-point operands with respect to the ordering

    -inf < -max < ... < -min < -0.0 == +0.0 < +min < ... < +max < +inf
    

    где 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.
    • Отрицательная бесконечность считается меньше, чем все остальные значения, но равна другой отрицательной бесконечности.A negative infinity is considered less than all other values, but equal to another negative infinity.
    • Положительная бесконечность считается больше всех остальных значений, но равна другой положительной бесконечности.A positive infinity is considered greater than all other values, but equal to another positive infinity.

Операторы сравнения десятичных чиселDecimal comparison operators

Предопределенные операторы сравнения десятичных чисел:The predefined decimal comparison operators are:

bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);

Каждый из этих операторов сравнивает числовые значения двух десятичных операндов и возвращает значение bool, которое указывает, имеет ли определенное отношение true или false.Each of these operators compares the numeric values of the two decimal operands and returns a bool value that indicates whether the particular relation is true or false. Каждое десятичное сравнение эквивалентно использованию соответствующего оператора реляционного или равенства типа System.Decimal.Each decimal comparison is equivalent to using the corresponding relational or equality operator of type System.Decimal.

Логические операторы равенстваBoolean equality operators

Стандартные логические операторы равенства:The predefined boolean equality operators are:

bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);

Результатом == является true, если оба x и y являются true или false и y.The result of == is true if both x and y are true or if both x and y are false. В противном случае результат будет false.Otherwise, the result is false.

Результатом != является false, если оба x и y являются true или false и y.The result of != is false if both x and y are true or if both x and y are false. В противном случае результат будет true.Otherwise, the result is true. Если операнды имеют тип bool, оператор != дает тот же результат, что и оператор ^.When the operands are of type bool, the != operator produces the same result as the ^ operator.

Операторы сравнения перечисленийEnumeration comparison operators

Каждый тип перечисления неявно предоставляет следующие стандартные операторы сравнения:Every enumeration type implicitly provides the following predefined comparison operators:

bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);

Результат вычисления x op y, где x и y являются выражениями типа перечисления E с базовым типом U, а op — одним из операторов сравнения, точно такой же, как при вычислении ((U)x) op ((U)y).The result of evaluating x op y, where x and y are expressions of an enumeration type E with an underlying type U, and op is one of the comparison operators, is exactly the same as evaluating ((U)x) op ((U)y). Иными словами, операторы сравнения типов перечисления просто сравнивают базовые целочисленные значения двух операндов.In other words, the enumeration type comparison operators simply compare the underlying integral values of the two operands.

Операторы равенства ссылочных типовReference type equality operators

Стандартные операторы равенства ссылочного типа:The predefined reference type equality operators are:

bool operator ==(object x, object y);
bool operator !=(object x, object y);

Операторы возвращают результат сравнения двух ссылок на равенство или неравенство.The operators return the result of comparing the two references for equality or non-equality.

Поскольку стандартные операторы равенства ссылочного типа принимают операнды типа object, они применяются ко всем типам, которые не объявляют применимые члены operator == и operator !=.Since the predefined reference type equality operators accept operands of type object, they apply to all types that do not declare applicable operator == and operator != members. И наоборот, все применимые определяемые пользователем операторы равенства фактически скрывают стандартные операторы равенства ссылочных типов.Conversely, any applicable user-defined equality operators effectively hide the predefined reference type equality operators.

Предопределенные операторы равенства ссылочного типа должны иметь одно из следующих значений:The predefined reference type equality operators require one of the following:

  • Оба операнда являются значением типа, известного как reference_type или Literal null.Both operands are a value of a type known to be a reference_type or the literal null. Кроме того, явное преобразование ссылок (явные преобразования ссылок) существует из типа любого операнда в тип другого операнда.Furthermore, an explicit reference conversion (Explicit reference conversions) exists from the type of either operand to the type of the other operand.
  • Один операнд — это значение типа T, где T — это type_parameter , а второй операнд — литерал null.One operand is a value of type T where T is a type_parameter and the other operand is the literal null. Более того, T не имеет ограничения типа значения.Furthermore T does not have the value type constraint.

Если не выполняется одно из этих условий, возникает ошибка времени привязки.Unless one of these conditions are true, a binding-time error occurs. Существенные последствия этих правил:Notable implications of these rules are:

  • Это ошибка времени привязки для использования предопределенных операторов равенства ссылочных типов для сравнения двух ссылок, которые могут различаться во время привязки.It is a binding-time error to use the predefined reference type equality operators to compare two references that are known to be different at binding-time. Например, если типы времени привязки операндов являются двумя типами классов A и B, и если ни A, ни B являются производными от другого, то бы два операнда не будут ссылаться на один и тот же объект.For example, if the binding-time types of the operands are two class types A and B, and if neither A nor B derives from the other, then it would be impossible for the two operands to reference the same object. Таким же действие считается ошибкой времени привязки.Thus, the operation is considered a binding-time error.
  • Стандартные операторы равенства ссылочного типа не допускают сравнение операндов типа значения.The predefined reference type equality operators do not permit value type operands to be compared. Таким образом, если тип структуры не объявляет свои собственные операторы равенства, нельзя сравнивать значения этого типа структуры.Therefore, unless a struct type declares its own equality operators, it is not possible to compare values of that struct type.
  • Стандартные операторы равенства ссылочного типа никогда не вызывают операции упаковки-преобразования для своих операндов.The predefined reference type equality operators never cause boxing operations to occur for their operands. Выполнение таких операций упаковки будет бессмысленным, так как ссылки на вновь выделенные Упакованные экземпляры должны отличаться от всех остальных ссылок.It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.
  • Если операнд типа параметра типа T сравнивается с null, а тип времени выполнения T — типом значения, результатом сравнения будет false.If an operand of a type parameter type T is compared to null, and the run-time type of T is a value type, the result of the comparison is false.

В следующем примере проверяется, имеет ли аргумент неограниченного типа параметра типа значение null.The following example checks whether an argument of an unconstrained type parameter type is null.

class C<T>
{
    void F(T x) {
        if (x == null) throw new ArgumentNullException();
        ...
    }
}

Конструкция x == null разрешена, несмотря на то, что T может представлять тип значения, а результат просто определяется как false, если T является типом значения.The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.

Для операции с формой x == y или x != y, если применимы operator == или operator !=, правила переопределения перегрузки операторов (разрешение перегрузкибинарного оператора) будут выбирать этот оператор вместо стандартного равенства ссылочного типа. станции.For an operation of the form x == y or x != y, if any applicable operator == or operator != exists, the operator overload resolution (Binary operator overload resolution) rules will select that operator instead of the predefined reference type equality operator. Однако всегда можно выбрать стандартный оператор равенства ссылочного типа, явно приведя один или оба операнда к типу object.However, it is always possible to select the predefined reference type equality operator by explicitly casting one or both of the operands to type object. ПримерThe example

using System;

class Test
{
    static void Main() {
        string s = "Test";
        string t = string.Copy(s);
        Console.WriteLine(s == t);
        Console.WriteLine((object)s == t);
        Console.WriteLine(s == (object)t);
        Console.WriteLine((object)s == (object)t);
    }
}

выводятся следующие выходные данныеproduces the output

True
False
False
False

Переменные s и t ссылаются на два отдельных экземпляра string, содержащие одни и те же символы.The s and t variables refer to two distinct string instances containing the same characters. Первое сравнение выводит True, так как стандартный оператор равенства строк (Операторы равенства строк) выбран, если оба операнда имеют тип string.The first comparison outputs True because the predefined string equality operator (String equality operators) is selected when both operands are of type string. Оставшиеся сравнения всех выходных данных False, так как выбран стандартный оператор равенства ссылочного типа, если один или оба операнда имеют тип object.The remaining comparisons all output False because the predefined reference type equality operator is selected when one or both of the operands are of type object.

Обратите внимание, что описанный выше метод не имеет смысла для типов значений.Note that the above technique is not meaningful for value types. ПримерThe example

class Test
{
    static void Main() {
        int i = 123;
        int j = 123;
        System.Console.WriteLine((object)i == (object)j);
    }
}

выводит False, поскольку приведения создают ссылки на два отдельных экземпляра упакованных значений int.outputs False because the casts create references to two separate instances of boxed int values.

Операторы равенства строкString equality operators

Стандартные операторы равенства строк:The predefined string equality operators are:

bool operator ==(string x, string y);
bool operator !=(string x, string y);

Два значения string считаются равными, если выполняется одно из следующих условий:Two string values are considered equal when one of the following is true:

  • Оба значения имеют значение null.Both values are null.
  • Оба значения не являются пустыми ссылками на экземпляры строк, имеющие одинаковую длину и идентичные символы в каждой позиции символа.Both values are non-null references to string instances that have identical lengths and identical characters in each character position.

Операторы равенства строк сравнивают строковые значения, а не ссылки на строки.The string equality operators compare string values rather than string references. Если два отдельных экземпляра строки содержат точную последовательность символов, значения строк равны, но ссылки отличаются.When two separate string instances contain the exact same sequence of characters, the values of the strings are equal, but the references are different. Как описано в разделе Операторы равенства ссылочного типа, операторы равенства ссылочного типа можно использовать для сравнения строковых ссылок вместо строковых значений.As described in Reference type equality operators, the reference type equality operators can be used to compare string references instead of string values.

Операторы равенства делегатовDelegate equality operators

Каждый тип делегата неявно предоставляет следующие стандартные операторы сравнения:Every delegate type implicitly provides the following predefined comparison operators:

bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);

Два экземпляра делегата считаются равными следующим образом.Two delegate instances are considered equal as follows:

  • Если один из экземпляров делегата имеет значение null, они равны, только если оба являются null.If either of the delegate instances is null, they are equal if and only if both are null.
  • Если у делегатов разные типы времени выполнения, они никогда не равны.If the delegates have different run-time type they are never equal.
  • Если оба экземпляра делегата имеют список вызовов (объявления делегатов), то эти экземпляры равны только в том случае, если их списки вызовов имеют одинаковую длину, и каждая запись в одном списке вызовов равна (как определено ниже) с соответствующим , по порядку, в списке вызовов другого пользователя.If both of the delegate instances have an invocation list (Delegate declarations), those instances are equal if and only if their invocation lists are the same length, and each entry in one's invocation list is equal (as defined below) to the corresponding entry, in order, in the other's invocation list.

Следующие правила управляют равенством записей в списке вызовов.The following rules govern the equality of invocation list entries:

  • Если две записи списка вызовов ссылаются на один и тот же статический метод, то записи равны.If two invocation list entries both refer to the same static method then the entries are equal.
  • Если две записи списка вызовов ссылаются на один и тот же нестатический метод в одном и том же целевом объекте (как определено операторами равенства ссылок), то записи равны.If two invocation list entries both refer to the same non-static method on the same target object (as defined by the reference equality operators) then the entries are equal.
  • Записи списка вызовов, созданные при вычислении семантически идентичных anonymous_method_expressions или lambda_expressions с тем же (возможно, пустой) набором захваченных экземпляров внешней переменной, разрешены (но не требуются) равномерно.Invocation list entries produced from evaluation of semantically identical anonymous_method_expressions or lambda_expressions with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal.

Операторы равенства и NULLEquality operators and null

Операторы == и != допускают, что один операнд является значением типа, допускающего значение null, а другой — литералом null, даже если для операции не существует заранее определенного или определяемого пользователем оператора (в форме без приподнятия или с нулификацией).The == and != operators permit one operand to be a value of a nullable type and the other to be the null literal, even if no predefined or user-defined operator (in unlifted or lifted form) exists for the operation.

Для операции одной из формFor an operation of one of the forms

x == null
null == x
x != null
null != x

Если x является выражением типа, допускающего значение null, то если разрешение перегрузки оператора (разрешение перегрузки бинарного оператора) не может найти применимого оператора, результат будет вычислен из свойства HasValue класса x.where x is an expression of a nullable type, if operator overload resolution (Binary operator overload resolution) fails to find an applicable operator, the result is instead computed from the HasValue property of x. В частности, первые две формы преобразуются в !x.HasValue, а последние две формы преобразуются в x.HasValue.Specifically, the first two forms are translated into !x.HasValue, and last two forms are translated into x.HasValue.

Оператор isThe is operator

Оператор is используется для динамической проверки того, совместим ли тип времени выполнения объекта с заданным типом.The is operator is used to dynamically check if the run-time type of an object is compatible with a given type. Результат операции E is T, где E является выражением, а T — типом, является логическим значением, указывающим, может ли E быть успешно преобразовано в тип T путем преобразования ссылок, упаковки-преобразования или преобразования распаковки.The result of the operation E is T, where E is an expression and T is a type, is a boolean value indicating whether E can successfully be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion. Операция вычисляется следующим образом, после того как аргументы типа были замещены для всех параметров типа:The operation is evaluated as follows, after type arguments have been substituted for all type parameters:

  • Если E является анонимной функцией, возникает ошибка времени компиляцииIf E is an anonymous function, a compile-time error occurs
  • Если E является группой методов или литералом null, если тип E является ссылочным типом или типом, допускающим значение null, а значение E равно null, результат равен false.If E is a method group or the null literal, of if the type of E is a reference type or a nullable type and the value of E is null, the result is false.
  • В противном случае Let D представляет динамический тип E следующим образом:Otherwise, let D represent the dynamic type of E as follows:
    • Если тип E является ссылочным типом, D — это тип времени выполнения ссылки на экземпляр по E.If the type of E is a reference type, D is the run-time type of the instance reference by E.
    • Если тип E является типом, допускающим значение null, то D является базовым типом этого типа, допускающего значение null.If the type of E is a nullable type, D is the underlying type of that nullable type.
    • Если тип E является типом значения, не допускающим значения NULL, D является типом E.If the type of E is a non-nullable value type, D is the type of E.
  • Результат операции зависит от D и T следующим образом:The result of the operation depends on D and T as follows:
    • Если T является ссылочным типом, результатом будет true, если D и T имеют один и тот же тип, если D является ссылочным типом и неявное преобразование ссылки из D в T существует, или если D является типом значения и упаковкой-преобразование из @no__t – 7 для T существует.If T is a reference type, the result is true if D and T are the same type, if D is a reference type and an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.
    • Если T является типом, допускающим значение null, результатом будет true, если D является базовым типом T.If T is a nullable type, the result is true if D is the underlying type of T.
    • Если T является типом значения, не допускающим значения NULL, результат имеет значение true, если D и T имеют один и тот же тип.If T is a non-nullable value type, the result is true if D and T are the same type.
    • В противном случае результатом будет false.Otherwise, the result is false.

Обратите внимание, что пользовательские преобразования не рассматриваются оператором is.Note that user defined conversions, are not considered by the is operator.

Оператор asThe as operator

Оператор as используется для явного преобразования значения в данный ссылочный тип или тип, допускающий значение null.The as operator is used to explicitly convert a value to a given reference type or nullable type. В отличие от выражения приведения (выражения приведения) оператор as никогда не создает исключение.Unlike a cast expression (Cast expressions), the as operator never throws an exception. Вместо этого, если указанное преобразование невозможно, полученное значение равно null.Instead, if the indicated conversion is not possible, the resulting value is null.

В операции формы E as T E должно быть выражением, а T должен быть ссылочным типом, параметром типа, который является ссылочным типом, или типом, допускающим значение null.In an operation of the form E as T, E must be an expression and T must be a reference type, a type parameter known to be a reference type, or a nullable type. Кроме того, должно быть задано по крайней мере одно из следующих условий, или в противном случае возникает ошибка времени компиляции:Furthermore, at least one of the following must be true, or otherwise a compile-time error occurs:

Если тип времени компиляции E не dynamic, операция E as T дает тот же результат, что иIf the compile-time type of E is not dynamic, the operation E as T produces the same result as

E is T ? (T)(E) : (T)null

за исключением того, что E вычисляется только один раз.except that E is only evaluated once. Компилятор может ожидать оптимизации E as T для выполнения не более чем для одной динамической проверки типа, чем для двух динамических типов, подразумеваемых расширением выше.The compiler can be expected to optimize E as T to perform at most one dynamic type check as opposed to the two dynamic type checks implied by the expansion above.

Если тип времени компиляции E имеет dynamic, в отличие от оператора CAST оператор as не связан динамически (Динамическая привязка).If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound (Dynamic binding). Таким образом, расширение в этом случае:Therefore the expansion in this case is:

E is T ? (T)(object)(E) : (T)null

Обратите внимание, что некоторые преобразования, такие как пользовательские преобразования, невозможны с помощью оператора as, поэтому они должны выполняться с использованием выражений приведения.Note that some conversions, such as user defined conversions, are not possible with the as operator and should instead be performed using cast expressions.

В примереIn the example

class X
{

    public string F(object o) {
        return o as string;        // OK, string is a reference type
    }

    public T G<T>(object o) where T: Attribute {
        return o as T;             // Ok, T has a class constraint
    }

    public U H<U>(object o) {
        return o as U;             // Error, U is unconstrained 
    }
}

параметр типа T из G называется ссылочным типом, так как имеет ограничение класса.the type parameter T of G is known to be a reference type, because it has the class constraint. Параметр типа U из H не имеет значение. Поэтому использование оператора as в H запрещено.The type parameter U of H is not however; hence the use of the as operator in H is disallowed.

Логические операторыLogical operators

Операторы &, ^ и | называются логическими операторами.The &, ^, and | operators are called the logical operators.

and_expression
    : equality_expression
    | and_expression '&' equality_expression
    ;

exclusive_or_expression
    : and_expression
    | exclusive_or_expression '^' and_expression
    ;

inclusive_or_expression
    : exclusive_or_expression
    | inclusive_or_expression '|' exclusive_or_expression
    ;

Если операнд логического оператора имеет тип времени компиляции dynamic, то выражение динамически привязано (Динамическая привязка).If an operand of a logical operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). В этом случае типом выражения во время компиляции является dynamic, и описанное ниже разрешение будет выполняться во время выполнения с использованием типов времени выполнения этих операндов, которые имеют тип времени компиляции dynamic.In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

Для операции с формой x op y, где op является одним из логических операторов, для выбора конкретной реализации оператора применяется разрешение перегрузки (разрешение перегрузки бинарного оператора).For an operation of the form x op y, where op is one of the logical operators, overload resolution (Binary operator overload resolution) is applied to select a specific operator implementation. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возвращаемого значения оператора.The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

Стандартные логические операторы описаны в следующих разделах.The predefined logical operators are described in the following sections.

Целочисленные логические операторыInteger logical operators

Стандартные целочисленные логические операторы:The predefined integer logical operators are:

int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);

int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);

int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);

Оператор & выдает побитовую логическую AND двух операндов, оператор | вычислит побитовую логическую OR двух операндов, а оператор ^ вычислит побитовую логическую исключающую OR двух операндов.The & operator computes the bitwise logical AND of the two operands, the | operator computes the bitwise logical OR of the two operands, and the ^ operator computes the bitwise logical exclusive OR of the two operands. В этих операциях переполняется невозможно.No overflows are possible from these operations.

Логические операторы перечисленияEnumeration logical operators

Каждый тип перечисления E неявно предоставляет следующие стандартные логические операторы:Every enumeration type E implicitly provides the following predefined logical operators:

E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);

Результат вычисления x op y, где x и y являются выражениями типа перечисления E с базовым типом U, а op — одним из логических операторов, точно такой же, как при вычислении (E)((U)x op (U)y).The result of evaluating x op y, where x and y are expressions of an enumeration type E with an underlying type U, and op is one of the logical operators, is exactly the same as evaluating (E)((U)x op (U)y). Иными словами, логические операторы типа перечисления просто выполняют логическую операцию с базовым типом двух операндов.In other words, the enumeration type logical operators simply perform the logical operation on the underlying type of the two operands.

Логические операторыBoolean logical operators

Предопределенные логические логические операторы:The predefined boolean logical operators are:

bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);

Результат операции x & y принимает значение true, если оба оператора x и y имеют значение true.The result of x & y is true if both x and y are true. В противном случае результат будет false.Otherwise, the result is false.

Результатом x | y является true, если либо x, либо ytrue.The result of x | y is true if either x or y is true. В противном случае результат будет false.Otherwise, the result is false.

Результатом x ^ y является true, если xtrue, а yfalse, ytrue.The result of x ^ y is true if x is true and y is false, or x is false and y is true. В противном случае результат будет false.Otherwise, the result is false. Если операнды имеют тип bool, оператор ^ вычислит тот же результат, что и оператор !=.When the operands are of type bool, the ^ operator computes the same result as the != operator.

Логические операторы, допускающие значения NULLNullable boolean logical operators

Тип Boolean, допускающий значение null, bool? может представлять три значения: true, false и null, и концептуально аналогичен типу с тремя значениями, используемому для логических выражений в SQL.The nullable boolean type bool? can represent three values, true, false, and null, and is conceptually similar to the three-valued type used for boolean expressions in SQL. Чтобы гарантировать, что результаты операторов & и | для операндов bool? соответствуют логике SQL с тремя значениями, предоставляются следующие предопределенные операторы:To ensure that the results produced by the & and | operators for bool? operands are consistent with SQL's three-valued logic, the following predefined operators are provided:

bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);

В следующей таблице перечислены результаты, создаваемые этими операторами для всех сочетаний значений true, false и null.The following table lists the results produced by these operators for all combinations of the values true, false, and null.

x y x & y x | y
true true true true
true false false true
true null null true
false true false true
false false false false
false null false null
null true null true
null false false null
null null null null

Условные логические операторыConditional logical operators

Операторы && и || называются условными логическими операторами.The && and || operators are called the conditional logical operators. Они также называются логическими операторами "сокращенного".They are also called the "short-circuiting" logical operators.

conditional_and_expression
    : inclusive_or_expression
    | conditional_and_expression '&&' inclusive_or_expression
    ;

conditional_or_expression
    : conditional_and_expression
    | conditional_or_expression '||' conditional_and_expression
    ;

Операторы && и || являются условными версиями операторов & и |:The && and || operators are conditional versions of the & and | operators:

  • Операция x && y соответствует операции x & y, за исключением того, что y вычисляется только в том случае, если x не false.The operation x && y corresponds to the operation x & y, except that y is evaluated only if x is not false.
  • Операция x || y соответствует операции x | y, за исключением того, что y вычисляется только в том случае, если x не true.The operation x || y corresponds to the operation x | y, except that y is evaluated only if x is not true.

Если операнд условного логического оператора имеет тип времени компиляции dynamic, то выражение динамически привязано (Динамическая привязка).If an operand of a conditional logical operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). В этом случае типом выражения во время компиляции является dynamic, и описанное ниже разрешение будет выполняться во время выполнения с использованием типов времени выполнения этих операндов, которые имеют тип времени компиляции dynamic.In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

Операция формы x && y или x || y обрабатывается путем применения разрешения перегрузки (разрешение перегрузки бинарного оператора), как если бы операция была написана x & y или x | y.An operation of the form x && y or x || y is processed by applying overload resolution (Binary operator overload resolution) as if the operation was written x & y or x | y. ЭтогоThen,

Нельзя напрямую перегружать условные логические операторы.It is not possible to directly overload the conditional logical operators. Однако, поскольку условные логические операторы оцениваются с точки зрения обычных логических операторов, перегрузки обычных логических операторов имеют определенные ограничения, которые также считаются перегрузками условных логических операторов.However, because the conditional logical operators are evaluated in terms of the regular logical operators, overloads of the regular logical operators are, with certain restrictions, also considered overloads of the conditional logical operators. Это описано далее в определяемых пользователем условных логических операторах.This is described further in User-defined conditional logical operators.

Логические условные операторыBoolean conditional logical operators

Если операнды && или || имеют тип bool или если операнды имеют типы, не определяющие применимые operator & или operator |, но определяют неявные преобразования в bool, операция обрабатывается следующим образом. :When the operands of && or || are of type bool, or when the operands are of types that do not define an applicable operator & or operator |, but do define implicit conversions to bool, the operation is processed as follows:

  • Операция x && y вычисляется как x ? y : false.The operation x && y is evaluated as x ? y : false. Иными словами, сначала вычисляется x и преобразуется в тип bool.In other words, x is first evaluated and converted to type bool. Затем, если x равно true, y вычисляется и преобразуется в тип bool, и это становится результатом операции.Then, if x is true, y is evaluated and converted to type bool, and this becomes the result of the operation. В противном случае результатом операции будет false.Otherwise, the result of the operation is false.
  • Операция x || y вычисляется как x ? true : y.The operation x || y is evaluated as x ? true : y. Иными словами, сначала вычисляется x и преобразуется в тип bool.In other words, x is first evaluated and converted to type bool. Затем, если x равно true, результатом операции будет true.Then, if x is true, the result of the operation is true. В противном случае y вычисляется и преобразуется в тип bool, и это становится результатом операции.Otherwise, y is evaluated and converted to type bool, and this becomes the result of the operation.

Пользовательские условные логические операторыUser-defined conditional logical operators

Если операнды && или || имеют типы, объявляющие применимые определяемые пользователем operator & или operator |, оба следующих типа должны иметь значение true, где T — это тип, в котором объявлен выбранный оператор:When the operands of && or || are of types that declare an applicable user-defined operator & or operator |, both of the following must be true, where T is the type in which the selected operator is declared:

  • Тип возвращаемого значения и тип каждого параметра выбранного оператора должны быть T.The return type and the type of each parameter of the selected operator must be T. Иными словами, оператор должен вычислять логическое AND или логическое OR двух операндов типа T и должен возвращать результат типа T.In other words, the operator must compute the logical AND or the logical OR of two operands of type T, and must return a result of type T.
  • T должен содержать объявления operator true и operator false.T must contain declarations of operator true and operator false.

Если какое-либо из этих требований не выполнено, возникает ошибка времени привязки.A binding-time error occurs if either of these requirements is not satisfied. В противном случае операция && или || вычисляется путем объединения определяемых пользователем operator true или operator false с выбранным определяемым пользователем оператором:Otherwise, the && or || operation is evaluated by combining the user-defined operator true or operator false with the selected user-defined operator:

  • Операция x && y вычисляется как T.false(x) ? x : T.&(x, y), где T.false(x) является вызовом operator false, объявленным в T, а T.&(x, y) — вызовом выбранного operator &.The operation x && y is evaluated as T.false(x) ? x : T.&(x, y), where T.false(x) is an invocation of the operator false declared in T, and T.&(x, y) is an invocation of the selected operator &. Иными словами, сначала вычисляется x, и operator false вызывается по результату, чтобы определить, является ли x безусловно ложным.In other words, x is first evaluated and operator false is invoked on the result to determine if x is definitely false. Затем, если x является определенно false, результатом операции будет значение, вычисленное ранее для x.Then, if x is definitely false, the result of the operation is the value previously computed for x. В противном случае вычисляется y, а выбранное operator & вызывается по значению, вычисленному ранее для x, и значение, вычисленное для y, для получения результата операции.Otherwise, y is evaluated, and the selected operator & is invoked on the value previously computed for x and the value computed for y to produce the result of the operation.
  • Операция x || y вычисляется как T.true(x) ? x : T.|(x, y), где T.true(x) является вызовом operator true, объявленным в T, а T.|(x,y) — вызовом выбранного operator|.The operation x || y is evaluated as T.true(x) ? x : T.|(x, y), where T.true(x) is an invocation of the operator true declared in T, and T.|(x,y) is an invocation of the selected operator|. Иными словами, сначала вычисляется x, и operator true вызывается по результату, чтобы определить, является ли x безусловно истинным.In other words, x is first evaluated and operator true is invoked on the result to determine if x is definitely true. Затем, если x является истинным значением true, результатом операции будет значение, вычисленное ранее для x.Then, if x is definitely true, the result of the operation is the value previously computed for x. В противном случае вычисляется y, а выбранное operator | вызывается по значению, вычисленному ранее для x, и значение, вычисленное для y, для получения результата операции.Otherwise, y is evaluated, and the selected operator | is invoked on the value previously computed for x and the value computed for y to produce the result of the operation.

В любой из этих операций выражение, заданное x, вычисляется только один раз, а выражение, заданное y, либо не вычисляется, либо вычисляется ровно один раз.In either of these operations, the expression given by x is only evaluated once, and the expression given by y is either not evaluated or evaluated exactly once.

Пример типа, реализующего operator true и operator false, см. в разделе логический тип базы данных.For an example of a type that implements operator true and operator false, see Database boolean type.

Оператор объединения со значением NULLThe null coalescing operator

Оператор ?? называется оператором объединения со значением NULL.The ?? operator is called the null coalescing operator.

null_coalescing_expression
    : conditional_or_expression
    | conditional_or_expression '??' null_coalescing_expression
    ;

Выражение объединения null в форме a ?? b требует, чтобы a был тип или ссылочный тип, допускающий значение null.A null coalescing expression of the form a ?? b requires a to be of a nullable type or reference type. Если a не равно null, результатом a ?? b будет a; в противном случае результатом является b.If a is non-null, the result of a ?? b is a; otherwise, the result is b. Операция вычисляет b, только если a имеет значение null.The operation evaluates b only if a is null.

Оператор объединения со значением null имеет правое ассоциативное значение, что означает, что операции группируются справа налево.The null coalescing operator is right-associative, meaning that operations are grouped from right to left. Например, выражение вида a ?? b ?? c вычисляется как a ?? (b ?? c).For example, an expression of the form a ?? b ?? c is evaluated as a ?? (b ?? c). В общих чертах выражение формы E1 ?? E2 ?? ... ?? En возвращает первый из операндов, не равных NULL, или значение null, если все операнды имеют значение null.In general terms, an expression of the form E1 ?? E2 ?? ... ?? En returns the first of the operands that is non-null, or null if all operands are null.

Тип выражения a ?? b зависит от того, какие неявные преобразования доступны для операндов.The type of the expression a ?? b depends on which implicit conversions are available on the operands. В порядке предпочтения типом a ?? b является A0, A или B, где A — это тип a (при условии, что a имеет тип), B — это тип b (при условии, что b имеет тип). , а 0 является базовым типом 1, если 2 является типом, допускающим значение null, или 3 в противном случае.In order of preference, the type of a ?? b is A0, A, or B, where A is the type of a (provided that a has a type), B is the type of b (provided that b has a type), and A0 is the underlying type of A if A is a nullable type, or A otherwise. В частности, a ?? b обрабатывается следующим образом:Specifically, a ?? b is processed as follows:

  • Если A существует и не является типом, допускающим значение null, или ссылочным типом, возникает ошибка времени компиляции.If A exists and is not a nullable type or a reference type, a compile-time error occurs.
  • Если b является динамическим выражением, тип результата dynamic.If b is a dynamic expression, the result type is dynamic. Во время выполнения сначала вычисляется a.At run-time, a is first evaluated. Если a не равно null, a преобразуется в динамический, и это становится результатом.If a is not null, a is converted to dynamic, and this becomes the result. В противном случае вычисляется b, и это становится результатом.Otherwise, b is evaluated, and this becomes the result.
  • В противном случае, если A существует и является типом, допускающим значение null, и существует неявное преобразование из b в A0, результатом будет тип A0.Otherwise, if A exists and is a nullable type and an implicit conversion exists from b to A0, the result type is A0. Во время выполнения сначала вычисляется a.At run-time, a is first evaluated. Если a не равно null, a преобразуется в тип A0, и это становится результатом.If a is not null, a is unwrapped to type A0, and this becomes the result. В противном случае b вычисляется и преобразуется в тип A0, и это становится результатом.Otherwise, b is evaluated and converted to type A0, and this becomes the result.
  • В противном случае, если A существует и существует неявное преобразование из b в A, результатом будет тип A.Otherwise, if A exists and an implicit conversion exists from b to A, the result type is A. Во время выполнения сначала вычисляется a.At run-time, a is first evaluated. Если a не равно null, результат будет a.If a is not null, a becomes the result. В противном случае b вычисляется и преобразуется в тип A, и это становится результатом.Otherwise, b is evaluated and converted to type A, and this becomes the result.
  • В противном случае, если b имеет тип B и существует неявное преобразование из a в B, тип результата — B.Otherwise, if b has a type B and an implicit conversion exists from a to B, the result type is B. Во время выполнения сначала вычисляется a.At run-time, a is first evaluated. Если a не равно null, a преобразуется в тип A0 (если A существует и допускает значение null) и преобразуется в тип B, и это становится результатом.If a is not null, a is unwrapped to type A0 (if A exists and is nullable) and converted to type B, and this becomes the result. В противном случае вычисляется b и становится результатом.Otherwise, b is evaluated and becomes the result.
  • В противном случае a и b несовместимы и возникает ошибка времени компиляции.Otherwise, a and b are incompatible, and a compile-time error occurs.

Условный операторConditional operator

Оператор ?: называется условным оператором.The ?: operator is called the conditional operator. Иногда он также называется оператором ternary.It is at times also called the ternary operator.

conditional_expression
    : null_coalescing_expression
    | null_coalescing_expression '?' expression ':' expression
    ;

Условное выражение формы b ? x : y сначала вычисляет условие b.A conditional expression of the form b ? x : y first evaluates the condition b. Затем, если b равно true, вычисляется x и становится результатом операции.Then, if b is true, x is evaluated and becomes the result of the operation. В противном случае вычисляется y и становится результатом операции.Otherwise, y is evaluated and becomes the result of the operation. Условное выражение никогда не вычисляет как x, так и y.A conditional expression never evaluates both x and y.

Условный оператор имеет правое ассоциативное значение, означающее, что операции группируются справа налево.The conditional operator is right-associative, meaning that operations are grouped from right to left. Например, выражение вида a ? b : c ? d : e вычисляется как a ? b : (c ? d : e).For example, an expression of the form a ? b : c ? d : e is evaluated as a ? b : (c ? d : e).

Первый операнд оператора ?: должен быть выражением, которое может быть неявно преобразовано в bool, или выражение типа, реализующего operator true.The first operand of the ?: operator must be an expression that can be implicitly converted to bool, or an expression of a type that implements operator true. Если ни одно из этих требований не удовлетворено, возникает ошибка времени компиляции.If neither of these requirements is satisfied, a compile-time error occurs.

Второй и третий операнды, x и y, оператора ?: управляют типом условного выражения.The second and third operands, x and y, of the ?: operator control the type of the conditional expression.

  • Если x имеет тип X, а y имеет тип Y, тоIf x has type X and y has type Y then
    • Если неявное преобразование (неявные преобразования) существует из X в Y, но не из Y в X, то Y является типом условного выражения.If an implicit conversion (Implicit conversions) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
    • Если неявное преобразование (неявные преобразования) существует из Y в X, но не из X в Y, то X является типом условного выражения.If an implicit conversion (Implicit conversions) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
    • В противном случае тип выражения не может быть определен, и возникает ошибка времени компиляции.Otherwise, no expression type can be determined, and a compile-time error occurs.
  • Если только один из x и y имеет тип, а как x, так и y, неявно преобразуется в этот тип, то это тип условного выражения.If only one of x and y has a type, and both x and y, of are implicitly convertible to that type, then that is the type of the conditional expression.
  • В противном случае тип выражения не может быть определен, и возникает ошибка времени компиляции.Otherwise, no expression type can be determined, and a compile-time error occurs.

Обработка условного выражения в формате времени выполнения b ? x : y состоит из следующих шагов:The run-time processing of a conditional expression of the form b ? x : y consists of the following steps:

  • Сначала вычисляется b, и определяется значение bool, равное b:First, b is evaluated, and the bool value of b is determined:
    • Если неявное преобразование из типа b в bool существует, это неявное преобразование выполняется для получения значения bool.If an implicit conversion from the type of b to bool exists, then this implicit conversion is performed to produce a bool value.
    • В противном случае для получения значения bool вызывается operator true, определенный типом b.Otherwise, the operator true defined by the type of b is invoked to produce a bool value.
  • Если значение bool, созданное на предыдущем шаге, равно true, то x вычисляется и преобразуется в тип условного выражения, и это становится результатом условного выражения.If the bool value produced by the step above is true, then x is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.
  • В противном случае y вычисляется и преобразуется в тип условного выражения, и это становится результатом условного выражения.Otherwise, y is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.

Выражения анонимных функцийAnonymous function expressions

Анонимная функция — это выражение, представляющее "встроенное" определение метода.An anonymous function is an expression that represents an "in-line" method definition. Анонимная функция не имеет значения или типа в и, но преобразуется в совместимый тип делегата или дерева выражения.An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression tre