Menu

C#の三項演算子(?:):構文、型、ネスト、??

C#の条件演算子 condition ? a : b は2つの値から1つを選びます。評価のされ方、両方の分岐に共通の型が必要な理由(CS0173)とそれを直すキャスト、三項演算子のネスト、文字列補間でのかっこ、そして ?? と ?. のほうが適している場面を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

条件演算子は、一般に三項演算子と呼ばれ、1つの式の中で2つの値から1つを選びます:condition ? valueIfTrue : valueIfFalse。

出力:

0 items, shipping 4.99
1 item, shipping 4.99
5 items, shipping 0

条件は bool でなければなりません。条件が評価された後、2つの分岐のうち1つだけが実行され、もう一方は評価されません。そのため、空のリストで list[0] を評価すると例外になる list.Count > 0 ? list[0] : "empty" のような、ガード付きのアクセスにも安全に使えます。

三項演算子とif/else

三項演算子は式です。代入したり、渡したり、返したりする値を生成します。if 文は文を実行します。次の2つのメソッドは同じことをします。

// with if/else
static string Status(int stock)
{
    if (stock > 0)
        return "in stock";
    else
        return "sold out";
}

// with the ternary
static string Status(int stock) => stock > 0 ? "in stock" : "sold out";

両方の分岐が同じ種類の短い値なら三項演算子を使います。分岐が複数の文を実行する、副作用がある、または条件が長くて1行に収まりにくい場合は if を使います。

三項演算子は、動作を実行するだけの if の代わりにはなりません。次のコードはコンパイルできません。

stock > 0 ? Ship(order) : Refund(order);
// error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement

elseのない三項演算子もありません。式はどちらの場合にも値を持たなければならないので、? と : は常に両方必要です。

両方の分岐に共通の型が必要

コンパイラは、2つの分岐から式全体の型を導きます。一方の分岐がもう一方の型に暗黙に変換できなければなりません。どちらも変換できないとCS0173になります。

var discount = isMember ? 10 : null;
// error CS0173: Type of conditional expression cannot be determined because
// there is no implicit conversion between 'int' and '<null>'

var label = inStock ? 1 : "none";
// error CS0173: ... between 'int' and 'string'

直し方は、一方の分岐を目的の型にキャストして、もう一方がその型に変換されるようにすることです。

出力:

no discount
7
3.5

points / 2.0 : 0 にキャストは不要です。0 は int で、double に暗黙に変換されるので、式全体は double になります。

C# 9では対象の型による型付けが追加されました。式が宣言された型に代入されるとき、コンパイラはその型を使います。int? discount = isMember ? 10 : null; はC# 9以降でコンパイルできます。var には対象の型がないので、var discount = isMember ? 10 : null; はどのバージョンでも失敗します。

優先順位とかっこ

条件演算子の優先順位は非常に低く、+、==、&& より低くなっています。a > b ? a : b では便利ですが、文字列の連結の中では意外な結果になります。

Console.WriteLine("Status: " + isActive ? "on" : "off");
// error CS0029: Cannot implicitly convert type 'string' to 'bool'

コンパイラは条件を "Status: " + isActive、つまり文字列として読みます。三項演算子をかっこで囲むと直ります:"Status: " + (isActive ? "on" : "off")。

文字列補間には独自の落とし穴があります。{ } の中ではコロンが書式指定子({price:F2})の始まりになるので、かっこのない三項演算子は解析器を混乱させます。かっこで囲みます。

出力:

2 users online
You have 1 unread message
Status: online

三項演算子のネスト

この演算子は右結合なので、a ? x : b ? y : z は a ? x : (b ? y : z) とまとまります。1行に1つの条件を並べた小さな連鎖なら読みやすくなります。

出力:

512 B
20 KB
5 MB

2段か3段を超える場合や、条件がそれぞれ別のものを判定している場合は、if/else if かswitch式に切り替えます。真の分岐の中でのネスト(a ? (b ? x : y) : z)が、最初に読みにくくなる形です。

?? と ?. でよくある三項演算子を置き換える

多くの三項演算子は null を扱うためだけに存在します。C#にはそのための、より短い演算子があります。

三項演算子短い形意味
name != null ? name : "guest"name ?? "guest"nullのときの代替値
customer != null ? customer.Email : nullcustomer?.Emailnullを許容するメンバーアクセス
c != null && c.Address != null ? c.Address.City : "unknown"c?.Address?.City ?? "unknown"両方の組み合わせ
value != null ? value : throw new ArgumentNullException(...)value ?? throw new ArgumentNullException(...)すぐに失敗させる

出力:

Lena from Berlin
guest from unknown
guest from unknown

throw 式は三項演算子のどちらの分岐にも書けます:int age = input >= 0 ? input : throw new ArgumentOutOfRangeException(nameof(input));。

ref条件式

C# 7.2以降、三項演算子は2つの値ではなく2つの変数から選ぶことができ、その結果を通して代入できます。

// C# 7.2 and later
int wins = 0, losses = 0;
bool won = true;

ref int counter = ref (won ? ref wins : ref losses);
counter++;               // increments wins

両方の分岐は、同じ型の変数への ref でなければなりません。アプリケーションのコードではまれで、主に大きな構造体や配列をコピーせずに扱う、性能が重要なコードのために存在します。

よくある質問

C#の三項演算子とは何ですか?

条件演算子 condition ? valueIfTrue : valueIfFalse のことで、C#で唯一3つのオペランドを持つ演算子です。条件を評価し、2つの値のうちちょうど1つを評価して返します:string label = age >= 18 ? "adult" : "minor";。

C#でelseのない三項演算子は使えますか?

使えません。式は常に値を生成しなければならないので、? と : の両方の部分が必須です。条件が成り立つときだけ何かをしたいなら if 文を使います。三項演算子は単独の文にもなれません:x > 0 ? A() : B(); はエラーCS0201です。

「Type of conditional expression cannot be determined」になるのはなぜですか?

2つの分岐に共通の型がないからです。たとえば var x = found ? 42 : null;(int と null)や found ? 1 : "none" です。型が決まるように一方の分岐をキャストします:found ? (int?)42 : null。C# 9以降は宣言された対象の型でも解決できます:int? x = found ? 42 : null; はコンパイルできますが、var では失敗したままです。

文字列補間の中で三項演算子を使うには?

かっこで囲みます:$"{(count == 1 ? "item" : "items")}"。かっこがないと : が書式指定子の始まりとして読まれ、コンパイルできません。

C#で三項演算子をネストできますか?

できます。この演算子は右結合なので、a ? x : b ? y : z は a ? x : (b ? y : z) の意味になります。1段のネストを別々の行に並べれば読みやすいですが、それより深い連鎖は if/else if の連鎖かswitch式のほうが明確です。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める