条件判定のあれこれ

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

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 (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と一致しない
変数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.WriteLine(f);
    Console.ReadLine();
}
else文実行
1.0000001

このコードは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の両方が真の場合に真を返す
(条件付き論理AND)
a || b
aまたはbが真の場合に真を返す
(条件付き論理OR)
!a
aが偽の場合に真を返す
(論理否定)

条件付き論理AND、条件付き論理ORは単に論理AND論理ORと呼ばれることもあります。
また論理ANDは論理積、論理ORは論理和と呼ばれることもあります。

条件付き論理AND(&&)


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)の二つの条件を一度に判定しています。

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

条件付き論理OR(||)


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

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

条件付き論理OR(||)は、条件式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文は、b1b2論理ANDの結果を反転します。
つまりどちらか一方または両方が偽の場合に成立します。

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


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

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

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

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

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


bool a = false;
bool b = true;

if (a && b)
{}

条件付き論理AND(a && b)は式aと式bの両方が真であれば条件式が成立します。
ということは、式aが偽ならば式bが真でも偽でも関係なくこの条件式は偽となります。
つまり、式aの判定の時点で偽ならば後の式bの条件判定はしなくても良く、条件式全体の評価値は偽を返せば良いことになります。

このような場合に後の式の判定を省略するのがショートサーキットです。

同じことは条件付き論理和でも起こります。


bool a = true;
bool b = true;

if (a || b)
{}

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

これらの論理演算子は短絡論理AND演算子、短絡論理OR演算子とも呼ばれます。

ショートサーキットの注意点

ショートサーキットは処理の効率化のための仕組みですが、ふたつ目の条件式が実行されない可能性があることを考慮する必要があります。


internal class Program
{
    static void Main(string[] args)
    {
        int num1, num2;

        if ((num1 = Init1()) > 0 && (num2 = Init2()) > 0)
        {
            Console.WriteLine("初期化完了");
        }

        Console.WriteLine(num1);
        Console.WriteLine(num2); //エラー
    }

    static int Init1()
    {
        //何らかの処理...

        return 1;
    }

    static int Init2()
    {
        //何らかの処理...

        return 2;
    }
}

このif文の条件式の処理は、まずInit1メソッドを実行し、その戻り値を変数num1に格納し、その戻り値が1以上(0より大きい)なら||の左辺の条件式は真になります。
同じことをInit2メソッドと変数num2にも行い、両方が真ならばこのif文は実行されます。

このとき、Init1が0以下を返すと、||の右辺の条件式は実行されません。
つまりInit2メソッドも実行されず、変数num2への値の代入も行われません。
変数num2は初期化されないかもしれないので、その後に使用しようとするとエラーになります。

ひとつ目の条件式の真偽に関わらずふたつ目の条件式も実行するには以下のようにします。


if (a & b)
{ }

if (a | b)
{ }

&&||と記号を二つ連続させていたところを、それぞれ&|とひとつにすれば、式aも式bも両方実行した上で条件式全体が判定されます。
これらはブール演算の論理AND演算子、ブール演算の論理OR演算子と呼ばれます。
単に「論理AND」「論理OR」と呼ぶ場合、どちらの意味なのかは文脈によります。

C言語等では&&||を論理積演算子(論理AND演算子)、論理和演算子(論理OR演算子)と呼びます。
&|はビット単位の論理積演算子、ビット単位の論理和演算子と呼びます。

C#でも同じ記号(&|)をビット演算に使用します。

代入式の評価値

変数への代入式も「式」なので、それを評価すると値を得ることができます。
評価値は、代入れた値です。
これを利用して、以下のような条件判定も可能です。


static int M() { return 1; }

static void Main(string[] args)
{
    int n;

    if ((n = 5) > 0) { }
    if ((n = M()) > 0) { }
}    

これはどちらも、変数nに値を代入し、その代入した値が0より大きいならif文が実行されます。

代入式自体は丸括弧で囲う必要があります。
これは代入演算子よりも比較演算子(等値演算子、関係演算子)のほうが優先度が高いので、演算の順序を変えないと正しい意味にならないからです。


if (n = 5 > 0) { }

上記の式は、まず5 > 0が先に演算され、その判定結果が変数nに代入されます。
比較演算子の演算結果はbool型なので、int型変数nには代入できずコンパイルエラーになります。

以下の場合は、それぞれのif文の実行結果でbool型変数bに代入される値が変わることになります。


bool b;
if ((b = false) == false) { }   //bはfalse
if (b = false == false) { }     //bはtrue