Operatory porównania, logiczne i bitowe
Zanim program podejmie jakąkolwiek decyzję, musi coś porównać. Ta lekcja pokazuje, jak zadawać komputerowi pytania typu „czy większe”, „czy równe” i jak łączyć takie pytania w bardziej złożone warunki. Na końcu zaglądamy pod maskę — do operacji na pojedynczych bitach.
Czego się dziś nauczysz
- Zbudujesz wyrażenie logiczne z operatorów porównania
- Porównasz dwa napisy z uwzględnieniem wielkości liter i bez niej
- Połączysz kilka warunków operatorami
&&,||i! - Wyjaśnisz, czym jest ocena skrócona i jak chroni przed błędem
- Uprościsz zaprzeczony warunek prawami De Morgana
- Odczytasz zapis dwójkowy liczby i wykonasz na niej operację bitową
Operatory porównania
Każdy z nich bierze dwie wartości i zwraca true albo false — czyli wartość typu bool.
| Operator | Znaczenie | Przykład | Wynik |
|---|---|---|---|
== | równe | 5 == 5 | true |
!= | różne | 5 != 3 | true |
> | większe | 5 > 3 | true |
< | mniejsze | 5 < 3 | false |
>= | większe lub równe | 5 >= 5 | true |
<= | mniejsze lub równe | 3 <= 5 | true |
int wiek = 17;
bool czyPelnoletni = wiek >= 18; // false
bool czyNastolatek = wiek >= 13; // true
Console.WriteLine(czyPelnoletni); // False
Console.WriteLine(wiek != 17); // False
Console.WriteLine(wiek * 2 > 30); // True — najpierw mnożenie, potem porównanie
Jeden znak równości to co innego niż dwa
= przypisuje wartość. == porównuje. To dwie zupełnie różne operacje i mylenie ich jest klasycznym błędem początkującego.
Dobra wiadomość: w C# ten błąd zwykle nie przejdzie przez kompilator. Zapis if (x = 5) zgłosi błąd, bo przypisanie zwraca liczbę, a warunek wymaga wartości bool. W językach C i C++ taki kod kompiluje się bez słowa i psuje program.
Porównania działają też na znakach
Typ char jest porównywany według numeru znaku w tablicy Unicode. Dlatego 'a' < 'b' daje true, a 'Z' < 'a' również — wielkie litery mają w tej tablicy niższe numery niż małe.
Porównywanie tekstu
Operator == na napisach porównuje zawartość, a nie miejsce w pamięci. To wygodne i działa dokładnie tak, jak się spodziewasz.
string a = "Anna";
string b = "Anna";
string c = "ANNA";
Console.WriteLine(a == b); // True — ta sama treść
Console.WriteLine(a == c); // False — inna wielkość liter!
Wielkość liter ma znaczenie
Dla komputera "Anna" i "ANNA" to dwa różne napisy. Przy porównywaniu danych wpisanych przez użytkownika prawie zawsze chcesz to zignorować — inaczej ktoś, kto wpisze TAK zamiast tak, dostanie zły wynik.
Porównanie ignorujące wielkość liter
string odpowiedz = "TAK";
// Sposób 1 — sprowadzamy oba napisy do małych liter
bool zgoda1 = odpowiedz.ToLower() == "tak";
// Sposób 2 — mówimy wprost, że wielkość liter nas nie interesuje (zalecany)
bool zgoda2 = string.Equals(odpowiedz, "tak", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(zgoda1); // True
Console.WriteLine(zgoda2); // True
| Sposób | Kiedy używać |
|---|---|
.ToLower() == |
Krótszy i czytelniejszy dla początkującego. Zupełnie wystarczy do zadań szkolnych i na egzaminie. |
string.Equals(..., OrdinalIgnoreCase) |
Poprawniejszy: nie tworzy nowego napisu w pamięci i nie zależy od ustawień regionalnych. W niektórych językach — na przykład tureckim — zamiana wielkości liter działa inaczej niż byśmy się spodziewali, a ten zapis jest na to odporny. |
Napisy porównujemy tylko na równość
Operatory < i > nie działają na napisach — kompilator zgłosi błąd. Żeby ustalić, który napis jest wcześniej w kolejności alfabetycznej, używa się metody string.Compare(a, b): zwraca liczbę ujemną, zero albo dodatnią. Wrócimy do niej w bloku o tekstach.
Porównywanie liczb z przecinkiem
Wracamy do problemu z lekcji 06. Typ double zapisuje ułamki w przybliżeniu, więc porównanie na równość bywa zawodne.
double a = 0.1 + 0.2;
double b = 0.3;
Console.WriteLine(a == b); // False ✗
double tolerancja = 0.000001;
Console.WriteLine(Math.Abs(a - b) < tolerancja); // True ✓
| Typ danych | Jak porównywać na równość |
|---|---|
int, long, char, bool | == — bez zastrzeżeń |
decimal | == — bez zastrzeżeń, bo liczy dziesiętnie |
double, float | przez tolerancję: Math.Abs(a - b) < 0.000001 |
string | == albo string.Equals z OrdinalIgnoreCase |
Operatory <, >, <= i >= na typie double działają normalnie — problem dotyczy tylko sprawdzania dokładnej równości.
Operatory logiczne — łączenie warunków
Rzadko wystarcza jeden warunek. Zwykle pytamy o kilka rzeczy naraz: „czy jest pełnoletni i ma prawo jazdy”.
| Operator | Nazwa | Zwraca true, gdy… |
|---|---|---|
&& | koniunkcja (I) | oba warunki są prawdziwe |
|| | alternatywa (LUB) | przynajmniej jeden warunek jest prawdziwy |
! | negacja (NIE) | warunek jest fałszywy — odwraca wartość |
Tabele prawdy
| A | B | A && B | A || B | !A |
|---|---|---|---|---|
| true | true | true | true | false |
| true | false | false | true | false |
| false | true | false | true | true |
| false | false | false | false | true |
Jak to zapamiętać bez tabeli
&& jest wymagające — wystarczy jeden fałsz, żeby całość była fałszywa.
|| jest wyrozumiałe — wystarczy jedna prawda, żeby całość była prawdziwa.
int wiek = 20;
bool maPrawoJazdy = true;
bool maSamochod = false;
// Wszystkie warunki muszą być spełnione
bool mozeProwadzic = wiek >= 18 && maPrawoJazdy;
Console.WriteLine(mozeProwadzic); // True
// Wystarczy jeden
bool maPojazd = maSamochod || wiek < 18;
Console.WriteLine(maPojazd); // False
// Negacja
Console.WriteLine(!maSamochod); // True
// Warunek złożony — przedział
int temperatura = 22;
bool przyjemnie = temperatura >= 18 && temperatura <= 25;
Console.WriteLine(przyjemnie); // True
Przedziału nie zapiszesz tak jak w matematyce
Matematyczny zapis 18 <= t <= 25 nie działa w C#. Kompilator policzyłby najpierw 18 <= t, dostał wartość bool, a potem próbował porównać ją z liczbą 25 — i zgłosił błąd.
Przedział zapisujemy zawsze jako dwa osobne warunki połączone &&: t >= 18 && t <= 25.
Ocena skrócona
C# nie sprawdza warunków dłużej, niż to konieczne. Gdy wynik jest już przesądzony, reszta wyrażenia w ogóle się nie wykonuje.
&& przerywa na pierwszym fałszu
Skoro jeden warunek jest fałszywy, całość na pewno będzie fałszywa. Po co sprawdzać dalej?
|| przerywa na pierwszej prawdzie
Skoro jeden warunek jest prawdziwy, całość na pewno będzie prawdziwa.
To nie jest tylko optymalizacja — to mechanizm, który pozwala pisać bezpieczny kod:
int dzielnik = 0;
int liczba = 100;
// ✓ Bezpiecznie — gdy dzielnik jest zerem, druga część się NIE wykona
bool wynik = dzielnik != 0 && liczba / dzielnik > 10;
Console.WriteLine(wynik); // False, bez żadnego błędu
// ✗ Odwrotna kolejność przerwie program
// bool zle = liczba / dzielnik > 10 && dzielnik != 0;
// └── DivideByZeroException, zanim dojdziemy do sprawdzenia
Zasada praktyczna
Warunek zabezpieczający stawiaj zawsze jako pierwszy. „Czy dzielnik nie jest zerem” przed dzieleniem, „czy tekst nie jest pusty” przed sięganiem po znak. Kolejność warunków w C# ma znaczenie i można ją świadomie wykorzystać.
Istnieją też operatory bez skracania
Pojedyncze & i | na wartościach bool działają podobnie, ale zawsze sprawdzają obie strony. W praktyce prawie nigdy się ich tak nie używa — ich głównym zastosowaniem są operacje bitowe, o których za chwilę. Zapamiętaj: przy warunkach zawsze podwójne && i ||.
Kolejność działań i prawa De Morgana
| Kolejność | Operatory | Uwaga |
|---|---|---|
| 1 | ! | negacja wykonuje się najwcześniej |
| 2 | * / % + - | arytmetyka przed porównaniami |
| 3 | < > <= >= | — |
| 4 | == != | równość po pozostałych porównaniach |
| 5 | && | koniunkcja przed alternatywą |
| 6 | || | — |
bool a = true, b = false, c = true;
// && wiąże mocniej niż ||, więc to wyrażenie...
bool wynik = a || b && c;
// ...kompilator czyta tak:
bool toSamo = a || (b && c); // true
Console.WriteLine(wynik == toSamo); // True
Nawiasy przy mieszaniu && i ||
Gdy w jednym warunku pojawiają się oba operatory, zawsze stawiaj nawiasy — nawet jeśli akurat nie są konieczne. Wyrażenie (wiek >= 18 && maPrawoJazdy) || jestInstruktorem czyta się od razu, a bez nawiasów trzeba się zatrzymać i przypomnieć sobie priorytety.
Prawa De Morgana
Dwie reguły, które pozwalają wnieść zaprzeczenie do środka nawiasu. Przy okazji zamieniają && na || i odwrotnie.
// Prawo pierwsze: !(A && B) == !A || !B
!(pada && zimno) ⟷ !pada || !zimno
// Prawo drugie: !(A || B) == !A && !B
!(weekend || swieto) ⟷ !weekend && !swieto
Po polsku brzmi to zupełnie naturalnie:
| Zapis zaprzeczony | To samo bez nawiasu |
|---|---|
| Nieprawda, że pada i jest zimno | Albo nie pada, albo nie jest zimno |
| Nieprawda, że weekend lub święto | Nie weekend i nie święto |
Po co to komu
Przydaje się, gdy chcesz odwrócić warunek, żeby stał się czytelniejszy. Zamiast if (!(wiek >= 18 && maZgode)) można napisać if (wiek < 18 || !maZgode) — to samo znaczenie, a od razu widać, w jakich sytuacjach warunek jest spełniony.
Operatory bitowe
Do tej pory traktowaliśmy liczbę jako całość. Operatory bitowe pozwalają pracować na pojedynczych bitach jej zapisu dwójkowego.
Przypomnienie: liczba 12 zapisana dwójkowo to 1100, bo 12 = 8 + 4. Liczba 10 to 1010, bo 10 = 8 + 2.
| Operator | Nazwa | Działanie na każdej parze bitów |
|---|---|---|
& | iloczyn bitowy (AND) | 1 tylko wtedy, gdy oba bity są 1 |
| | suma bitowa (OR) | 1, gdy przynajmniej jeden bit jest 1 |
^ | różnica symetryczna (XOR) | 1, gdy bity są różne |
~ | negacja bitowa (NOT) | odwraca każdy bit — jednoargumentowy |
<< | przesunięcie w lewo | przesuwa bity w lewo, dopisując zera |
>> | przesunięcie w prawo | przesuwa bity w prawo |
int a = 12; // 1100
int b = 10; // 1010
Console.WriteLine(a & b); // 8 1000 — jedynka tylko na pozycji, gdzie obie miały 1
Console.WriteLine(a | b); // 14 1110 — jedynka tam, gdzie choć jedna miała 1
Console.WriteLine(a ^ b); // 6 0110 — jedynka tam, gdzie bity się różnią
// Podgląd zapisu dwójkowego
Console.WriteLine(Convert.ToString(a, 2)); // 1100
Console.WriteLine(Convert.ToString(a & b, 2)); // 1000
Przesunięcia bitowe
int x = 5; // 101
Console.WriteLine(x << 1); // 10 1010 przesunięcie w lewo o 1 = mnożenie przez 2
Console.WriteLine(x << 3); // 40 101000 przesunięcie o 3 = mnożenie przez 8
Console.WriteLine(x >> 1); // 2 10 przesunięcie w prawo = dzielenie całkowite przez 2
Przesunięcie to mnożenie i dzielenie przez potęgi dwójki
Przesunięcie w lewo o n pozycji mnoży liczbę przez 2n. Przesunięcie w prawo — dzieli całkowicie. Kiedyś była to popularna sztuczka optymalizacyjna, bo procesor wykonywał ją szybciej niż mnożenie.
Dziś tego nie robimy. Współczesne kompilatory same zamieniają x * 8 na przesunięcie, gdy to się opłaca, a zapis x * 8 jest dużo czytelniejszy. Operatory bitowe stosuje się tam, gdzie naprawdę chodzi o bity: przy obsłudze sprzętu, formatach plików, maskach uprawnień i kompresji.
Typowe zastosowanie: maska bitowa
Jednym bajtem można zapisać osiem niezależnych informacji typu tak/nie. Każdy bit to jedna flaga.
// Każde uprawnienie to inny bit
const int Odczyt = 1; // 001
const int Zapis = 2; // 010
const int Usuwanie = 4; // 100
// Nadajemy dwa uprawnienia naraz — suma bitowa
int uprawnienia = Odczyt | Zapis; // 011 = 3
Console.WriteLine(uprawnienia); // 3
// Sprawdzamy, czy dane uprawnienie jest włączone — iloczyn bitowy
bool mozeCzytac = (uprawnienia & Odczyt) != 0;
bool mozeUsuwac = (uprawnienia & Usuwanie) != 0;
Console.WriteLine(mozeCzytac); // True
Console.WriteLine(mozeUsuwac); // False
// Dodajemy uprawnienie
uprawnienia = uprawnienia | Usuwanie; // 111 = 7
Console.WriteLine(uprawnienia); // 7
Nie myl & z &&
To dwa różne operatory. && łączy warunki logiczne i skraca ocenę. & działa na bitach liczb. Jeden znak różnicy, a zupełnie inne znaczenie — dlatego przy warunkach zawsze pisz podwójny.
Kwalifikacja na kurs prawa jazdy
Program sprawdza, czy kandydat spełnia warunki zapisu. Zasady: ukończone 17 lat i 9 miesięcy, ważne badania lekarskie oraz albo zgoda opiekuna (dla niepełnoletnich), albo ukończone 18 lat.
// Dane kandydata
int wiekMiesiace = 213; // 17 lat i 9 miesięcy
bool maBadania = true;
bool maZgodeOpiekuna = true;
const int MinimalnyWiekMiesiace = 213; // 17 lat i 9 miesiecy
const int PelnoletnoscMiesiace = 216; // 18 lat
// Warunki cząstkowe — każdy nazwany, więc kod tłumaczy się sam
bool wiekOk = wiekMiesiace >= MinimalnyWiekMiesiace;
bool czyPelnoletni = wiekMiesiace >= PelnoletnoscMiesiace;
bool zgodaOk = czyPelnoletni || maZgodeOpiekuna;
// Warunek końcowy
bool mozeSieZapisac = wiekOk && maBadania && zgodaOk;
Console.WriteLine($"Wiek w miesiacach: {wiekMiesiace} ({wiekMiesiace / 12} lat {wiekMiesiace % 12} mies.)");
Console.WriteLine($"Wiek wystarczajacy: {wiekOk}");
Console.WriteLine($"Pelnoletni: {czyPelnoletni}");
Console.WriteLine($"Badania: {maBadania}");
Console.WriteLine($"Zgoda w porzadku: {zgodaOk}");
Console.WriteLine();
Console.WriteLine($"MOZE SIE ZAPISAC: {mozeSieZapisac}");
Wiek w miesiacach: 213 (17 lat 9 mies.)
Wiek wystarczajacy: True
Pelnoletni: False
Badania: True
Zgoda w porzadku: True
MOZE SIE ZAPISAC: True
Omówienie
| Fragment | Dlaczego tak |
|---|---|
| Wiek liczony w miesiącach | Warunek „17 lat i 9 miesięcy” nie da się sensownie zapisać na latach. Sprowadzenie wszystkiego do jednej jednostki upraszcza całą resztę — to częsty zabieg przy datach i czasie. |
| Rozbicie na warunki cząstkowe | Można było napisać jedno wielkie wyrażenie. Byłoby krótsze, ale nieczytelne i trudne do sprawdzenia. Nazwane warunki pośrednie są kluczową techniką — pozwalają też wypisać, który dokładnie warunek zawiódł. |
czyPelnoletni || maZgodeOpiekuna |
Alternatywa, bo wystarczy jedno z dwojga. Pełnoletni nie potrzebuje zgody; niepełnoletni musi ją mieć. |
wiekOk && maBadania && zgodaOk |
Koniunkcja, bo tu wszystkie trzy warunki są obowiązkowe. Zwróć uwagę, że jeden z nich sam w sobie jest alternatywą — warunki można zagnieżdżać dowolnie. |
{wiekMiesiace / 12} i {wiekMiesiace % 12} |
Znajomy duet z poprzedniej lekcji: dzielenie daje pełne lata, reszta — miesiące. |
Spróbuj sam
Zmień maBadania na false i uruchom program. Potem ustaw maZgodeOpiekuna = false. Zobacz, jak każda zmiana wpływa na warunek końcowy — to najlepszy sposób, żeby poczuć różnicę między && a ||.
Na co uważać
| Kod | Problem |
|---|---|
if (x = 5) |
Jeden znak równości to przypisanie. W C# to błąd kompilacji — warunek musi być typu bool. |
18 <= wiek <= 65 |
Zapis matematyczny nie działa. Trzeba wiek >= 18 && wiek <= 65. |
if (liczba) |
W C# liczba nie jest wartością logiczną. Trzeba if (liczba != 0). |
a == b dla double |
Bywa zawodne. Porównuj przez tolerancję. |
tekst == "tak" dla danych od użytkownika |
Wpisanie Tak albo TAK da false. Użyj porównania ignorującego wielkość liter. |
if (a & b) zamiast && |
Zadziała dla wartości bool, ale bez skracania — obie strony zostaną sprawdzone. Traci się zabezpieczenie przed błędem. |
liczba / dzielnik > 10 && dzielnik != 0 |
Zła kolejność. Zabezpieczenie musi być pierwsze, inaczej ocena skrócona nie zdąży zadziałać. |
a || b && c bez nawiasów |
Kompiluje się, ale && wiąże mocniej — łatwo o pomyłkę w interpretacji. Postaw nawiasy. |
tekst1 < tekst2 |
Napisów nie da się porównać operatorem <. Służy do tego string.Compare. |
Zadania
Dla int a = 5, b = 10; i bool t = true, f = false; zapisz wynik każdej linii.
Console.WriteLine(a < b && b < 20);
Console.WriteLine(a > b || b > 20);
Console.WriteLine(!(a == 5));
Console.WriteLine(t && f || t);
Console.WriteLine(a + 5 == b);
Console.WriteLine(a * 2 != b);
pokaż rozwiązanie
True
False
False
True
True
False
Linia 4 — && wiąże mocniej, więc czytamy to jako (t && f) || t, czyli false || true, czyli true.
Linie 5 i 6 — arytmetyka wykonuje się przed porównaniem: najpierw 5 + 5, potem sprawdzenie równości z 10.
Zapisz w C# poniższe warunki, używając zmiennych wiek, ocena, maZaswiadczenie:
- wiek mieści się w przedziale od 15 do 19 lat włącznie
- ocena jest niedostateczna (mniejsza niż 2) albo brakuje zaświadczenia
- uczeń nie jest ani zbyt młody (poniżej 15), ani zbyt stary (powyżej 19)
pokaż rozwiązanie
bool wPrzedziale = wiek >= 15 && wiek <= 19;
bool problem = ocena < 2 || !maZaswiadczenie;
bool odpowiedniWiek = !(wiek < 15 || wiek > 19);
// to samo prawami De Morgana:
bool odpowiedniWiek2 = wiek >= 15 && wiek <= 19;
Trzeci warunek to dokładnie ten sam przedział co pierwszy, tylko zapisany „od drugiej strony”. Prawa De Morgana pozwalają zamienić jeden zapis na drugi — i zwykle wersja bez zaprzeczenia jest czytelniejsza.
Zastosuj prawa De Morgana, aby usunąć zewnętrzną negację z każdego wyrażenia:
!(zalogowany && maUprawnienia)
!(pusty || uszkodzony)
!(wiek >= 18 && maDokument)
pokaż rozwiązanie
!zalogowany || !maUprawnienia
!pusty && !uszkodzony
wiek < 18 || !maDokument
Zwróć uwagę na trzecie wyrażenie: zaprzeczeniem warunku wiek >= 18 nie jest !(wiek >= 18) zapisane dosłownie, tylko po prostu wiek < 18. Każdy operator porównania ma swoje przeciwieństwo:
>= ↔ < > ↔ <= == ↔ !=
Korzystanie z tego zamiast wstawiania wykrzyknika daje dużo czytelniejszy kod.
Napisz warunek sprawdzający, czy rok jest przestępny. Zasada: rok jest przestępny, jeśli dzieli się przez 4, ale nie przez 100 — chyba że dzieli się przez 400. Sprawdź go dla lat 2024, 1900 i 2000.
pokaż rozwiązanie
int rok = 2024;
bool przestepny = (rok % 4 == 0 && rok % 100 != 0) || rok % 400 == 0;
Console.WriteLine($"{rok}: {przestepny}");
2024: True
1900: False
2000: True
2024 — dzieli się przez 4, nie dzieli przez 100 → pierwszy nawias daje true.
1900 — dzieli się przez 4, ale też przez 100, więc pierwszy nawias daje false. Przez 400 też się nie dzieli, więc całość jest fałszywa.
2000 — pierwszy nawias daje false (dzieli się przez 100), ale drugi warunek ratuje sytuację: 2000 dzieli się przez 400.
Nawias wokół pierwszej części jest tu konieczny. Bez niego && i tak wiązałoby mocniej i wynik byłby przypadkiem ten sam — ale nikt nie chce polegać na przypadku przy tak zawiłej regule.
Dla liczb a = 20 i b = 6 wypisz ich zapis dwójkowy oraz wyniki działań &, |, ^, a także a << 2 i a >> 2. Sprawdź, czy przesunięcia rzeczywiście odpowiadają mnożeniu i dzieleniu przez 4.
pokaż rozwiązanie
int a = 20; // 10100
int b = 6; // 110
Console.WriteLine($"a = {a,3} {Convert.ToString(a, 2)}");
Console.WriteLine($"b = {b,3} {Convert.ToString(b, 2)}");
Console.WriteLine($"a & b = {a & b,3} {Convert.ToString(a & b, 2)}");
Console.WriteLine($"a | b = {a | b,3} {Convert.ToString(a | b, 2)}");
Console.WriteLine($"a ^ b = {a ^ b,3} {Convert.ToString(a ^ b, 2)}");
Console.WriteLine($"a << 2 = {a << 2,3} (a * 4 = {a * 4})");
Console.WriteLine($"a >> 2 = {a >> 2,3} (a / 4 = {a / 4})");
a = 20 10100
b = 6 110
a & b = 4 100
a | b = 22 10110
a ^ b = 18 10010
a << 2 = 80 (a * 4 = 80)
a >> 2 = 5 (a / 4 = 5)
Wyrównaj bity, żeby zobaczyć, co się dzieje. 20 to 10100, 6 to 00110:
& zostawia jedynkę tylko tam, gdzie obie liczby miały jedynkę — czyli na trzeciej pozycji od prawej: 00100 = 4.
^ daje jedynkę tam, gdzie bity się różnią: 10010 = 18.
Przesunięcia potwierdzają regułę: o 2 pozycje to mnożenie lub dzielenie przez 2² = 4.
Zbuduj maskę bitową dla czterech uprawnień: odczyt (1), zapis (2), usuwanie (4), udostępnianie (8). Nadaj użytkownikowi odczyt i udostępnianie, sprawdź wszystkie cztery uprawnienia, następnie odbierz udostępnianie i sprawdź ponownie.
pokaż rozwiązanie
const int Odczyt = 1; // 0001
const int Zapis = 2; // 0010
const int Usuwanie = 4; // 0100
const int Udostepnianie = 8; // 1000
// Nadanie dwóch uprawnien
int uprawnienia = Odczyt | Udostepnianie; // 1001 = 9
Console.WriteLine($"Maska: {uprawnienia} ({Convert.ToString(uprawnienia, 2)})");
Console.WriteLine($" odczyt: {(uprawnienia & Odczyt) != 0}");
Console.WriteLine($" zapis: {(uprawnienia & Zapis) != 0}");
Console.WriteLine($" usuwanie: {(uprawnienia & Usuwanie) != 0}");
Console.WriteLine($" udostepnianie: {(uprawnienia & Udostepnianie) != 0}");
// Odebranie uprawnienia: iloczyn z zanegowana maska
uprawnienia = uprawnienia & ~Udostepnianie; // 0001 = 1
Console.WriteLine();
Console.WriteLine($"Po odebraniu udostepniania: {uprawnienia} ({Convert.ToString(uprawnienia, 2)})");
Console.WriteLine($" udostepnianie: {(uprawnienia & Udostepnianie) != 0}");
Maska: 9 (1001)
odczyt: True
zapis: False
usuwanie: False
udostepnianie: True
Po odebraniu udostepniania: 1 (1)
udostepnianie: False
Trzy operacje warto zapamiętać jako zestaw:
- Nadanie uprawnienia:
maska | flaga - Sprawdzenie:
(maska & flaga) != 0 - Odebranie:
maska & ~flaga— negacja bitowa robi maskę z jedynkami wszędzie poza tym jednym bitem, więc iloczyn zeruje właśnie jego
Ten wzorzec spotkasz w systemach plików, uprawnieniach w Windowsie i w wielu formatach binarnych. W C# istnieje wygodniejszy sposób zapisania takich flag — typ wyliczeniowy z atrybutem [Flags] — ale wymaga on znajomości typów wyliczeniowych, które omawiamy w bloku 9.
Co trzeba zapamiętać
- Operatory porównania zwracają
bool.=przypisuje,==porównuje. - Przedział zapisujemy dwoma warunkami:
x >= 18 && x <= 65. Zapis matematyczny nie działa. - Na napisach
==porównuje treść, ale rozróżnia wielkość liter. Dla danych od użytkownika użyjToLower()alboStringComparison.OrdinalIgnoreCase. - Typu
doublenie porównuj przez==— użyj tolerancji. &&wymaga wszystkich warunków,||wystarczy jeden,!odwraca.- Ocena skrócona: gdy wynik jest przesądzony, reszta się nie wykonuje. Warunek zabezpieczający stawiaj pierwszy.
&&wiąże mocniej niż||. Przy mieszaniu obu zawsze stawiaj nawiasy.- Prawa De Morgana:
!(A && B)to!A || !B, a!(A || B)to!A && !B. - Operatory bitowe
&|^~<<>>działają na pojedynczych bitach — nie myl ich z logicznymi.