web-dev-qa-db-ja.com

C#とJavaの三項演算子の違い(?:)

私はC#初心者であり、問​​題が発生しました。 C#とJava三項演算子(? :)を扱う場合には違いがあります。

次のコードセグメントでは、なぜ4行目が機能しないのですか?コンパイラは、there is no implicit conversion between 'int' and 'string'のエラーメッセージを表示します。 5行目も機能しません。両方のListsはオブジェクトではありませんか?

int two = 2;
double six = 6.0;
Write(two > six ? two : six); //param: double
Write(two > six ? two : "6"); //param: not object
Write(two > six ? new List<int>() : new List<string>()); //param: not object

ただし、Javaでも同じコードが機能します。

int two = 2;
double six = 6.0;
System.out.println(two > six ? two : six); //param: double
System.out.println(two > six ? two : "6"); //param: Object
System.out.println(two > six ? new ArrayList<Integer>()
                   : new ArrayList<String>()); //param: Object

C#のどの言語機能が欠落していますか?ある場合、なぜ追加されないのですか?

94
blackr1234

C#5言語仕様セクション7.14:条件演算子を見ると、次のことがわかります。

  • XのタイプがXでyのタイプがYの場合

    • 暗黙的な変換(§6.1)がXからYに存在し、YからXに存在しない場合、Yは条件式のタイプです。

    • 暗黙的な変換(§6.1)がYからXに存在し、XからYに存在しない場合、Xは条件式のタイプです。

    • そうでない場合、式のタイプを判別できず、コンパイル時エラーが発生します

つまり、xとyをotherotherに変換できるかどうかを確認しようとします。変換できない場合は、コンパイルエラーが発生します。この場合、intstringには明示的または暗黙的な変換がないため、コンパイルされません。

これを Java 7言語仕様セクション15.25:条件演算子 と比較してください:

  • 2番目と3番目のオペランドが同じ型(null型の場合もある)である場合、それは条件式の型です。 ([〜#〜] no [〜#〜]
  • 2番目と3番目のオペランドの一方がプリミティブ型Tで、もう一方の型がボクシング変換(§5.1.7)をTに適用した結果である場合、条件式の型はTです。([〜#〜] no [〜#〜]
  • 2番目と3番目のオペランドの一方がNULL型で、もう一方の型が参照型である場合、条件式の型はその参照型になります。 ([〜#〜] no [〜#〜]
  • それ以外の場合、2番目と3番目のオペランドが数値型に変換可能な型(§5.1.8)である場合、いくつかのケースがあります:([〜#〜] no [〜#〜]
  • それ以外の場合、第2および第3オペランドはそれぞれS1およびS2型です。 T1をボクシング変換をS1に適用した結果の型とし、T2をボクシング変換をS2に適用した結果の型とします。
    条件式のタイプは、キャプチャ変換(§5.1.10)をlub(T1、T2)(§15.12.2.7)に適用した結果です。 ([〜#〜] yes [〜#〜]

そして、 セクション15.12.2.7。実際の引数に基づいて型引数を推測する を見ると、ObjectObjectisは受け入れ可能な引数なので、呼び出しは機能します。

106
Jeroen Vannevel

与えられた答えは良いです。私は彼らに、このC#のルールは、より一般的な設計ガイドラインの結果であることを付け加えます。いくつかの選択肢の1つから式のタイプを推測するように求められた場合、C#はそれらのユニークなベストを選択します。つまり、C#に「キリン、哺乳類、動物」などの選択肢を与えると、状況に応じて最も一般的な動物を選択するか、最も具体的なキリンを選択する可能性があります。ただし、実際に与えられた選択肢の1つを選択する必要があります。 C#は「私の選択は猫と犬の間だからです。したがって、動物が最良の選択であると推測します」とは言いません。これは選択肢ではなかったため、C#では選択できません。

三項演算子の場合、C#はintと文字列のより一般的なタイプを選択しようとしますが、どちらもより一般的なタイプではありません。 C#は、オブジェクトのようにそもそも選択ではなかったタイプを選択するのではなく、タイプを推測できないと判断します。

また、これはC#の別の設計原則に沿っていることにも注意してください。何か問題があるように見える場合は、開発者に伝えてください。言語は「あなたが何を意味するかを推測し、できればやり通す」とは言っていません。言語は「あなたはここで混乱させるようなものを書いたと思う、そして私はそれについてあなたに話すつもりだ」と言う。

また、C#はvariableからに割り当てられた値が、むしろ他の方向。 C#は、「オブジェクト変数に割り当てているため、式はオブジェクトに変換可能である必要があるため、それが正しいことを確認します」とは言っていません。むしろ、C#は「この式には型が必要であり、その型がオブジェクトと互換性があることを推測できなければならない」と言っています。式には型がないため、エラーが生成されます。

86
Eric Lippert

ジェネリック部分について:

two > six ? new List<int>() : new List<string>()

C#では、コンパイラは右側の式の部分を一般的な型にconvertしようとします。 List<int>およびList<string>は2つの異なる構築型であり、一方を他方に変換することはできません。

Javaでは、コンパイラーは変換する代わりに共通のスーパータイプを見つけようとします。そのため、コードのコンパイルには ワイルドカード および type erasure ;

two > six ? new ArrayList<Integer>() : new ArrayList<String>()

コンパイルタイプはArrayList<?>(実際には、ArrayList<? extends Serializable>またはArrayList<? extends Comparable<?>>(使用コンテキストに応じて、これらは両方とも共通の汎用スーパータイプであるため)およびランタイムArrayListの実行時タイプ(共通のロースーパータイプであるため)。

たとえば、 (自分でテストする)

void test( List<?> list ) {
    System.out.println("foo");
}

void test( ArrayList<Integer> list ) { // note: can't use List<Integer> here
                                 // since both test() methods would clash after the erasure
    System.out.println("bar");
}

void test() {
    test( true ? new ArrayList<Object>() : new ArrayList<Object>() ); // foo
    test( true ? new ArrayList<Integer>() : new ArrayList<Object>() ); // foo 
    test( true ? new ArrayList<Integer>() : new ArrayList<Integer>() ); // bar
} // compiler automagically binds the correct generic QED
24
Mathieu Guindon

JavaとC#(および他のほとんどの言語)では、式の結果には型があります。三項演算子の場合、結果に対して評価される2つの部分式があり、両方ともJavaの場合、int変数とIntegerの両方が継承するため、オートボクシングによってInteger変数をStringに変換できます。 Objectから、単純な絞り込み変換により同じ型に変換できます。

一方、C#では、intはプリミティブであり、stringまたはその他のobjectへの暗黙的な変換はありません。

6
Code-Apprentice

これは非常に簡単です。 stringとintの間の暗黙的な変換はありません。三項演算子では、最後の2つのオペランドが同じ型である必要があります。

試してください:

Write(two > six ? two.ToString() : "6");
5
ChiralMichael