条件判定のあれこれ

条件判定のためには以下のような機能が使用できます。

bool型

bool型は条件判定で頻繁に用いられるデータ型です。
bool型はtruefalseのどちらかの値のみを保存できるデータ型です。

trueはともいい、条件が成立することを意味します。
falseはともいい、条件が成立しないことを意味します。


static void Main(string[] args)
{
    string str = "abc";

    //bool型変数
    bool isNotEmpty = str.Length > 0;

    if(isNotEmpty)
    {
        Console.WriteLine("isNotEmptyは真です。");
    }
    else
    {
        Console.WriteLine("isNotEmptyは偽です。");
    }
    Console.WriteLine(isNotEmpty);

    Console.ReadLine();
}
isNotEmptyは真です。
True

6行目、今までif文の条件式で行っていた処理を、bool型の変数に格納しています。
「str.Length > 0」という式が成立するか否かを判定した結果が変数isNotEmptyに格納されることになります。

bool型変数はそれ単体で「真」「偽」のどちらかを表すので、if文などの条件判定式にそのまま記述することができます。
もちろん、必要ならば他の値と比較して条件判定をすることもできます。


string str1 = "abc";
string str2 = "def";
bool b1 = str1.Length > 0;
bool b2 = str2.Length > 0;

if (b1 == b2)
{
    Console.WriteLine("b1とb2の真偽値は同じです。");
}
else
{
    Console.WriteLine("b1とb2の真偽値は異なります。");
}

bool型変数には「true」または「false」の値を直接代入することもできます。


bool b1 = true;
bool b2 = false;

条件判定のための演算子

等値演算子、関係演算子

値の等価や数値の大小の判定を行うには等値演算子関係演算子という演算子を使用します。

a == b
aとbは等しい
a != b
aとbは等しくない
a > b
aはbより大きい
a >= b
aはbと等しいか、大きい
a < b
aはbより小さい
a <= b
aはbと等しいか、小さい

数学では等号(イコール記号)は等しいことを表しますが、C#では代入のための記号となっています。
値が等しいことをチェックするにはイコール記号を二つ連続で記述します。

「!=」はノットイコールと言い、両者が等しくない場合に成立します。
数学では「≠」という記号ですが、これは全角文字なので多くのプログラミング言語では使用しません。

大なり小なり記号は数学のものと同じです。
ただし大なりイコール「≧」、小なりイコール「≦」は全角文字なため、それぞれ「>=」「<=」と二文字で表します。

文字列の比較

文字列の比較は等値演算子(==、!=)が使用可能です。
このとき大文字と小文字とは別の文字列と判定されるので注意してください。
大文字小文字を区別せずに比較したい場合は専用のメソッドを使用します。
(詳しくは文字列の操作 - Equals参照)


string str = "abc";

if (str == "ABC") //偽
    Console.WriteLine("変数strは文字列ABCと一致する");
else
    Console.WriteLine("変数strは文字列ABCと一致しない");

//大文字小文字を無視して比較
if (string.Equals(str, "ABC", StringComparison.OrdinalIgnoreCase)) //真
    Console.WriteLine("変数strは文字列ABCと一致する");
else
    Console.WriteLine("変数strは文字列ABCと一致しない");
変数strは文字列ABCと一致しない
変数strは文字列ABCと一致する

小数の誤差に注意

小数を扱うデータ型(float、double)に等価演算子を使用する場合、計算誤差に注意する必要があります。


static void Main(string[] args)
{
    float f = 0;

    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;

    if (f == 1)
    {
        Console.WriteLine("if文実行");
    }
    else
    {
        Console.WriteLine("else文実行");
    }

    Console.ReadLine();
}
else文実行

このコードはfloat型変数に「0.1」を10回加算しているだけです。
「0.1×10」は「1」なのでif文が実行されるように思いますが、else文が実行されてしまいます。
これは小数の計算誤差によりピッタリと「1」にはならないためです。

小数の比較には等価演算子を使用せず、許容誤差をあらかじめ決めておきその範囲内であるかを判定します。
以下のコードは誤差が「0.001」以下ならばif文が実行されます。


static void Main(string[] args)
{
    //許容誤差
    float tolerance = 0.001f;
    
    float f = 0;
    
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    f += 0.1f;
    
    if(Math.Abs(f) - 1 < tolerance)
    {
        Console.WriteLine("if文実行");
    }
    else
    {
        Console.WriteLine("else文実行");
    }

    Console.ReadLine();
}
if文実行

Math.Absは差の絶対値を得るメソッドです。

論理演算子

一つのif文で複数の条件の判定をする場合は論理演算子を使用します。

a && b
aとbの両方が真の場合に真を返す
(論理積)
a || b
aまたはbが真の場合に真を返す
(論理和)
!a
aが偽の場合に真を返す
(論理否定)

論理積(&&)


string str1 = "ab";
string str2 = "abcde";

if(str1.Length > 0 && str2.Length > 0)
{
    Console.WriteLine("str1とstr2は空文字ではありません。");
}
else
{
    Console.WriteLine("str1かstr2、あるいは両方が空文字です。");
}
str1とstr2は空文字ではありません。

このサンプルコードでは、「str1.Length > 0(条件式A)」と「str2.Length > 0(条件式B)」の二つの条件を一度に判定しています。

論理積(&&)は、条件式Aと条件式Bの両方が真の場合に真を返します。
どちらかが偽の場合、あるいは両方が偽の場合に偽を返します。

論理和(||)


string str1 = "ab";
string str2 = "";

if(str1.Length > 0 || str2.Length > 0)
{
    Console.WriteLine("str1とstr2の少なくとも一方は空文字ではありません。");
}
else
{
    Console.WriteLine("str1とstrは両方が空文字です。");
}
str1とstr2の少なくとも一方は空文字ではありません。

論理和(||)は、条件式Aと条件式Bの少なくとも一方が真の場合に真を返します。
両方が偽の場合に偽を返します。

論理否定(!)


string str = "";

if(!(str.Length > 0))
{
    Console.WriteLine("strは空文字です。");
}
else
{
    Console.WriteLine("strは空文字ではありません。");
}
strは空文字です。

論理否定(!)は、条件判定の結果を反転させます。
サンプルコードの「str.Length > 0」の条件式は偽となりますが、その結果を否定によって反転させているため、ifブロックが実行されます。

論理否定の注意点

論理否定は演算子の位置に注意してください。
以下のコードは間違いです。


string str = "abc";

if (!str.Length > 0)
{
}

論理否定演算子は関係演算子よりも優先度が高いため、これは「str.Length」を否定することになってしまいます。
このコードでは論理否定演算子を使用できない式(ただのint型)に使用しているのでコンパイルエラーになりますが、真偽判定できる式の場合はエラーにはならず、意図しない判定になるので気づきにくいバグになります。


bool b1 = true;
bool b2 = false;

if (!b1 && b2)
{
    //b1がfalse、b2がtrueの場合に実行される
}

if (!(b1 && b2))
{
    //b1とb2の一方または両方がfalseの場合に実行される
}

4行目のif文は、bool型変数b1の真偽値を反転させたものとbool型変数b2の両方が真の場合に成立します。
つまりb1はfalse、b2はtrueの場合に成立します。

9行目のif文は、b1とb2論理積の結果を反転します。
つまりどちらか一方または両方が偽の場合に成立します。

見た目は似ていますが、条件の判定方法が全く異なります。
例えば以下のように書いても文法的には正しく、それでいてどういう判定になるのかが分かりにくいので注意が必要です。


if (! b1&&b2)
{
    //b1がfalse、b2がtrueの場合に実行される
}

if (!(b1 &&b2))
{
    //b1とb2の一方または両方がfalseの場合に実行される
}

Visual Studioならば、行末にセミコロンを書くなどすれば自動的に書式を整えてくれます。
書式を整えるコマンドもありますが、一行程度ならばこちらのほうが速いです。


if (! b1&&b2)
{}

//↓行末にセミコロンを書くと書式が整う

if (!b1 && b2) ;
{}

//↓セミコロンを削除

if (!b1 && b2)
{}

ショートサーキット(短絡評価)

論理積、論理和を使用する場合、C#ではショートサーキット(短絡評価)という仕組みが採用されています。

例えば以下のような条件式があるとします。


bool a = false;
bool b = true;

if (a && b)
{}

論理積(a && b)は式aと式bの両方が真であれば条件式が成立します。
ということは、式aが偽ならば式bが真でも偽でも関係なくこの条件式は偽となります。
つまり、式aの判定の時点で偽ならば後の式bの条件判定はしなくても良く、偽を返せば良いことになります。
このような場合に後の式の判定を省略するのがショートサーキットです。

同じことは論理和でも起こります。


bool a = true;
bool b = true;

if (a || b)
{}

論理和(a || b)は式aと式bの少なくともどちらか一方が真であれば条件式が成立します。
ということは、式aが真ならば式bが真でも偽でも関係なくこの条件式は真となります。
つまり、式aの判定の時点で真ならば後の式bの条件判定はしなくても良く、真を返せば良いことになります。

ショートサーキットにしない

ショートサーキットは処理の効率化のための仕組みですが、ふたつ目の条件式が実行されない可能性があることを考慮する必要があります。
ひとつ目の条件の真偽に関わらずふたつ目の条件式も実行するには以下のようにします。


if (a & b)
{ }

if (a | b)
{ }

「&&」「||」と記号を二つ連続させていたところを、それぞれ「&」「|」と一回のみにすれば式aも式bも両方実行した上で条件が判定されます。