Operatory języka C#

Wskazówka

Ten artykuł jest częścią sekcji Podstawy dla deweloperów, którzy już znają co najmniej jeden język programowania i uczą się języka C#. Jeśli dopiero zaczynasz programować, najpierw zacznij od samouczków Wprowadzenie .

Pochodzi z innego języka? Większość operatorów w tym artykule (+, -, /%||!&&*!=<==>operatory porównania i =) działają tak samo jak w Java, C++i JavaScript. Głównymi niespodziankami dla nowicjuszy są zachowanie dzielenia liczb całkowitych, różnica między formą prefiksową a postfiksową dla ++/-- oraz sposób, w jaki operator przypisania złożonego konwertuje wynik z powrotem do typu wyrażenia po lewej stronie.

Operator łączy jeden lub więcej operandów w jedną wartość. Wiesz już o wyrażeniach i pierwszeństwie operatorów w wyrażeniach języka C#; Ten artykuł zawiera bardziej szczegółowe informacje na temat określonych operatorów, których będziesz używać każdego dnia.

Operatory arytmetyczne

Pięć operatorów arytmetycznych wykonuje obliczenia liczbowe.

Operator Name Przykład Result
+ Dodawanie 10 + 3 13
- Odejmowanie 10 - 3 7
* Mnożenie 10 * 3 30
/ Dzielenie 10 / 3 3
% Upomnienie 10 % 3 1
int apples = 10;
int oranges = 3;

Console.WriteLine(apples + oranges);  // => 13  (addition)
Console.WriteLine(apples - oranges);  // => 7   (subtraction)
Console.WriteLine(apples * oranges);  // => 30  (multiplication)
Console.WriteLine(apples / oranges);  // => 3   (integer division: truncates toward zero)
Console.WriteLine(apples % oranges);  // => 1   (remainder)

// Integer division always truncates toward zero — the fractional part is discarded
int result = 7 / 2;
Console.WriteLine(result); // => 3, not 3.5

// Truncation applies to negative results too: -7 / 2 is -3, not -4
int negResult = -7 / 2;
Console.WriteLine(negResult); // => -3

// To get a decimal result, at least one operand must be a double or float
double precise = 7.0 / 2;
Console.WriteLine(precise); // => 3.5

// Remainder with negative operands: the sign of the result matches the dividend
Console.WriteLine(-7 % 3);  // => -1  (-7 = 3 × -2 + (-1))
Console.WriteLine(7 % -3);  // => 1   ( 7 = -3 × -2 + 1)

Dzielenie liczb całkowitych obcina się w kierunku zera. Gdy oba operandy są liczbami całkowitymi, / odrzuca część ułamkową: 7 / 2 to 3, a nie 3.5. Obcinanie odbywa się w kierunku zera, a nie w stronę mniejszej wartości: -7 / 2 to -3 (a nie -4). Aby uzyskać wynik dziesiętny, wykonaj co najmniej jeden operand typu zmiennoprzecinkowego: 7.0 / 2 to 3.5. Różni się to od niektórych języków, w których / zawsze tworzy wynik zmiennoprzecinkowy.

Reszta (%) zwraca to, co pozostaje po dzieleniu całkowitym: 10 % 3 to 1, ponieważ 10 = 3 × 3 + 1. Jest to przydatne do iterowania po stałym zakresie (index % length), sprawdzania podzielności (n % 2 == 0) i wyodrębniania cyfr. Z ujemnymi operandami znak wyniku odpowiada znakowi dywidendy (lewy operand): -7 % 3 jest -1 i 7 % -3 ma wartość 1.

Operatory jednoargumentowe

Operatory jednoargumentowe działają na jednym operandie.

int temperature = 20;
int windChill = -5;

int heatIndex = +temperature;   // unary +: value unchanged (rarely needed)
int coldFactor = -windChill;    // unary -: negates the value → 5

Console.WriteLine(heatIndex);   // => 20
Console.WriteLine(coldFactor);  // => 5

bool isRaining = false;
bool isSunny = !isRaining;     // logical NOT: flips true/false
Console.WriteLine(isSunny);    // => True
  • +x (plus unarny) — pozostawia wartość bez zmian; rzadko zapisywany jawnie, ale poprawny.
  • -x (jednoargumentowy minus) — neguje wartość.
  • !x (logiczne NOT) — zmienia true na false i false na true. Często będziesz używać ! : if (!list.Contains(item)).

Przyrost i dekrementacja

++ dodaje 1 i -- odejmuje 1. Oba mają postać prefiksową i postfiksową, które różnią się tym, jaka wartość jest zwracana:

int counter = 5;

// Prefix: increment first, then use the new value
int a = ++counter;
Console.WriteLine(a);       // => 6
Console.WriteLine(counter); // => 6

// Postfix: use the current value first, then increment
int b = counter++;
Console.WriteLine(b);       // => 6  (value before increment)
Console.WriteLine(counter); // => 7  (incremented after)

// Decrement works the same way
int score = 10;
Console.WriteLine(score--); // => 10 (current value; score becomes 9)
Console.WriteLine(score);   // => 9
  • Prefiks (++i, --i): zwiększa lub dekrementuje zmienną jako pierwszą, a następnie zwraca nową wartość.
  • Postfix (i++, i--): zwraca najpierw bieżącą wartość, a następnie zwiększa lub dekrementuje zmienną.

Gdy ++ lub -- pojawia się jako autonomiczna instrukcja (nie część większego wyrażenia), prefiks i postfiks mają taki sam efekt. Rozróżnienie ma znaczenie tylko wtedy, gdy wynik jest używany — na przykład w przypisaniu lub jako argument metody.

Operatory relacyjne

Operatory relacyjne porównują dwie wartości i zwracają bool.

Operator Meaning Przykład
< Mniejsze niż speed < limit
> Większe niż speed > limit
<= Mniejsze lub równe score <= 100
>= Większe niż lub równe score >= 0
int speed = 75;
int limit = 60;

Console.WriteLine(speed > limit);   // => True   (greater than)
Console.WriteLine(speed < limit);   // => False  (less than)
Console.WriteLine(speed >= limit);  // => True   (greater than or equal)
Console.WriteLine(speed <= limit);  // => False  (less than or equal)

// Relational operators work on all numeric types and char
// char comparison uses the character's numeric Unicode code point, not alphabetical position
// 'B' (U+0042, value 66) is less than 'A' (U+0041, value 65)? No — 'A' (65) < 'B' (66)
char grade = 'B';
Console.WriteLine(grade >= 'A' && grade <= 'C'); // => True  ('A'=65 <= 'B'=66 <= 'C'=67)

Operatory relacyjne działają dla wszystkich typów liczbowych i char. Dla char porównanie opiera się na numerycznej wartości punktu kodowego Unicode danego znaku, nie zaś na kolejności alfabetycznej ani specyficznej dla domeny. W powyższym przykładzie ocen 'A' jest większe lub równe 'B', ponieważ 'B' ma wartość Unicode 66, a 'A' ma wartość Unicode 65 — liczby określają porównanie, a nie znaczenie ocen literowych.

Operatory równości

== i != sprawdź, czy dwie wartości są równe, czy nie. != jest true wtedy, gdy operandy nie są równe, a false wtedy, gdy są równe.

int expected = 42;
int actual = 42;

Console.WriteLine(actual == expected);  // => True   (values are equal)
Console.WriteLine(actual != expected);  // => False  (true when values are not equal)

string name = "Alice";
Console.WriteLine(name == "Alice");  // => True   (string content matches)
Console.WriteLine(name == "alice");  // => False  (case-sensitive)

int x = 5;
Console.WriteLine(x == 10);  // => False

W przypadku typów liczbowych i string, równość testuje wartości. W przypadku typów referencyjnych wartość domyślna to tożsamość (czy dwie zmienne wskazują ten sam obiekt), ale wiele typów, w tym string i record zastępuje tę wartość w celu porównania zawartości. Pełny obraz — sposób działania równości między typami wartości, typami referencyjnymi, rekordami i strukturami — zobacz Porównanie równości.

Note

Język C# nie ma === operatora. Zapis === jest błędem kompilacji:

// This does not compile — C# has no === operator
bool same = (x === 10);

Jeśli pochodzisz z języka JavaScript, użyj polecenia == do porównania wartości (język C# == porównuje już wartość dla typów pierwotnych i ciągów). Typowym powiązanym błędem jest przypadkowe użycie = (operatora przypisania), gdy chodziło o == (sprawdzenie równości). Kompilator wykrywa najczęstsze przypadki, ale dokładnie sprawdź każdy warunek if, który zawiera =.

Operatory logiczne warunkowe

&& (AND) i || (OR) łączą bool wyrażenia.

int age = 20;
bool hasTicket = true;

// && (AND): both sides must be true
bool canEnter = age >= 18 && hasTicket;
Console.WriteLine(canEnter); // => True

// || (OR): at least one side must be true
bool freeEntry = age < 5 || age >= 65;
Console.WriteLine(freeEntry); // => False

// Short-circuit: right side is skipped when the result is already determined
// Here, items.Count is never called if items is null
List<string>? items = null;
bool hasItems = items != null && items.Count > 0;
Console.WriteLine(hasItems); // => False  (short-circuits; no NullReferenceException)

Oba operatory stosują skrócone wartościowanie: pomijają obliczenie prawego operandu, gdy wynik jest już znany.

  • && zwraca false, gdy tylko lewa strona ma wartość false. Prawa strona nigdy nie jest oceniana.
  • || zwraca true, gdy tylko lewa strona ma wartość true. Prawa strona nigdy nie jest oceniana.

Mechanizm skróconego obliczania ma praktyczną zaletę: można bezpiecznie zabezpieczyć wykonanie operacji po prawej stronie przez sprawdzenie po lewej stronie, czy wartość nie jest nullem, jak pokazano w powyższym przykładzie. Jeśli items ma wartość null, na tym && się kończy — items.Count nigdy nie jest wywoływany, więc nie jest zgłaszany żaden NullReferenceException.

Operator warunkowy ?:

Operator warunkowy (nazywany również operatoremternary ) oblicza jedno z dwóch wyrażeń na podstawie warunku:

condition ? value-when-true : value-when-false
int temperature2 = 35;

// condition ? value-when-true : value-when-false
string weather = temperature2 > 30 ? "hot" : "comfortable";
Console.WriteLine(weather); // => hot

// Only the matching branch evaluates — the other branch is never run
int divisor = 0;
// The division 10 / divisor is never evaluated because divisor == 0 is true
int safe = divisor == 0 ? -1 : 10 / divisor;
Console.WriteLine(safe); // => -1

Operator ?: zawsze oblicza dokładnie jedną gałąź — gałąź, która nie spełnia warunku, nigdy nie jest obliczana. Dzięki temu można bezpiecznie użyć wyrażenia po jednej stronie, które zakończy się niepowodzeniem w przypadku innych danych wejściowych, o ile warunek prawidłowo go chroni.

Użyj ?: do prostych wyborów w tekście. W przypadku warunków wielokierunkowych lub bloków kodu if/else instrukcja jest zwykle jaśniejsza.

Operatory przypisania

Prosty operator = przypisania przechowuje wartość w zmiennej:

int level = 1;  // declaration + initialization
level = 5;      // reassignment

Przypisanie w języku C# jest prawostronnie łączne, co oznacza, że a = b = c = 0 jest obliczane od prawej do lewej: c otrzymuje 0, następnie b otrzymuje a, a potem 0 otrzymuje 0.

Przypisanie złożone

Operatory przypisania złożonego łączą operację binarną z przypisaniem:

Operator Odpowiednik
x += y x = x + y
x -= y x = x - y
x *= y x = x * y
x /= y x = x / y
x %= y x = x % y
int level = 1;
level = 5;          // simple assignment: replaces the value
Console.WriteLine(level); // => 5

// Compound assignment: short form of binary operation + assignment
int hp = 100;
hp += 20;   // same as: hp = hp + 20
Console.WriteLine(hp); // => 120
hp -= 10;   // same as: hp = hp - 10
Console.WriteLine(hp); // => 110
hp *= 2;    // same as: hp = hp * 2
Console.WriteLine(hp); // => 220
hp /= 3;    // same as: hp = hp / 3 (integer division)
Console.WriteLine(hp); // => 73
hp %= 7;    // same as: hp = hp % 7
Console.WriteLine(hp); // => 3

Przypisanie złożone to coś więcej niż tylko skrócony zapis. Oblicza lewą stronę dokładnie raz, a następnie konwertuje wynik z powrotem do typu lewej strony. Ma to znaczenie, gdy lewa strona wyrażenia ma skutki uboczne (na przykład indeksator tablicy), i dlatego przypisanie złożone do zmiennej byte kompiluje się bez jawnego rzutowania, podczas gdy jego rozwinięta postać już nie:

// Assignment is right-associative: evaluated right to left
int a2, b2, c2;
a2 = b2 = c2 = 0;     // c2 = 0 first, then b2 = 0, then a2 = 0
Console.WriteLine($"{a2} {b2} {c2}"); // => 0 0 0

// Compound assignment evaluates the left side once and converts back to the LHS type
byte small = 200;
small += 10;  // equivalent to: small = (byte)(small + 10); result is 210
Console.WriteLine(small); // => 210

small += 10 kompiluje, ponieważ kompilator wstawia konwersję zawężającą automatycznie — wynik 210, mieści się w byte zakresie od 0 do 255. small = small + 10 wymagałoby jawnego rzutowania na (byte), ponieważ operacje arytmetyczne promują oba operandy do typu int.

Inne operatory języka C#

W tym artykule opisano operatory, które najczęściej spotykasz w kodzie codziennym. Język C# zawiera więcej operatorów przydatnych w określonych scenariuszach:

  • Operatory przesunięcia (<<, >>, >>>) — przesuwają bity wartości całkowitej w lewo lub w prawo o określoną liczbę pozycji. Bitowe i logiczne operatory liczb całkowitych (&, |, ^, ~) — łączą lub odwracają wartości całkowite bit po bicie, przydatne do obsługi flag, masek i kodu niskiego poziomu: Operatory bitowe i przesunięć
  • checked and unchecked — określa, czy przepełnienie całkowite zgłasza wyjątek (checked) lub zawija w trybie dyskretnym (unchecked): zaznaczone i niezaznaczone
  • Operatory wartości null (??, ??=, ?., ?[]) — bezpiecznie obsługują wartości null, zapewniając wartości domyślne lub skrócony dostęp do składowych: Operatory wartości null
  • Operatory sprawdzania typu i konwersji (is, as, typeof, rzutowanie (T)) — sprawdzanie lub konwertowanie typu wartości w czasie wykonywania: Operatory sprawdzania typu i rzutowania
  • Operatory zakresu i indeksu (.., ^) — tworzą zakresy i indeksy względne dla tablic fragmentowania i zakresów: dostęp do składowych i operatory warunkowe o wartości null
  • Przypisanie dekonstruujące — rozpakowanie krotki lub typu na poszczególne zmienne w jednym wyrażeniu: Dekonstrukcja krotek i innych typów

Zobacz także