[C#] 왜 ‘&’가 아닌 ‘&&’입니까?

&&바람직 &하고 ||바람직 |합니까?

나는 수년간 프로그래밍을 해 온 누군가에게 물었고 그의 설명은 다음과 같습니다.

예를 들어,에 if (bool1 && bool2 && bool3) { /*DoSomething*/ }, bool1시험에 대한 진실이어야 bool2로 이동하기 전에 사실로 가지고 bool3내가 하나를 사용했던 경우 등, &대신 그들 모두에 충실 할 경우에도 시험에는 순서가 없다 다음 줄로 나아가면 어쨌든 중요한가요?

참고 : 나는 유아와 동등한 프로그래밍이고 이것은 심각하거나 긴급한 질문이 아니라는 것을 지적하고 싶습니다. 왜 일이 다른 사람과 반대로 특정 방식으로 이루어져야 하는지를 이해하는 것이 더 중요합니다.



답변

대부분의 경우, &&||이상 선호 &하고 |이전하기 때문에 평가는 즉시 결과를 알 수있는 바와 같이 취소 즉, 단락이다.

예:

if(CanExecute() && CanSave())
{
}

CanExecute반환 하면 의 반환 값에 관계없이 false전체 표현식이됩니다 . 이 때문에 실행되지 않습니다.falseCanSaveCanSave

이것은 다음과 같은 상황에서 매우 편리합니다.

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValuefalse제공된 키가 사전에 없으면를 반환 합니다. 때문에의 단락 특성 &&, value.Contains("test")경우에만 실행됩니다 TryGetValue반환 true따라서와는 value아닙니다 null. 비트 AND 연산자를 &대신 사용 NullReferenceException하면 식의 두 번째 부분이 실행되므로 사전에 키를 찾을 수 없으면 if가 표시됩니다.

이와 비슷하지만 더 간단한 예는 다음 코드입니다 (TJHeuvel에서 언급 한대로).

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecuteop아닌 경우에만 실행됩니다 null. 경우 op이며 null, 식의 첫 번째 부분은 ( op != null)로 평가 false하고, 나머지의 평가 ( op.CanExecute()) 스킵된다.

이 외에도에서은, 기술적으로, 그들도, 다른 :
&&||에서만 사용할 수있는 bool반면, &|모든 정수 계열 형식 (사용할 수 있습니다 bool, int, long, sbyte그들이 비트 연산자이기 때문에, …). &는 IS 비트 AND 연산자와 |는 IS 비트 OR 연산자.

매우 정확하게 말하자면 C #에서 이러한 연산자 ( &, |[및 ^])를 “논리 연산자”라고합니다 ( C # 사양 , 장 7.11 참조). 이러한 연산자에는 여러 가지 구현이 있습니다.

  1. 정수의 ( int, uint, longulong, 제 7.11.1)
    들은 피연산자 오퍼레이터의 비트 결과를 계산하도록 구현되고, 즉, &비트 단위 논리 계산하도록 구현이다 AND등등
  2. 열거 형의 경우 (7.11.2 장) :
    기본 형식의 열거 형의 논리적 작업을 수행하도록 구현됩니다.
  3. 부울 및 널 입력 가능 부울 (7.11.3 및 7.11.4 장)의 경우 :
    비트 단위 계산을 사용하여 결과가 계산되지 않습니다. 결과는 가능성이 너무 적기 때문에 기본적으로 두 피연산자의 값을 기반으로 조회됩니다.
    두 값 모두 조회에 사용되므로이 구현은 단락되지 않습니다.

답변

이것이 의미하는 바를 명확하게 설명하기 위해 (다른 답변이 암시하더라도-아마도 이해하지 못하는 용어를 사용하십시오).

다음 코드 :

if (a && b)
{
   Foo();
}

실제로 이것으로 컴파일됩니다 :

if (a)
{
    if (b)
    {
        Foo();
    }
}

다음 코드는 표시된 그대로 정확하게 컴파일됩니다.

if (a & b)
{
   Foo();
}

이것을 단락이라고합니다. 일반적으로 항상 &&그리고 ||귀하의 조건에서 사용해야 합니다.

보너스 마크 : 하지 말아야 할 한 가지 시나리오가 있습니다. 만약 성능이 결정적인 상황에 있다면 (이것은 나노초 결정적입니다 ) 단락 null은 분기 / 점프이므로 반드시 (예 : 점검) 단락을 사용하십시오 . CPU에서 분기 오류가 발생할 수 있습니다. 는 &보다 훨씬 저렴합니다 &&. 단락이 실제로 논리를 깰 수있는 시나리오도 있습니다 . 이 답변 을 살펴보십시오 .

Diatribe / Monologue : 가장 행복하게 무시하는 가지 잘못 예측에 대해 Andy Firth 인용 (13 년 동안 게임을해온 사람) : “이것은 사람들이 갈 필요가 있다고 생각하는 수준이 낮을 수 있지만 잘못되었을 것입니다. 프로그래밍하는 하드웨어가 지점을 어떻게 처리 할 수 ​​있는지 이해 대부분의 프로그래머가 re : death of a 1,000 cuts보다 훨씬 더 감사 할 것입니다. “

  • 게임 개발자 (및 극단적 인 실시간 조건에서 작업하는 다른 사람들)는 예측 변수에 더 적합하도록 논리를 재구성하는 것까지 진행합니다. 디 컴파일 된 mscorlib 코드에도 이러한 증거가 있습니다.
  • .NET이 이러한 유형의 것을 차단한다고해서 중요하지 않다는 의미는 아닙니다. 브랜치 잘못 예측은 60Hz에서 엄청나게 비싸다. 또는 10,000 요청 / 초
  • 인텔은 잘못된 예측 위치를 식별 할 수있는 도구가 없거나 Windows에 이에 대한 성능 카운터가 없거나 문제를 설명 할 단어가 없습니다.
  • 낮은 수준과 아키텍처에 대한 무지가 자신을 잘 아는 사람을 만들지는 않습니다.
  • 작업중인 하드웨어의 한계를 항상 이해하십시오.

다음은 비신자에 대한 벤치 마크입니다. https://gist.github.com/1200737에 영향을 미치는 스케줄러를 완화하기 위해 RealTime / High에서 프로세스를 실행하는 것이 가장 좋습니다.


답변

논리 연산자 ( ||&&)와 비트 연산자 ( |&).

논리 연산자와 비트 연산자의 가장 중요한 차이점은 논리 연산자는 두 개의 부울을 취하고 부울을 생성하는 반면 비트 연산자는 두 개의 정수를 취하고 정수를 생성한다는 것입니다 (참고 : 정수는 int뿐만 아니라 모든 정수 데이터 유형을 의미 함).

반복적으로 비트 단위 연산자는 비트 패턴 (예 : 01101011)을 가져와 각 비트에서 비트 단위 AND / OR을 수행합니다. 예를 들어 두 개의 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

둘째, true와 false가 각각 1과 0과 같기 때문에 bool에서 비트 연산자를 사용하는 것이 종종 가능하며, true를 1로 변환하고 0을 0으로 변환하면 비트 연산을 수행 한 다음 0이 아닌 값을 변환합니다 true로, 0에서 false로; 논리 연산자를 방금 사용한 경우 결과가 동일하게됩니다 (연습을 위해 이것을 점검하십시오).

또 다른 중요한 차이점은 논리 연산자가 단락 되었다는 것 입니다. 따라서 일부 서클에서 [1] 사람들은 종종 다음과 같은 일을하는 것을 보게됩니다.

if (person && person.punch()) {
    person.doVictoryDance()
}

어떤로 변환 : “사람이 (즉, null이 아닌)이있는 경우, 그에게 / 그녀에게 펀치를 시도하고 펀치가 (사실 즉, 반환) 성공하면, 다음 승리의 춤을” .

비트 연산자를 대신 사용했다면 다음과 같습니다.

if (person & person.punch()) {
    person.doVictoryDance()
}

에 변환합니다 : “사람이 존재하는 경우 (즉, null이 아닌)과 펀치 (사실, 즉 반환), 다음 승리의 춤을 성공한다” .

단락 된 논리 연산자에서 널 (null) 인 person.punch()경우 코드가 전혀 실행되지 않을 수 있습니다 person. 실제로,이 특정한 경우, 두 번째 코드는 사람이 null인지 여부에 관계없이 person호출하려고하기 때문에 널 (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에서
2 = 참 (비 – 제로)에서 C

trueANDS 논리적으로 true에주고 true.

그러나

1 & 2
이진에서 1 = 0001
2 이진에서 0010
이므로 = 0 = false

0001 AND는 0010과 비트 단위로 10 진수로 0000 = 0을 제공합니다.

마찬가지로 || 와 | 연산자도 …!


답변

&&의 단락 버전입니다 &.

우리가 평가 false & true하고 있다면 , 우리는 이미 결과가 거짓이라는 첫 번째 주장을 살펴 보는 것을 알고 있습니다. &&운영자의 버전은 전체 표현식을 평가하는 것이 아니라, 즉시이 할 수있는 결과를 반환합니다. |연산자 의 유사한 버전도 있습니다 ||.


답변

if (list.Count() > 14 && list[14] == "foo")

는 안전하다

if (list.Count() > 14 & list[14] == "foo")

목록의 크기가 맞지 않으면 충돌이 발생합니다.