なぜ_&&
_は_&
_よりも、_||
_は_|
_よりも望ましいのですか?
私は何年もプログラミングをしている人に尋ねましたが、彼の説明は:
たとえば、if (bool1 && bool2 && bool3) { /*DoSomething*/ }
では、_bool1
_は、_bool2
_に移行する前にtrueでなければならない_bool3
_をテストするためにtrueにする必要があります。 dは単一の_&
_を使用しましたが、次の行に進むためにすべてがtrueである必要がある場合でも、テストの順序はありません。
注:私は幼児に相当するプログラミングであり、これは深刻な問題でも緊急の問題でもないことを指摘したいと思います。それは、物事が別の方法ではなく特定の方法で行われるべき理由を理解することの問題です。
これが何を意味するのかを非常に明確に説明する(他の答えが示唆しているとしても-おそらくあなたが理解していない用語を使用している)。
次のコード:
if (a && b)
{
Foo();
}
本当にこれにコンパイルされます:
if (a)
{
if (b)
{
Foo();
}
}
以下のコードは、表示されているとおりに正確にコンパイルされます。
if (a & b)
{
Foo();
}
これは短絡と呼ばれます。一般的に、あなたは常にあなたの条件で&&
と||
を使用すべきです。
ボーナスマーク:すべきでないシナリオが1つあります。パフォーマンスが重要な状況にある場合(およびこれがナノ秒が重要である場合)、必要な場合にのみ短絡を使用してください(例:null
チェック)-短絡は分岐/ジャンプであるため;これにより、CPUの分岐予測が失敗する可能性があります。 &
は&&
よりもはるかに安価です。短絡が実際にロジックを壊す可能性のあるシナリオもあります-私の この答え を見てください。
Diatribe/Monologue:最も喜んで無視するブランチの予測ミスについて。引用 Andy Firth (13年間ゲームに取り組んできました): "これは、人々が彼らが行く必要があると思うより低いレベルかもしれません...しかし、彼らは間違っているでしょう。 「御branches走の枝のためのプログラミングは、パフォーマンスに大きな影響を与える可能性があります。ほとんどのプログラマーが再評価するよりもはるかに多く、1000カットで死に至ります。」
ここに非信者のベンチマークがあります。スケジューラーの効果を緩和するには、プロセスをRealTime/Highで実行するのが最善です: https://Gist.github.com/1200737
論理演算子(_||
_および_&&
_)対ビット演算子(_|
_および_&
_)。
論理演算子とビット演算子の最も重要な違いは、論理演算子が2つのブール値を生成してブール値を生成を取り、ビット演算子が2つの整数を生成して整数値を生成(注:整数は、intだけでなく、あらゆる整数データ型を意味します。
論理的になるために、ビット演算子はビットパターン(例:01101011)を取り、各ビットでビット単位のAND/ORを実行します。したがって、たとえば、2つの8ビット整数がある場合:
_a = 00110010 (in decimal: 32+16+2 = 50)
b = 01010011 (in decimal: 64+ 16+2+1 = 83)
----------------
a & b = 00010010 (in decimal: 16+2 = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)
_
論理演算子はbool
でのみ機能します:
_a = true
b = false
--------------
a && b = false
a || b = true
_
第二に、boolでビット演算子を使用することはしばしば可能です。なぜなら、trueとfalseはそれぞれ1と0に等しいからです。trueを1に、falseを0に変換し、ビット単位の演算を行ってから非ゼロに変換する場合trueに、ゼロからfalseに。論理演算子を使用した場合と同じ結果になることがあります(これを確認してください)。
もう1つの重要な違いは、論理演算子が短絡であることです。したがって、一部のサークル[1]では、次のようなことをしている人がよく見られます。
_if (person && person.punch()) {
person.doVictoryDance()
}
_
"人が存在する場合(nullでない場合)、パンチを試み、パンチが成功する場合(trueを返す場合)、勝利ダンスを実行する"。
代わりにビット演算子を使用した場合、これ:
_if (person & person.punch()) {
person.doVictoryDance()
}
_
「人が存在し(nullではない)、パンチが成功した(trueを返した)場合、勝利ダンスを行います」.
短絡論理演算子では、person
がnullの場合、person.punch()
コードはまったく実行されないことに注意してください。実際、この特定の場合、person
がnullの場合、2番目のコードはnull参照エラーを生成します。これは、人がnullであるかどうかに関係なくperson.punch()
を呼び出そうとするためです。正しいオペランドを評価しないこの動作は、-短絡と呼ばれます。
[1]副作用のある関数呼び出しをif
式の中に入れるプログラマーがいる一方で、他のプログラマーにとっては一般的で非常に便利なイディオムです。
ビット単位演算子は一度に32ビットで動作するため(32ビットマシンを使用している場合)、膨大な数の条件を比較する必要がある場合、よりエレガントで高速なコードを作成できます。
_int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;
Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;
Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;
Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;
CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;
// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`
_
論理演算子で同じことを行うには、扱いにくい量の比較が必要になります。
_Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;
Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;
Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;
CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch = bar.rules.can_punch && person.can_punch,
cloc1.usable_abilities.can_kick = bar.rules.can_kick && person.can_kick,
cloc1.usable_abilities.can_drink = bar.rules.can_drink && person.can_drink,
cloc1.usable_abilities.can_sit = bar.rules.can_sit && person.can_sit,
cloc1.usable_abilities.can_shoot_guns = bar.rules.can_shoot_guns && person.can_shoot_guns,
cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
cloc1.usable_abilities.can_talk = bar.rules.can_talk && person.can_talk;
bool cloc2.usable_abilities.can_punch = military.rules.can_punch && person.can_punch,
cloc2.usable_abilities.can_kick = military.rules.can_kick && person.can_kick,
cloc2.usable_abilities.can_drink = military.rules.can_drink && person.can_drink,
cloc2.usable_abilities.can_sit = military.rules.can_sit && person.can_sit,
cloc2.usable_abilities.can_shoot_guns = military.rules.can_shoot_guns && person.can_shoot_guns,
cloc2.usable_abilities.can_talk = military.rules.can_talk && person.can_talk,
cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;
_
ビットパターンとビット演算子を使用する古典的な例は、Unix/Linuxファイルシステムのアクセス許可です。
の場合:
if (obj != null && obj.Property == true) { }
期待どおりに動作します。
しかし:
if (obj != null & obj.Property == true) { }
null参照例外をスローする可能性がありました。
短くて簡単:
1 && 2
= true
1 = Cでtrue(非ゼロ)
2 = Cでtrue(非ゼロ)
true
は、true
と論理的にANDSして、true
を返します。
しかし
1 & 2
= 0 = false
1 = 0001バイナリ
2 = 0010バイナリ
0001 0010とビット単位のANDをとり、10進数で0000 = 0を返します。
同様に||と|演算子も...!
Ifステートメント_&&
_などの論理式で使用する場合は、最初の偽の結果が検出されるとすぐに式の評価を停止するため、望ましいです。これは、偽の値により式全体が偽になるために可能です。同様に(また論理式でも)_||
_が望ましいのは、真の値があると式全体が真になるため、真の式に遭遇するとすぐに式の評価を停止するからです。
ただし、論理和または論理和をとる式に副作用があり、これらすべてを式の結果として(論理式の結果に関係なく)発生させる場合は、_&
_および_|
_を使用できます。逆に、_&&
_および_||
_演算子は、不要な副作用(例外がスローされる原因となるNULLポインターなど)に対するガードとして役立ちます。
_&
_および_|
_演算子は整数でも使用できます。この場合、2つのオペランドがビットレベルで論理積または論理和された整数結果を生成します。これは、整数値のバイナリビットがtrue値とfalse値の配列として使用される場合に役立ちます。特定のビットがオンかオフかをテストするために、ビットマスクが値ごとにビット単位で論理積されます。ビットをオンにするには、同じマスクを値ごとにビット単位で論理和します。最後に、ビットをオフにするために、マスクのビット単位の補数(_~
_を使用)が値とビット単位の論理積を取ります。
_int a = 0; // 0 means all bits off
a = a | 4; // set a to binary 100
if ((a & 4) != 0) {
// will do something
}
a = a & (~4) // turn bit off again, a is now 000
_
C#以外の言語では、論理モードとビット単位の&および|モードに注意する必要があります。上記のコードでは、if
ステートメントの条件式_(a & 4) != 0
_はこの条件を安全に表現する方法ですが、多くのCのような言語では、条件ステートメントはゼロの整数値をfalseおよび非ゼロとして単純に処理できます整数値が真。 (この理由は、利用可能な条件分岐プロセッサ命令と、整数演算ごとに更新されるゼロフラグとの関係に関連しています。)したがって、ゼロに対する_ìf
_ステートメントのテストを削除して、条件を短縮できます。 _(a & 4)
_に。
これにより、ビット単位の演算子と演算子を使用して組み合わせた式が、整列したビットを持たない値を返す場合、混乱が発生する可能性があります。 2つの関数の副作用が必要な次の例を考えてみましょう。両方の関数が成功したことを確認する前に(ゼロ以外の値を返すことで定義されています):
_if (foo() & bar()) {
// do something
}
_
Cでは、foo()
が1を返し、bar()
が2を返す場合、_1 & 2
_がゼロであるため、「何か」は実行されません。
C#では、if
などの条件ステートメントにブール値のoeprandが必要であり、言語では整数値をブール値にキャストできません。したがって、上記のコードはコンパイラエラーを生成します。より正確に次のように表現されます。
_if (foo() != 0 & bar() != 0) {
// do something
}
_
OK、額面どおり
Boolean a = true;
Boolean b = false;
Console.WriteLine("a({0}) && b({1}) = {2}", a, b, a && b);
Console.WriteLine("a({0}) || b({1}) = {2}", a, b, a || b);
Console.WriteLine("a({0}) == b({1}) = {2}", a, b, a == b);
Console.WriteLine("a({0}) & b({1}) = {2}", a, b, a & b);
Console.WriteLine("a({0}) | b({1}) = {2}", a, b, a | b);
Console.WriteLine("a({0}) = b({1}) = {2}", a, b, a = b);
同じ答えを生成します。しかし、あなたが示したように、より複雑な質問がある場合:
if (a and b and c and d) ..
a
が真ではなく、おそらくb
が機能を停止して、何かに接続し、これを取得し、それを実行し、決定を下す必要がある場合。時間の無駄、yoすでに失敗していることを知っています。なぜマシンをオフにして余分な無意味な作業をするのですか?
私はいつも&&
失敗する可能性が最も高いものを最初に配置したので、無理な場合は先に進む前に計算を減らします。データの出力を制限するブール値など、あまり起こりそうにない選択肢を予測する方法がない場合は、次のようにします。
if (limit && !MyDictionary.ContainsKey("name"))
continue;
limit
でない場合は、キーの確認に煩わされないでください。これには時間がかかります。
&&
は、&
の短絡バージョンです。
false & true
を評価している場合、最初の引数を見れば、結果がfalseになることがすでにわかっています。 &&
バージョンの演算子は、式全体を評価するのではなく、できるだけ早く結果を返します。 |
演算子、||
の同様のバージョンもあります。
if (list.Count() > 14 && list[14] == "foo")
安全です
if (list.Count() > 14 & list[14] == "foo")
リストのサイズが適切でないとクラッシュします。
あなたが昔のCプログラマである場合、注意してください。 C#は本当に驚いた。
[〜#〜] msdn [〜#〜] は|
演算子:
バイナリ|演算子は、整数型およびboolに対して事前定義されています。整数型の場合、|オペランドのビット単位ORを計算します。boolオペランドの場合、|はオペランドの論理ORを計算します。つまり、次の場合にのみ結果はfalseです。両方のオペランドが偽です。
(エンファシスは私のものです。)ブール型は特別に処理されます。このコンテキストでは、質問は意味を持ち始めただけで、違いは、他の回答で既に説明されているとおりです。
&&
および||
は短絡しています。&
および|
評価bothオペランド。
望ましいものは、副作用、パフォーマンス、コードの読みやすさなどの多くのものに依存しますが、一般的に短絡演算子は、私のような同様の背景を持つ人々によってよく理解されるため、好ましいです。
その理由は次のとおりです。私はこのように議論します。Cには真のブール型がないため、ビット演算子|
およびif条件で結果を真実または偽として評価します。しかし、これはC#の間違った態度です。なぜなら、ブール型には既に特別なケースがあるからです。
これを行うときにコードの正確な操作を知る必要がない人にこれを説明する最も簡単な(そして少し馬鹿げた)方法
&&はこれらの各条件でチェックを行いますntilはfalseを見つけ、結果全体をfalseとして返します
は、これらの各条件でチェックを実行していますntilは、trueを検出し、結果全体をtrueとして返します。
&は、両方/すべての条件に基づいてMATHSを実行し、結果を処理しています。
は、両方/すべての条件に基づいてMATHSを実行し、結果を処理しています。
Ifステートメント内で&またはを使用する必要があるポイントに出くわしたことはありません。私は主に、ビット単位のシフトを使用して16進値をコンポーネントの色に切り分けるために使用します。
例えば:
r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;
この操作内で、「&0xFF」はバイナリ値のみを強制的に調べます。私はまだの使用法を見つけていません。
単に、
if exp1 && exp2
exp1がflase
の場合、exp2をチェックしない
しかし
if exp1 & exp2
exp1がfalse
またはtrue
の場合exp2をチェック
まれに&
exp1がfalse
の場合、exp2をチェックすることはめったにないため
&&と&は、2つの非常に異なることを意味し、2つの異なる答えを与えます。
1 && 2
は1(「true」)を生成します1 & 2
は0( "false")を生成します
&&
は論理演算子です。「両方のオペランドがtrueの場合、true」を意味します&
はビット単位の比較です。 「どちらのオペランドが両方のオペランドに設定されているか教えてください」という意味です。
これは重要です。なぜなら、bool2(たとえば)の評価コストが高くてもbool1がfalseの場合、&& over&を使用することでかなりの計算を節約できるからです。
&&
と||
はif/else
と同じようにフロー制御に使用されるためです。必ずしも条件に関するものではありません。 ステートメントとして、をif
またはwhile
条件としてではなく、次のように記述することは完全に合理的です。
a() && b() && c() && d();
あるいは
w() || x() || y() || z();
同等のif/else
バージョンよりも入力しやすいというだけではありません。また、読みやすく理解しやすくなっています。