C#-kifejezések

Tip

Ez a cikk az Alapismeretek szakasz része azon fejlesztők számára, akik már legalább egy programozási nyelvet ismernek, és c#-t tanulnak. Ha még csak most ismerkedik a programozással, először az Első lépések oktatóanyagokkal kezdje.

Más nyelvről jön? A C#-kifejezések ugyanúgy működnek, mint Java, C++ és JavaScript nyelven. Egy különbség, amit érdemes megjegyezni: az összetett hozzárendelési operátorokhoz hasonló += és a növekményes operátorok ++ a C# kifejezései, így nagyobb kifejezésekben is megjelenhetnek.

A kifejezés olyan kódrészlet, amelyet a fordító kiértékel, hogy értéket állítson elő – számot, sztringet, hivatkozást vagy egy bool. Például a 3 + 4 * 2 egy olyan kifejezés, amelynek értéke a(z) 11 egész szám, a total > 10 pedig egy olyan kifejezés, amelynek értéke true vagy false.

A legegyszerűbb kifejezések a literálok (mint például 42 vagy "hello") és a változónevek (mint például total). Összetettebb kifejezéseket hozhat létre az egyszerűbbek és az operátorok kombinálásával.

Kifejezések és állítások

Egy kifejezés értéket hoz létre. Az utasítás egy teljes utasítás, amelyet a program végrehajt. Számos utasítás tartalmaz kifejezéseket. Például int total = 3 + 4 * 2; egy változó deklarációs utasítás. A fordító kiértékeli az inicializáló kifejezést 3 + 4 * 2, amely létrehozza 11, és hozzárendeli ezt az értéket az új változóhoz total.

Úgy tekinthet egy kifejezésre, mint egy szókapcsolatra, egy C#-utasításra pedig mint egy teljes mondatra. Ezek a munkadefiníciók segítenek megérteni azokat a formális terminológiát és magyarázatokat, amelyekkel a C#-ról való tájékozódás során találkozhat, bár a szokásos kódolás ritkán igényli, hogy tudatosan emlékezzen a megkülönböztetésre.

Kifejezések kombinálása

A kifejezések kombinálhatók. Fontolja meg 3 + 4 * 2. Ez az egyetlen kifejezés valójában két kisebb kifejezést tartalmaz: a szorzási kifejezést4 * 2 és az összeadási kifejezést3 + <result>. A kifejezések kombinálásakor a C#-nak egy szabályra van szüksége annak eldöntéséhez, hogy melyiket kell először kiértékelni. Ez a szabály az operátorok elsőbbsége.

Nem kell megjegyeznie a teljes elsőbbségi hierarchiát. A következő szakasz összefoglalja a mindennapi csoportokat, és zárójelekkel mindig egyértelművé teheti a rendelést, ha nem biztos benne.

Operátorok elsőbbsége

Az operátorok elsőbbsége határozza meg, hogy egy kombinált kifejezés hogyan csoportosítható részkifejezésekké. Ez a fogalom hasonló a matematikai osztály műveleti sorrendjének szabályaihoz. A magasabb elsőbbséget élvező operátorok szorosabban kötődnek az operandusokhoz. A kifejezés úgy van felépítve, mintha ezek az operandusok együtt zárójelezve lennének. A(z) 3 + 4 * 2 esetén a(z) * nagyobb precedenciával rendelkezik, mint a(z) +, ezért a kifejezés így csoportosul: 3 + (4 * 2), nem pedig (3 + 4) * 2.

A C#-nak több előzménycsoportja van, mint az itt összefoglalt négynek. Ezek a további csoportok az ismert szabályokat – például a hozzáadás előtti szorzást – kényszerítik ki, és lefedik az operátorok teljes készletét. A következő csoportok a mindennapi kódban leggyakrabban előforduló problémákat ismertetik:

  1. Elsődleges, unáris és intervallum – ez három különálló elsőbbségi csoport, amelyek mindegyike erősebben kötődik, mint az aritmetikai műveletek. Ez az összefoglalás egyetlen lépésben egyesíti őket, mert gyakorlati célokra mind az aritmetika előtt alkalmazhatók.
    • Az elsődleges operátorok – a tagok hozzáférése (x.y), a metódushívások (f()), az indexelés (a[i]) és a null-feltételes hozzáférés (?., ?[]) – a legszorosabban kötődnek.
    • Az unáris operátorok egyetlen operandusra alkalmazhatók: negáció (-x), logikai NOT (!flag), prefix növelés (++i) és postfix növelés (i++).
    • A tartomány operátora (..) indextartományokat hoz létre szeletkifejezésekhez, például array[1..4].
  2. Aritmetikai műveletek — a *, / és % szorosabban kötődik, mint a + és -. A szorzás és osztás az összeadás és kivonás előtt történik.
  3. Összehasonlítás<, >, <=, >=, ==, != gyengébben kötődik, mint az aritmetikai műveletek, így azok az összehasonlítás előtt hajtódnak végre.
  4. Logikai&& és || a közös operátorok közül a legkevésbé szoros kötés, ezért az összehasonlítások a logikai kombináció előtt befejeződnek.

Ez a precedencia azt jelenti, hogy a(z) score + bonus > threshold && attempts < maxAttempts kifejezés pontosan úgy értékelődik ki, ahogyan olvasná: adja össze a(z) score és bonus értékét, hasonlítsa össze az összeget a(z) threshold értékkel, hasonlítsa össze a(z) attempts értéket a(z) maxAttempts értékkel, majd kombinálja a két bool eredményt a(z) && operátorral. Ha zárójeleket ad hozzá, hogy minden csoportosítás explicit legyen, ugyanaz a struktúra jelenik meg: ((score + bonus) > threshold) && (attempts < maxAttempts).

Az összes operátorra kiterjedő teljes elsőbbségi hierarchiát a C#-operátorok és -kifejezések című témakörben tekinti meg.

A szándék egyértelművé tétele zárójelekkel

Zárójelek felülbírálják az elsőbbséget, és egyszerre dokumentálják a szándékot. Ha a fenti csoportok nem egyértelműek a sorrendben, zárójeleket adjon hozzá:

int score = 80;
int bonus = 15;
int threshold = 90;
bool eligible = true;

// Without parentheses: || binds less tightly than &&, so this reads as:
//   eligible && (score + bonus > threshold) || bonus > 20
bool result1 = eligible && score + bonus > threshold || bonus > 20;

// With parentheses: forces the || to combine two complete conditions
bool result2 = (eligible && score + bonus > threshold) || bonus > 20;

Console.WriteLine(result1); // => True
Console.WriteLine(result2); // => True

// Parentheses can also change the result:
bool isAdmin = false;
bool isOwner = true;

// Without: && binds tighter, so: (isAdmin && isOwner) || true
bool access1 = isAdmin && isOwner || true;
// With: forces the || to run first
bool access2 = isAdmin && (isOwner || true);

Console.WriteLine(access1); // => True   (false && true is false; false || true is true)
Console.WriteLine(access2); // => False  (false && anything is false)

Az utolsó két sor azt mutatja, hogy a zárójelek nem csak a stílust, hanem az eredményt is megváltoztathatják. Amikor && és || együtt jelennek meg, zárójelek hozzáadásával adja meg, hogy melyik feltétel egyesül először. Egy olvasó, aki látja (isAdmin && isOwner) || isSuperUser , azonnal ismeri a szándékot.

Kifejezések kiértékelése

Egy hasonlattal könnyebben megérthető, hogy a C# hogyan értékeli ki a kombinált kifejezéseket. Képzelje el a ceruzával és a papírral kapcsolatos összetett matematikai probléma megoldását: azonosítja a legbelső vagy legmagasabb prioritású alkifejezést, kiszámítja annak köztes értékét, írja le az eredményt, majd ismételje meg a következő alkifejezéssel – amíg el nem éri a végső választ.

A C# ugyanazt a folyamatot követi, amelyet két szabály vezérel:

1. szabály: Az operandusok balról jobbra értékelnek. Minden bináris kifejezés esetében mindkét operandust teljes mértékben ki kell értékelni az operátor alkalmazása előtt. A C# először kiértékeli a bal operandust, majd a jobb oldalt, majd végrehajtja a műveletet.

2. szabály: Egyes operátorok rövidzárlatban vannak. Bizonyos operátorok az eredmény meghatározása után leállnak az értékelés során, kihagyva a fennmaradó operandusokat:

  • && (feltételes ÉS): visszaadja a(z) false értéket, amint a bal oldali érték false. A jobb oldal soha nincs kiértékelve.
  • || (feltételes VAGY): a(z) true értéket adja vissza, amint a bal oldal true. A jobb oldal soha nincs kiértékelve.
  • ?: (feltételes/ternáris): csak a feltételnek megfelelő ágat értékeli ki – a másik ág soha nem lesz kiértékelve.
  • ?. (null-feltételes taghozzáférés) és ?[] (null-feltételes elemhez való hozzáférés): állítsa le és adja vissza null azonnal, amikor a bal oldalon van null, kihagyva a taghozzáférést vagy az indexet.
  • ??= (null-egyesítő értékadás): csak akkor rendeli a jobb oldali értéket, ha a bal oldali érték null.

Papír-ceruza alapú értékelés

Fontolja meg a kifejezést 3 + 6 / 2. Annak ellenére, hogy az összeadás jelenik meg elsőként az olvasási sorrendben, a / precedenciája magasabb, mint a +-é, ezért a 6 / 2 az az alkifejezés, amely először értékelődik ki. Lépésről lépésre haladva – pontosan úgy, ahogy papíron tenné:

3 + 6 / 2
      ↓   (evaluate 6 / 2 → 3)
3 +   3
  ↓   (evaluate 3 + 3 → 6)
  6
// 3 + 6 / 2
// Step 1: 6 / 2 has higher precedence → interim value 3
// Step 2: 3 + 3 → final result 6
int result = 3 + 6 / 2;
Console.WriteLine(result); // => 6

A(z) + által előállított köztes érték, 3, a(z) 6 jobb oldali operandusává válik, a végeredmény pedig 6 / 2. Minden alkifejezés létrehoz egy köztes értéket; ezek a köztes értékek a következő alkifejezést táplálják, amíg csak egy érték marad.

Az asszociativitás egy kapcsolódó fogalom: ha két operátor azonos elsőbbséget élvez, az asszociativitás dönti el, hogy melyik legyen az első. A C# -operátorok többsége bal asszociatív, ami azt jelenti, hogy balról jobbra csoportosítja őket. Tehát a - b - c ugyanaz, mint (a - b) - c, nem a - (b - c).

Rövidzárlat-értékelés a gyakorlatban

A rövidzárlatok kiértékelése különösen hasznos null értékű ellenőrzések esetén:

string? text = null;

// Safe: second condition runs only when text is not null
bool hasContent = text != null && text.Length > 0;
Console.WriteLine(hasContent); // => False  (short-circuits after null check; no NullReferenceException)

text = "hello";
hasContent = text != null && text.Length > 0;
Console.WriteLine(hasContent); // => True

// || short-circuits on true: right side is never evaluated when left side is true
string word = "hello";
bool anyMatch = word.StartsWith("h") || word.StartsWith("x");
Console.WriteLine(anyMatch); // => True  (right side never evaluated)

// ?. short-circuits on null: returns null without accessing .Length
string? maybeNull = null;
int? length = maybeNull?.Length;   // length is null; no NullReferenceException
Console.WriteLine(length.HasValue); // => False

text != null && text.Length > 0 biztonságos, mert a második feltétel csak akkor fut, ha text nem null. Hasonlóképpen, a ?. leállítja a kiértékelést, amikor egy null referenciával találkozik, így elkerülhető egy NullReferenceException explicit if ellenőrzés nélkül.

A nullbiztos operátorok részletesebb áttekintéséért tekintse meg a C# null operátorok című témakört.

Lásd még