タプル型での = = および! = のサポートSupport for == and != on tuple types

t1 == t2 t1t2 が同じカーディナリティのタプルまたは null 許容のタプル型である式を使用して、とほぼ同じように評価し temp1.Item1 == temp2.Item1 && temp1.Item2 == temp2.Item2 var temp1 = t1; var temp2 = t2; ます。Allow expressions t1 == t2 where t1 and t2 are tuple or nullable tuple types of same cardinality, and evaluate them roughly as temp1.Item1 == temp2.Item1 && temp1.Item2 == temp2.Item2 (assuming var temp1 = t1; var temp2 = t2;).

逆に、とし t1 != t2 てこれを許可して評価し temp1.Item1 != temp2.Item1 || temp1.Item2 != temp2.Item2 ます。Conversely it would allow t1 != t2 and evaluate it as temp1.Item1 != temp2.Item1 || temp1.Item2 != temp2.Item2.

Null 許容の場合は、とに対する追加のチェック temp1.HasValue temp2.HasValue が使用されます。In the nullable case, additional checks for temp1.HasValue and temp2.HasValue are used. たとえば、は nullableT1 == nullableT2 として評価さ temp1.HasValue == temp2.HasValue ? (temp1.HasValue ? ... : true) : false れます。For instance, nullableT1 == nullableT2 evaluates as temp1.HasValue == temp2.HasValue ? (temp1.HasValue ? ... : true) : false.

要素ごとの比較でブール以外の結果が返された場合 (たとえば、ブール型でないユーザー定義またはが使用されている場合や、 operator == operator != 動的な比較の場合)、その結果はに変換されるか、 bool operator true またはを operator false 取得するためにまたはを実行し bool ます。When an element-wise comparison returns a non-bool result (for instance, when a non-bool user-defined operator == or operator != is used, or in a dynamic comparison), then that result will be either converted to bool or run through operator true or operator false to get a bool. タプルの比較では、常にが返さ bool れます。The tuple comparison always ends up returning a bool.

C# 7.2 で error CS0019: Operator '==' cannot be applied to operands of type '(...)' and '(...)' は、ユーザー定義がない限り、このようなコードではエラー () が生成され operator== ます。As of C# 7.2, such code produces an error (error CS0019: Operator '==' cannot be applied to operands of type '(...)' and '(...)'), unless there is a user-defined operator==.

詳細情報Details

==(または) 演算子をバインドする場合 != 、既存の規則は (1) 動的なケース、(2) オーバーロードの解決、および (3) は失敗します。When binding the == (or !=) operator, the existing rules are: (1) dynamic case, (2) overload resolution, and (3) fail. この提案は、(1) と (2) の間に組の大文字と小文字を追加します。比較演算子の両方のオペランドがタプル (タプル型またはタプルリテラル) で、一致するカーディナリティがある場合、比較は要素ごとに実行されます。This proposal adds a tuple case between (1) and (2): if both operands of a comparison operator are tuples (have tuple types or are tuple literals) and have matching cardinality, then the comparison is performed element-wise. この組の等値は、null 許容の組にもリフトされています。This tuple equality is also lifted onto nullable tuples.

両方のオペランド (および組リテラルの場合は、その要素) が左から右に評価されます。Both operands (and, in the case of tuple literals, their elements) are evaluated in order from left to right. 要素の各ペアは == 、演算子 (または) を再帰的にバインドするオペランドとして使用され != ます。Each pair of elements is then used as operands to bind the operator == (or !=), recursively. コンパイル時の型を持つすべての要素で dynamic エラーが発生します。Any elements with compile-time type dynamic cause an error. これらの要素ごとの比較の結果は、条件演算子と (または) 演算子のチェーンでオペランドとして使用されます。The results of those element-wise comparisons are used as operands in a chain of conditional AND (or OR) operators.

たとえば、のコンテキストでは、 (int, (int, int)) t1, t2; t1 == (1, (2, 3)) はとして評価さ temp1.Item1 == temp2.Item1 && temp1.Item2.Item1 == temp2.Item2.Item1 && temp1.Item2.Item2 == temp2.Item2.Item2 れます。For instance, in the context of (int, (int, int)) t1, t2;, t1 == (1, (2, 3)) would evaluate as temp1.Item1 == temp2.Item1 && temp1.Item2.Item1 == temp2.Item2.Item1 && temp1.Item2.Item2 == temp2.Item2.Item2.

組リテラルがオペランドとして (両側で) 使用される場合、演算子 (または) をバインドするときに導入される要素ごとの変換によって形成される、変換されたタプル型を受け取り == != ます。When a tuple literal is used as operand (on either side), it receives a converted tuple type formed by the element-wise conversions which are introduced when binding the operator == (or !=) element-wise.

たとえば、で (1L, 2, "hello") == (1, 2L, null) は、両方の組リテラルの変換後の型がで (long, long, string) あり、2番目のリテラルには自然型がありません。For instance, in (1L, 2, "hello") == (1, 2L, null), the converted type for both tuple literals is (long, long, string) and the second literal has no natural type.

分解と組への変換Deconstruction and conversions to tuple

では (a, b) == xx 2 つの要素に分解ことができるという事実は、役割を果たしません。In (a, b) == x, the fact that x can deconstruct into two elements does not play a role. 将来の提案になる可能性もありますが、に関する質問が発生します (これは単純な比較であるか、 x == y 要素ごとの比較であり、カーディナリティを使用している場合)。That could conceivably be in a future proposal, although it would raise questions about x == y (is this a simple comparison or an element-wise comparison, and if so using what cardinality?). 同様に、タプルへの変換はロールを持ちません。Similarly, conversions to tuple play no role.

タプル要素名Tuple element names

タプルリテラルを変換するときに、リテラルに明示的なタプル要素名が指定されているが、ターゲットのタプル要素名と一致しない場合に警告します。When converting a tuple literal, we warn when an explicit tuple element name was provided in the literal, but it doesn't match the target tuple element name. 組の比較でも同じルールを使用しているので、での警告を想定 (int a, int b) t d して t == (c, d: 0) います。We use the same rule in tuple comparison, so that assuming (int a, int b) t we warn on d in t == (c, d: 0).

Bool 以外の要素ごとの比較結果Non-bool element-wise comparison results

要素ごとの比較が組の等価性で動的である場合は、演算子の動的呼び出しを使用してを取得し、 false bool さらに要素ごとの比較を続行します。If an element-wise comparison is dynamic in a tuple equality, we use a dynamic invocation of the operator false and negate that to get a bool and continue with further element-wise comparisons.

要素ごとの比較によってタプル内の他の非 bool 型が返される場合は、次の2つのケースがあります。If an element-wise comparison returns some other non-bool type in a tuple equality, there are two cases:

  • ブール型以外の型がに変換される場合、 bool その変換が適用されます。if the non-bool type converts to bool, we apply that conversion,
  • そのような変換がないが、型に演算子がある場合は、それ false を使用して結果を否定します。if there is no such conversion, but the type has an operator false, we'll use that and negate the result.

組の非等値では、 true 演算子の代わりに演算子 (否定なし) を使用する点を除いて、同じ規則が適用され false ます。In a tuple inequality, the same rules apply except that we'll use the operator true (without negation) instead of the operator false.

これらの規則は、 if ステートメントとその他の既存のコンテキストでブール型以外の型を使用する場合に関係する規則に似ています。Those rules are similar to the rules involved for using a non-bool type in an if statement and some other existing contexts.

評価順序と特別なケースEvaluation order and special cases

左側の値が最初に評価され、次に右側の値が評価されます。次に、左から右 (変換を含む) と、条件付き演算子または OR 演算子の既存の規則に基づく早期終了を含む要素ごとの比較が行われます。The left-hand-side value is evaluated first, then the right-hand-side value, then the element-wise comparisons from left to right (including conversions, and with early exit based on existing rules for conditional AND/OR operators).

たとえば、型から型への変換と、メソッドへの変換がある場合、 A B (A, A) GetTuple() を評価すると次のように (new A(1), (new B(2), new B(3))) == (new B(4), GetTuple()) なります。For instance, if there is a conversion from type A to type B and a method (A, A) GetTuple(), evaluating (new A(1), (new B(2), new B(3))) == (new B(4), GetTuple()) means:

  • new A(1)
  • new B(2)
  • new B(3)
  • new B(4)
  • GetTuple()
  • 次に、要素ごとの変換と比較および条件付きロジックが評価されます (型に変換し、 new A(1) B それをと比較し new B(4) ます)。then the element-wise conversions and comparisons and conditional logic is evaluated (convert new A(1) to type B, then compare it with new B(4), and so on).

比較 null``nullComparing null to null

これは、通常の比較の特殊なケースで、タプルの比較に引き継がれます。This is a special case from regular comparisons, that carries over to tuple comparisons. null == null比較は許可され、 null リテラルは型を取得しません。The null == null comparison is allowed, and the null literals do not get any type. タプルの等価性では、 (0, null) == (0, null) も許可され、 null との組リテラルは型を取得しません。In tuple equality, this means, (0, null) == (0, null) is also allowed and the null and tuple literals don't get a type either.

Null 許容の構造体とを比較した場合の比較 null``operator==Comparing a nullable struct to null without operator==

これは、タプルの比較に関する別の特殊なケースです。This is another special case from regular comparisons, that carries over to tuple comparisons. が指定されていない場合、 struct S operator== 比較は許可され、とし (S?)x == null て解釈され ((S?).x).HasValue ます。If you have a struct S without operator==, the (S?)x == null comparison is allowed, and it is interpreted as ((S?).x).HasValue. タプルの等価性では、同じルールが適用されるため、 (0, (S?)x) == (0, null) が許可されます。In tuple equality, the same rule is applied, so (0, (S?)x) == (0, null) is allowed.

互換性Compatibility

ValueTuple比較演算子の実装によって独自の型を記述した場合は、オーバーロードの解決によって以前に取得されていた可能性があります。If someone wrote their own ValueTuple types with an implementation of the comparison operator, it would have previously been picked up by overload resolution. ただし、新しい組のケースはオーバーロードの解決の前にあるため、ユーザー定義の比較に頼るのではなく、タプルの比較でこのケースを処理します。But since the new tuple case comes before overload resolution, we would handle this case with tuple comparison instead of relying on the user-defined comparison.


関係演算子および型テスト演算子に関連する#190Relates to relational and type testing operators Relates to #190