Kako podesiti pametne telefone i računare. Informativni portal

C # matematičke operacije. Uslovni izrazi

Relacioni i Bulovi operatori

U notaciji operator relacije i logički operator termin odnos znači odnos koji može postojati između dva značenja i pojma logicno- odnos između logičkih vrijednosti "tačno" i "netočno". A budući da relacijski operatori daju istinite ili lažne rezultate, često se koriste zajedno s logičkim operatorima. Iz tog razloga se smatraju zajedno.

Ovo su relacioni operatori:

Booleovi operatori uključuju sljedeće:

Rezultat izvršavanja operatora relacije ili logičkog operatora je booleova vrijednost tipa bool.

Općenito, objekti se mogu porediti za jednakost ili nejednakost koristeći relacijske operatore == i! =. Operatori poređenja, =, mogu se primijeniti samo na tipove podataka koji podržavaju odnos naručivanja. Stoga se relacijski operatori mogu primijeniti na sve numeričke tipove podataka. Ali bool vrijednosti se mogu porediti samo za jednakost ili nejednakost, jer istinite i lažne vrijednosti nisu poređane. Na primjer, poređenje true> false u C # je besmisleno.

Razmotrite primjer programa koji pokazuje upotrebu relacijskih i logičkih operatora:

Korišćenje sistema; koristeći System.Collections.Generic; koristeći System.Linq; koristeći System.Text; imenski prostor ConsoleApplication1 (klasa Program (statički void Main (string args) (kratko d = 10, f = 12; bool var1 = true, var2 = false; if (df) Console.WriteLine ("d> f"); // Uporedite varijable var1 i var2 if (var1 & var2) Console.WriteLine ("Ovaj tekst neće biti prikazan"); if (! (var1 & var2)) Console.WriteLine ("! (var1 & var2) = true"); ako (var1 | var2) Console.WriteLine ("var1 | var2 = true"); if (var1 ^ var2) Console.WriteLine ("var1 ^ var2 = true"); Console.ReadLine ();)))

Logički operatori u C # izvode najčešće logičke operacije. Ipak, postoji niz operacija koje se izvode prema pravilima formalne logike. Ove logičke operacije se mogu izgraditi pomoću logičkih operatora podržanih u C #. Stoga, C# pruža takav skup logičkih operatora, koji je dovoljan za konstruiranje gotovo svake logičke operacije, uključujući implikacije. Implikacije je binarna operacija koja se procjenjuje na false samo ako je njen lijevi operand istinit, a desni netačan. (Operacija implikacije odražava sljedeći princip: istina ne može značiti laž.)

Operacija implikacije može se izgraditi na osnovu kombinacije logičkih operatora! i |:

Skraćeni logički operatori

C # takođe nudi posebne, skraćeno, varijante logičkih operatora AND i OR, dizajnirane da dobiju efikasniji kod. Objasnimo ovo sljedećim primjerima logičkih operacija. Ako je prvi operand logičke I operacije lažan, tada će njegov rezultat biti lažan bez obzira na vrijednost drugog operanda. Ako prvi operand logičke operacije ILI ima tačnu vrijednost (true), tada će njegov rezultat imati tačnu vrijednost bez obzira na vrijednost drugog operanda. Zbog činjenice da vrijednost drugog operanda u ovim operacijama nije potrebno izračunavati, štedi vrijeme i poboljšava efikasnost koda.

Skraćena logička I operacija se izvodi pomoću operater &&, i skraćenu logičku operaciju ILI koristeći operater ||... Ovi skraćeni logički operatori odgovaraju uobičajenim logičkim operatorima & i |. Jedina razlika između skraćenog logičkog operatora i običnog je u tome što se njegov drugi operand procjenjuje samo prema potrebi.

Posljednje ažuriranje: 19.06.2017

C # koristi većinu operacija koje se koriste u drugim programskim jezicima. Operacije predstavljaju specifične akcije nad operandima koji su učesnici u operaciji. Operand može biti varijabla ili neka vrijednost (na primjer, broj). Operacije mogu biti unarne (izvode se na jednom operandu), binarne - na dva operanda i ternarne - na tri operanda. Razmotrimo sve vrste operacija.

Binarne aritmetičke operacije:

    Operacija sabiranja dva broja:

    Int x = 10; int z = x + 12; // 22

    Operacija oduzimanja dva broja:

    Int x = 10; int z = x - 6; // 4

    Operacija množenja dva broja:

    Int x = 10; int z = x * 5; // 50

    operacija dijeljenja dva broja:

    Int x = 10; int z = x / 5; // 2 duplo a = 10; duplo b = 3; duplo c = a / b; // 3.33333333

    Prilikom dijeljenja, imajte na umu da ako oba operanda predstavljaju cijele brojeve, onda će i rezultat biti zaokružen na cijeli broj:

    Dvostruki z = 10/4; // rezultat je 2

    Unatoč činjenici da se rezultat operacije na kraju stavlja u varijablu tipa double, što vam omogućava da sačuvate razlomak, sama operacija uključuje dva literala, koji se po defaultu tretiraju kao int objekti, odnosno cijeli brojevi, a rezultat će biti isti cijeli broj.

    Da biste izašli iz ove situacije, potrebno je definirati literale ili varijable koje učestvuju u operaciji, točno kao tipovi double ili float:

    Dvostruki z = 10,0 / 4,0; // rezultat je 2.5

    Operacija dobivanja ostatka cjelobrojnog dijeljenja dva broja:

    Dvostruki x = 10,0; duplo z = x% 4,0; // rezultat je 2

Postoji i niz unarnih operacija u kojima učestvuje jedan operand:

    Operacija povećanja

    Povećanje može imati prefiks: ++ x - prvo se vrijednost varijable x povećava za 1, a zatim se njena vrijednost vraća kao rezultat operacije.

    Tu je i postfiksni inkrement: x ++ - prvo se vraća vrijednost varijable x kao rezultat operacije, a zatim joj se dodaje 1.

int x1 = 5; int z1 = ++ x1; // z1 = 6; x1 = 6 Console.WriteLine ($ "(x1) - (z1)"); int x2 = 5; int z2 = x2 ++; // z2 = 5; x2 = 6 Console.WriteLine ($ "(x2) - (z2)");

Operacija dekrementiranja ili smanjenja vrijednosti za jedan. Tu je i dekrement prefiksa (--x) i postfiks (x--).

Int x1 = 5; int z1 = --x1; // z1 = 4; x1 = 4 Console.WriteLine ($ "(x1) - (z1)"); int x2 = 5; int z2 = x2--; // z2 = 5; x2 = 4 Console.WriteLine ($ "(x2) - (z2)");

Kada izvodite nekoliko aritmetičkih operacija odjednom, uzmite u obzir redosljed kojim se one izvode. Prioritet operacija od najvišeg do najnižeg:

    Povećanje, smanjenje

    Množenje, dijeljenje, dobivanje ostatka

    Sabiranje, oduzimanje

Zagrade se koriste za promjenu redoslijeda operacija.

Razmotrite skup operacija:

Int a = 3; int b = 5; int c = 40; int d = c --- b * a; // a = 3 b = 5 c = 39 d = 25 Console.WriteLine ($ "a = (a) b = (b) c = (c) d = (d)");

Ovdje imamo posla sa tri operacije: dekrementiranje, oduzimanje i množenje. Prvo, c se smanjuje, zatim se b * a množi, a zatim oduzima. To jest, u stvari, skup operacija izgledao je ovako:

Int d = (c -) - (b * a);

Ali uz pomoć zagrada, mogli bismo promijeniti redoslijed operacija, na primjer, na sljedeći način:

Int a = 3; int b = 5; int c = 40; int d = (c - (- b)) * a; // a = 3 b = 4 c = 40 d = 108 Console.WriteLine ($ "a = (a) b = (b) c = (c) d = (d)");

Asocijativnost operatora

Kao što je gore navedeno, operacije množenja i dijeljenja imaju isti prioritet, ali kakav će onda biti rezultat u izrazu:

Int x = 10/5 * 2;

Da li ovaj izraz treba tumačiti kao (10/5) * 2 ili kao 10 / (5 * 2)? Zaista, u zavisnosti od interpretacije, dobićemo različite rezultate.

Kada operacije imaju isti prioritet, redoslijed evaluacije je određen asocijativnošću operatora. Postoje dvije vrste operatora ovisno o asocijativnosti:

    Lijevi asocijativni operatori koji se izvršavaju s lijeva na desno

    Desno asocijativni operatori koji se izvršavaju s desna na lijevo

Svi aritmetički operatori (osim prefiksa inkrementa i dekrementa) su lijevo asocijativni, odnosno izvršavaju se s lijeva na desno. Stoga se izraz 10/5 * 2 mora tumačiti kao (10/5) * 2, odnosno rezultat će biti 4.

Ne postoje implicitne logičke konverzije u C #, čak ni za cjelobrojne aritmetičke tipove. Stoga je notacija sasvim ispravna u jeziku C ++:

intk1 = 7;
ako (k1) Konzola. WriteLine(" uredu!");

nezakonito u C # programima. U fazi prevođenja, doći će do greške jer izračunati uvjet ima tip int, i implicitna konverzija ovog tipa u tip bool nedostaje.

U C #, stroža pravila važe i za logičke operacije. Dakle, ulaz ako(k1 && (x> y)), ispravno u C ++, rezultira greškom u

C # programira jer je && definiran samo za operande tipa bool, i u ovom izrazu, jedan od operanada je tipa int. U jeziku C #, u ovim situacijama, trebali biste koristiti sljedeće unose:

ako(k1>0)
ako((k1>0) && (x> y))

Logičke operacije su podijeljene u dvije kategorije: neke se izvode na logičkim vrijednostima operanada, dok druge izvode logičke operacije nad bitovima operanada. Iz tog razloga, u C # postoje dva unarna negacija operatora — logička negacija, specificirana operatorom!, i bitna negacija, specificirana operatorom ~. Prvi je definiran preko operanda tipa bool, drugi - preko operanda cjelobrojnog tipa, počevši od tipa int i više (int, uint, dugo, ulong). Rezultat operacije u drugom slučaju je operand u kojem je svaki bit zamijenjen svojim komplementom. Dajemo primjer:

/// < sažetak>
/// Bulovi izrazi
/// sažetak>
javnostivoidLogika() {
// operacije negacije ~,!
bool b1, b2;
b1= 2*2 == 4;
b2= b1;
// b2 = ~ b1;
uint j1= 7, j2;
j2= ~ j1;
// j2= !j1;
int j4= 7, j5;
j5= ~ j4;
Console.WriteLine ("uint j2= " + j2+ " int j5= " + j5);
} // Logika

U ovom isječku, izjave koje dovode do grešaka su komentirane. U prvom slučaju, pokušano je primijeniti operaciju negacije po bitu na izraz kao što je bool, u drugom, logička negacija je primijenjena na cjelobrojne podatke. Oba su ilegalna u C #. Obratite pažnju na različita tumačenja bitne negacije za neoznačene i predpisane tipove cijelih brojeva. Za varijable j5 i j2 bitni niz koji definira vrijednost je isti, ali se različito tumači. Odgovarajući zaključak je sljedeći:

uintj2 = 4294967288
intj5 = -8.

Binarne logičke operacije" && - kondicional I "i" || - uvjetno OR "definira se samo nad podacima tipa bool. Operacije se nazivaju uslovne ili kratke, jer izračunavanje drugog operanda zavisi od već izračunate vrednosti prvog operanda. Vrijednost uslovnih logičkih operacija leži u njihovoj efikasnosti u smislu vremena izvršenja. Često vam omogućavaju da procijenite logički izraz koji ima smisla, ali u kojem je drugi operand nedefiniran. Uzmimo za primjer klasični problem pretraživanja po uzorku u nizu, kada se traži element sa datom vrijednošću (uzorkom). Takav element u nizu može ili ne mora postojati. Evo tipičnog rješenja ovog problema u pojednostavljenom obliku, ali prenosi suštinu stvari:

// UslovnoI- &&
int [] ar= { 1, 2, 3 };
int search= 7;
int i= 0;
dok ((i< ar.Length)&& (ar [i]!= traži)){
i ++;
}
ako ja< ar.Length)Console.WriteLine ("Uzorakpronađeno");
ostaloConsole.WriteLine ("Uzoraknepronađeno");

Ako je vrijednost varijable traži(uzorak) ne odgovara nijednoj od vrijednosti elemenata niza ar, zatim posljednja provjera stanja petlje dokće se izvršiti ako vrijednost i, jednaka ar. Dužina. U ovom slučaju, prvi operand će primiti vrijednost false, i iako drugi operand nije definiran, petlja će se normalno završiti. Drugi operand je nedefiniran u posljednjoj provjeri jer je indeks elementa niza izvan opsega (u C # indeksiranje elementa počinje od nule).

Tri binarne bitne operacije - "& - AND", "| - OR "," ^ - XOR "koriste se na dva načina. Definirani su kao gore navedeni tipovi cijelih brojeva int, i preko logičkih tipova. U prvom slučaju koriste se kao bitne operacije, u drugom kao obične logičke operacije. Ponekad je potrebno da se oba operanda izračunaju u svakom slučaju, tada se ove operacije ne mogu izbjeći. Evo primjera njihove prve upotrebe:

// Logičke operacije u bitovimaI, Or, XOR(&,|,^)
int k2= 7, k3= 5, k4, k5, k6;
k4= k2& k3;
k5= k2 | k3;
k6= k2 ^k3;
Console.WriteLine ("k4= " + k4+ " k5= " + k5+ " k6= " + k6);

Izlazni rezultati:

k4 = 5 k5 = 7 k6 =2

Evo primjera pretraživanja uzorka pomoću logičkog I: i= 0;

traži= ar;
dok ((i< ar.Length)& (ar [i]!= traži)) i ++;
ako ja< ar.Length)Console.WriteLine ("Uzorakpronađeno");
ostalo cConsole.WriteLine ("Uzoraknepronađeno");

Zagarantovano je da ovaj fragment ima obrazac pretraživanja u nizu i fragment će biti uspješno izvršen. U istim slučajevima kada niz ne sadrži element traži, bit će izbačen izuzetak. Smisleno značenje takvog postupka - pojava izuzetka - može biti znak greške u podacima, što zahtijeva posebno postupanje sa situacijom.

Ovaj odeljak ukratko opisuje sintaksu i primenu svih C# operacija, osim nekih primarnih, o kojima će se raspravljati kasnije u toku srodnog materijala.

Povećanje i smanjenje

Operacije povećanja (++) i dekrementa (-) povećavaju i smanjuju operand za jedan. Imaju dva oblika notacije - prefiks kada je znak operacije napisan ispred operanda, i postfix... U prefiksnom obliku, prvo se mijenja operand, a zatim njegova vrijednost postaje rezultantna vrijednost izraza, au postfiksnom obliku vrijednost izraza je originalna vrijednost operanda, nakon čega se mijenja.

Standardne operacije povećanja postoje za cjelobrojne, simboličke, realne i finansijske vrijednosti.

Nova operacija

Nova operacija se koristi za kreiranje novog objekta. Format operacije:

novi tip ([argumenti])

Pomoću ove operacije možete kreirati objekte i referentnog i vrijednosnog tipa, na primjer:

objekat z = novi objekat (); int i = novi int (); // isto kao int i = 0;

Prilikom izvođenja nove operacije prvo se dodjeljuje potrebna količina memorije (za referentne tipove u hrpi, za tipove vrijednosti - na steku), a zatim tzv. zadani konstruktor, odnosno metoda kojom se objekt inicijalizira. Dodjeljuje se varijabla tipa vrijednosti default, što je jednako nuli odgovarajućeg tipa.

Operacije negacije

Aritmetička negacija(unarni minus -) obrće znak operanda. Standardni operator negacije definiran je za int, long, float, double i decimalni tip. Može se primijeniti na vrijednosti drugih tipova ako je za njih moguća implicitna konverzija u te tipove.

Logično negacija(!) je definiran za tip bool. Rezultat operacije je netačan ako je operand istinit, i istinit ako je operand lažan.

Bitwise negacija(~), koji se često naziva bitwise, invertuje svaki bit u binarnom prikazu int, uint, long ili ulong operanda.

Eksplicitna konverzija tipa

Operacija se koristi za eksplicitnu konverziju vrijednosti iz jednog tipa u drugi. Ovo je potrebno kada ne postoji implicitna konverzija. Gubitak informacija je moguć pri pretvaranju iz dužeg u kraći tip. Format operacije:

(tip) izraz

Ovdje je tip naziv tipa u koji se vrši konverzija, a izraz je najčešće naziv varijable, na primjer:

dužina b = 300; int a = (int) b; // podaci nisu izgubljeni int d = (bajt) a; // podaci su izgubljeni

Množenje, dijeljenje i ostatak dijeljenja

Operacija množenja(*) vraća rezultat množenja dva operanda. Standardna operacija množenja definirana je za int, uint, long, ulong, float, double i decimalni tip. Može se primijeniti na vrijednosti drugih tipova ako je za njih moguća implicitna konverzija u te tipove. Tip rezultata operacije jednak je "najvećem" od tipova operanda, ali ne manji od int.

Tabela 3.2. Simboli x i y označavaju konačne pozitivne vrijednosti, simbol z - rezultat realne operacije množenja. Ako je rezultat prevelik da bi bio predstavljen datim tipom, pretpostavlja se da je "beskonačno", ako je premalen, uzima se na 0. NaN (ne broj) znači da rezultat nije broj.

Tabela 3.2. Realni rezultati množenja
* + y -y +0 -0 + - NaN
+ x + z -z +0 -0 + - NaN
-x -z + z -0 +0 - + NaN
+0 +0 -0 +0 -0 NaN NaN NaN
-0 -0 +0 -0 +0 NaN NaN NaN
+ + - NaN NaN + - NaN
- - + NaN NaN - + NaN
NaN NaN NaN NaN NaN NaN NaN NaN

Operacija divizije(/) izračunava količnik prvog operanda podijeljen sa drugim. Standardna operacija dijeljenja definirana je za int, uint, long, ulong, float, double i decimalni tip. Može se primijeniti na vrijednosti drugih tipova ako za njih postoji implicitna konverzija u te tipove. Tip rezultata je određen pravilima konverzije, ali ne manje od int.

Ako su oba operanda cijeli brojevi, rezultat operacije se zaokružuje na najbliži cijeli broj. Ako je djelitelj nula, izbacuje se System.DivideByZeroException.

Ako je barem jedan od operanada realan, razlomak rezultata dijeljenja se ne odbacuje, a sve moguće vrijednosti su prikazane u tabeli 3.3.

Tabela 3.3. Realni rezultati podjela
/ + y -y +0 -0 + - NaN
+ x + z -z + - +0 -0 NaN
-x -z + z - + -0 +0 NaN
+0 +0 -0 NaN NaN +0 -0 NaN
-0 -0 +0 NaN NaN -0 +0 NaN
+ + - + - NaN NaN NaN
- - + - + NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN

Za finansijske vrijednosti (decimalni tip), dijeljenje sa 0 i prelivanje, izbacuju se odgovarajući izuzeci; nestanak reda rezultat je 0.

Rad po modulu(%) se također različito tumači za cjelokupne, stvarne i finansijske vrijednosti. Ako su oba operanda cijeli brojevi, rezultat operacije je x - (x / y) * y. Ako je djelitelj nula, izbacuje se System.DivideByZeroException.

Ako je barem jedan od operanada realan, rezultat operacije se izračunava po formuli x - n * y, gdje je n najveći cijeli broj manji ili jednak rezultatu dijeljenja x sa y. Sve moguće kombinacije vrijednosti operanda prikazane su u tabeli 3.4.

Tabela 3.4. Realni rezultati ostatka
% + y -y +0 -0 + - NaN
+ x + z z NaN NaN x x NaN
-x -z -z NaN NaN -x -x NaN
+0 +0 +0 NaN NaN +0 +0 NaN
-0 -0 -0 NaN NaN -0 -0 NaN
+ NaN NaN NaN NaN NaN NaN NaN
- NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN

Za finansijske vrijednosti (decimalni tip), prilikom dobivanja ostatka od dijeljenja sa 0 i prekoračenja, izbacuju se odgovarajući izuzeci, kada nestanak reda rezultat je 0. Predznak rezultata je znak prvog operanda.

Sabiranje i oduzimanje

Operacija sabiranja(+) vraća zbir dva operanda. Standardna operacija sabiranja definirana je za int, uint, long, ulong, float, double i decimalni tip. Može se primijeniti na vrijednosti drugih tipova ako za njih postoji implicitna konverzija u te tipove. Tip rezultata operacije jednak je "najvećem" od tipova operanda, ali ne manji od int.

Sve moguće vrijednosti za realne operande prikazane su u tabeli 3.5.

Tabela 3.5. Pravi rezultati zbrajanja
+ y +0 -0 + - NaN
x z x x + - NaN
+0 y +0 +0 + - NaN
-0 y +0 -0 + - NaN
+ + + + + NaN NaN
- - - - NaN - NaN
NaN NaN NaN NaN NaN NaN NaN

Operacija oduzimanja(-) vraća razliku dva operanda. Standardna operacija oduzimanja je definirana za int, uint, long, ulong, float, double i decimalni tip. Može se primijeniti na vrijednosti drugih tipova ako za njih postoji implicitna konverzija u te tipove. Tip rezultata operacije jednak je "najvećem" od tipova operanda, ali ne manji od int.

Ako su oba operanda cjelobrojna ili decimalna i rezultat operacije je prevelik da bi bio predstavljen specificiranim tipom, izbacuje se System.OverflowException.

Sve moguće vrijednosti rezultata oduzimanja za realne operande prikazane su u tabeli 3.6. Simboli x i y označavaju konačne pozitivne vrijednosti, simbol z označava rezultat realne operacije oduzimanja. Ako su x i y jednaki, rezultat je pozitivna nula. Ako je rezultat prevelik da bi se mogao predstaviti datim tipom, uzima se jednakim vrijednosti "beskonačnost" sa istim predznakom kao x - y, ako je premali, uzima se kao 0 sa istim predznakom kao x - y .

Tabela 3.6. Realni rezultati oduzimanja
- y +0 -0 + - NaN
x z x x - + NaN
+0 -y +0 +0 - + NaN
-0 -y -0 +0 - + NaN
+ + + + NaN + NaN
- - - - - NaN NaN
NaN NaN NaN NaN NaN NaN NaN

Operacije smjena

Operacije smjena (<< и >>) se primjenjuju na cjelobrojne operande. Oni pomiču binarni prikaz prvog operanda lijevo ili desno za broj bitova specificiranih drugim operandom.

At pomak lijevo (<< ) освободившиеся разряды обнуляются. При pomak udesno(>>) oslobođeni bitovi se popunjavaju nulama ako je prvi operand bez predznaka, a u suprotnom su potpisani. Standardne operacije pomaka su definirane za tipove int, uint, long i ulong.

Operacije odnosa i testovi jednakosti

Operacije veza (< , <= , >,> =, ==,! =) uporedi prvi operand sa drugim. Operandi moraju biti aritmetičkog tipa. Rezultat operacije je boolean tip, jednak true ili false. Pravila za izračunavanje rezultata su data u> y

true ako je x veći od y, false u suprotnom x<= y true ako je x manji ili jednak y, false u suprotnom x> = y tačno ako je x veće ili jednako y, u suprotnom netačno

Bitove logičke operacije

Bitove logičke operacije(&, |, ^) primjenjuju se na cjelobrojne operande i rade na njihovim binarnim prikazima. Prilikom izvođenja operacija, operandi se uparuju bit po bit (prvi bit prvog operanda sa prvim bitom drugog, drugi bit prvog operanda sa drugim bitom drugog itd.). Standardne operacije su definirane za tipove int, uint, long i ulong.

At bitwise konjunkcija(&), bit rezultata je 1 samo kada su odgovarajući bitovi oba operanda 1.

At bitwise disjunction(|), bit rezultata je 1 ako je odgovarajući bit najmanje jednog od operanada 1.

Za isključivo bitno OR() bit rezultata je 1 samo kada je odgovarajući bit samo jednog od operanada 1.

Uslovne logičke operacije

Uslovne logičke operacije I (&&) i OR (||) se najčešće koriste sa logičkim operandima. Rezultat logičke operacije je istinito, tada će rezultat uvjetne operacije biti vrijednost drugog operanda, u suprotnom - vrijednost trećeg operanda. Uvijek se evaluira ili drugi ili treći operand. Njihov tip može varirati.

Tip rezultata operacije zavisi od tipa drugog i trećeg operanda. Ako su operandi istog tipa, on postaje tip rezultata operacije.

Operacije dodjele

Operacije dodjele(=, + =, - =, * =, itd.) postavite novu vrijednost varijabli. Ove operacije se mogu koristiti u programu kao potpuni izrazi.

Format operacije jednostavan zadatak (= ):

varijabla = izraz

Mehanizam za izvođenje operacije dodjeljivanja je sljedeći: izraz se evaluira i njegov rezultat se pohranjuje u memoriju na adresi koja je određena imenom varijable lijevo od znaka operacije. Ono što je prethodno bilo pohranjeno u ovom memorijskom području je izgubljeno. Primjeri operatora dodjele:

sabiranje sa dodeljivanjem, prvi se dodaje drugom operandu, a rezultat se upisuje u prvi operand, odnosno izraz a + = b je kompaktniji izraz izraza a = a + b.

Rezultat složene operacije dodjeljivanja je vrijednost upisana u lijevi operand.

Operacije dodjele desno-asocijativna, odnosno izvode se s desna na lijevo, za razliku od većine drugih operacija (a = b = c znači a = (b = c)).

Pitanja i zadaci za samostalni rad studenta

  1. Gdje možete opisati varijable? Šta je uključeno u opis varijable?
  2. Šta se dešava kada koristite različite tipove operanda u izrazu? Navedite primjere.
  3. Navedite C # operacije po prioritetu.
  4. Šta je NaN? U kojim operacijama nastaje NaN?
  5. Na koje se vrste operanda primjenjuju operacije pomaka?
  6. Šta su izuzeci?
  7. Opišite principe obrade izuzetaka.

POGLAVLJE 10. Izrazi i operatori

U ovom poglavlju ćemo se osvrnuti na osnove bilo kojeg programskog jezika — njegovu sposobnost obavljanja zadataka i poređenja pomoću operatora. Vidjet ćemo koji su operatori u C # i koji je njihov prioritet, a zatim ćemo zaroniti u pojedinačne kategorije izraza za izvođenje aritmetičkih operacija, dodjeljivanje vrijednosti i poređenje operanada.

Operateri

Operator je simbol koji označava operaciju koja se izvodi na jednom ili više argumenata. Kada se naredba izvrši, dobije se rezultat. Sintaksa za korišćenje operatora se malo razlikuje od pozivanja metoda i trebalo bi da znate format izraza koji sadrže operatore u C # kao svoj džep. Kao i kod većine drugih jezika, semantika operatora u C # prati pravila i notacije koje su nam poznate iz škole. Osnovni operatori u C # uključuju množenje (*), deljenje (/), sabiranje i unarni plus (+), oduzimanje i unarni minus (-), modul (%) i dodeljivanje (=).

Operatori se koriste za dobivanje nove vrijednosti iz vrijednosti na kojima se radi. Ove početne vrijednosti se nazivaju operandi. Rezultat operacije mora biti pohranjen u memoriji. Ponekad se pohranjuje u varijablu koja sadrži jedan od originalnih operanada. C # kompajler generiše poruku o grešci ako nova vrijednost nije definirana ili pohranjena kada se koristi operator. Kod ispod ne mijenja vrijednosti. Kompajler će izdati poruku o grešci jer se aritmetički izraz koji ne mijenja barem jednu vrijednost obično smatra greškom.

klasa NoResultApp

{

javna static void Main ()

{

int i; int j;

i + j; // Greška jer rezultat nije dodijeljen ničemu. )>

Većina operatora radi samo sa numeričkim tipovima podataka kao što su Byte, Short, Long, Integer, Single, Double i Decimala. Izuzetak su operatori poređenja (== i! =). Također, u C #, možete koristiti + i - operatore za klasu String pa čak i koriste operatore inkrementa (++) i (-) za neobične jezičke konstrukcije kao što su delegati. O ovom drugom ću govoriti u 14. poglavlju.

Operaterski staž

Kada u jednom izrazu postoji više naredbi, prevodilac mora odrediti redosled njihovog izvršavanja. U ovom slučaju, kompajler se rukovodi pravilima tzv staž operatera. Razumijevanje prioriteta operatora je neophodno za ispravan pravopis izraza - ponekad rezultat možda neće biti očekivan.

Uzmite u obzir izraz 42 + 6 * 10. Ako dodate 42 i 6, a zatim pomnožite zbir sa 10, dobit ćete 480. Ako pomnožite 6 sa 10 i rezultatu dodate 42, dobit ćete 102. Prilikom kompajliranja koda, posebna komponenta kompajlera je - leksički analizator - odgovoran je za redosled čitanja ovog koda. Leksički analizator je taj koji određuje relativni prioritet heterogenih operatora u jednom izrazu. Da bi to učinio, koristi neku vrijednost - prioritet - svakog podržanog operatora. Najprije su dozvoljeni operateri višeg prioriteta. U našem primjeru, * operator ima prednost nad + operatorom, jer * upija(sada ću objasniti ovaj pojam) moji operandi prije nego to učini +. Objašnjenje leži u općim pravilima aritmetike: množenju i dijeljenju. uvijek imaju prednost nad sabiranjem i oduzimanjem. Vratimo se na primjer: kažu da je broj 6 progutao operator * u oba 42 + 6 * 10 i 42 * 6 + 10, tako da su ovi izrazi ekvivalentni 42 + (6 * 10) i (42 * 6) + 10.

Kako je staž definiran u C #

Sada da vidimo kako je prioritet operatora definisan u C #. Operatori su navedeni u opadajućem redoslijedu prioriteta (Tabela 10-1). Kasnije ću detaljnije govoriti o različitim kategorijama operatora podržanih u C #.

Tab. 10-1. Prioritet operatora u C #.

Kategorija operatera Operateri
Jednostavno (x), xy, f (x), a [x], x ++, x -, novo, typeof, sizeof, označeno, neoznačeno
Unary +, -,!, ++ x, - x, (T) x
Multiplikativno *,/, %
Dodatak +, -
Shift «, »
Stav <, >, <=, >=, je
Jednakost ==
Logično I (I) &
Logički isključivi OR (XOR) ^
logičko ILI (ILI) 1
Uslovno I &&
Uslovni IL I (ILI) II
Stanje 9-
Zadatak = *= /= % = , + = , -= « = , » = , &=, ^ = , =

Lijeva i desna asocijativnost

Asocijativnost određuje koji dio izraza treba prvo procijeniti. Na primjer, rezultat gornjeg izraza može biti 21 ili 33, ovisno o tome koja će se asocijativnost koristiti za operator "-": lijevo ili desno.

Operator - ima lijevu asocijativnost, odnosno prvo se računa 42-15, a zatim se od rezultata oduzima 6. Da ima desnu asocijativnost prvo bi se izračunala desna strana izraza (15-6), a zatim rezultat bi se oduzelo od 42.

Svi binarni operatori (operatori sa dva operanda), osim operatora dodjeljivanja, - lijevo-asocijativno, odnosno obrađuju izraze s lijeva na desno. Na ovaj način, a + b+ sa - isto kao (a + b) + c, gdje se prvo izračunava a + b, a zatim se zbroj sabira With. Dodjela i uvjetni operatori - desno-asocijativno, odnosno obrađuju izraze s desna na lijevo. Drugim riječima, a = b = c ekvivalentno a = (b= Sa). Mnogi ljudi naiđu na ovo kada žele staviti nekoliko naredbi o dodjeli u jedan red, pa pogledajmo ovaj kod:

korištenje sistema;

klasa RightAssocApp (

public static void Main () (

int a = 1; int b = 2; int c = 3;

Console.WriteLine ("a = (0) b = (1) c = (2>", a, b, c); a = b = c;

Console.WriteLine ("Nakon" a = b = c -: a = (0) b = (1) c = (2) ", a, b, c);>>

Rezultat pokretanja ovog primjera je sljedeći:

a = 1 b = 2 c = 3

Nakon "a = b = c": a = 3 b = 3 o = 3

Evaluacija izraza s desna na lijevo može u početku biti zbunjujuća, ali pristupimo tome na ovaj način: ako je operator dodjeljivanja lijevo asocijativan, kompajler bi prvo morao procijeniti a = b, onda a bi jednako 2 i tada b= s i kao rezultat b bilo bi 3. Krajnji rezultat bi bio a = 2 b = 3 c = 3. Očigledno, ovo nije ono što očekujemo kada pišemo a= b= sa, i zato su dodeljivanje i uslovni operatori desno-asocijativni.

Praktična upotreba

Ništa nije više problematično od pronalaženja greške napravljene samo zato što programer nije poznavao pravila prvenstva i asocijativnosti. Naišao sam na poruke na mail konferencijama u kojima su očigledno razumni ljudi predložili neku vrstu mehanizma samodokumentiranja - korištenje razmaka za označavanje operatera da, po njihovom mišljenju, imaju staž. Na primjer, pošto znamo da operator množenja ima prednost nad operatorom sabiranja, trebali bismo napisati nešto poput ovoga, u kojem razmaci označavaju implicirano staž:

a = b * c + d;

Ovaj pristup je u osnovi pogrešan: kompajler ne može pravilno raščlaniti kod ako specifična sintaksa nije definirana. Kompajler analizira kod prema pravilima definisanim od strane programera kompajlera. S druge strane, postoje zagrade koje se koriste za eksplicitno označavanje prioriteta i asocijativnosti. Na primjer, izraz a= b * c + d može se prepisati kao a = (b * c) + d ili kako a= b * (c + d) a kompajler će prvo procijeniti izraz u zagradama. Ako postoji više parova zagrada, kompajler će prvo procijeniti izraze u zagradama, a zatim cijeli izraz, na osnovu opisanih pravila prioriteta i asocijativnosti.

Moje je čvrsto uvjerenje da zagrade uvijek treba koristiti kada postoji više operatora u izrazu. Preporučujem da to učinite čak i ako razumijete redoslijed izračunavanja, jer ljudi koji će održavati vaš kod možda neće biti toliko pismeni.

C # operatori

Najispravnije je posmatrati operatere po stažu. U nastavku ću opisati najčešće operatore.

Jednostavni operateri

  • (x) Ovo je varijacija operatora zagrade za kontrolu redosleda izračunavanja, kako u matematičkim operacijama tako iu pozivima metoda.
  • xy Operator točka se koristi za označavanje člana klase ili strukture. Evo X predstavlja entitet koji sadrži člana at.
  • f (x) Ova vrsta operatora zagrade se koristi za navođenje argumenata metoda.
  • a [x] Uglaste zagrade se koriste za indeksiranje niza. Ove zagrade se takođe koriste zajedno sa indekserima kada se objekti mogu tretirati kao niz. Za indeksere, pogledajte poglavlje 7.
  • h ++ O operatoru inkrementa ćemo govoriti posebno u odjeljku "Operatori inkrementa i dekrementa".
  • x- O operatoru dekrementa ćemo govoriti kasnije.
  • new Ovaj operator se koristi za instanciranje objekata na osnovu definicije klase.

vrstu

Refleksija(refleksija) je mogućnost dobivanja informacija o tipu u vrijeme izvođenja. Ove informacije uključuju imena tipova, klasa i članova strukture. U NET Frameworku ova funkcionalnost je povezana s klasom Sistem. Tip. Ova klasa je korijen svih operatora refleksije i može se dobiti pomoću operatora vrstu. Nećemo sada ulaziti u detalje refleksije (to ćemo učiniti u poglavlju 16), ali evo jednostavnog primjera koji ilustruje koliko je lako koristiti operator vrstu"da biste dobili gotovo sve informacije o tipu ili objektu tokom izvršavanja programa:

korištenje sistema;

koristeći System.Reflection;

javna klasa jabuka (

public int nSeeds;

javna praznina Ripen ()

{

> >

javna klasa TypeOfApp (

public static void Main () (

Tip t = typeof (Apple);

string className = t.ToStringO;

Console.IgShip ("\ nInformacija 0 klasa (O)", className);

Console.WriteLine ("\ nMeroflH (0)", className); Konzola. WriteLine ("-------"); Metodlnfo metode = t.GetMethodsO;

foreach (metoda MethodInfo u metodama)

Console.WriteLine (metod.ToSt ring ());

}

Console.WriteLine ("\ nBce members (O)", className); Konzola. Writel_ine ("--------"); Memberlnfo allMembers = t.GetMembersO; foreach (član info član u svim članovima)

{

Konzola. WriteLine (member.ToStringO);

} > }

Ovaj program sadrži klasu Apple koja ima samo dva člana: polje nSeeds i metod Ripen. Prvo, korištenjem operatora vrstu i ime klase dobijam objekat Sistem. Tip, koji se zatim pohranjuje u varijablu t. WITH u ovom trenutku mogu koristiti objekt Sistem. Tip da dobijete sve metode i članove klase Apple. Ovo se radi pomoću metoda GetMethods i GetMembers respektivno. Rezultati ovih metoda se zapisuju u standardni izlaz na sljedeći način:

Apple Class Information Apple Metode

Int32 GetHashCodeQ

System.String ToStringQ

Void RipenO

System.Type GetTypeO

Svi Apple članovi

Int32 nSeeds

Int32 GetHashCodeO

Boolean jednak (System.Object)

System.String ToStringO

Void RipenO

System.Type GetTypeO

Prije nego što krenem dalje, želim istaći dvije stvari. Prvo, primijetite da su naslijeđeni članovi klase također zaključeni. Pošto klasa nije eksplicitno izvedena iz druge klase, znamo da svi članovi nisu definisani u klasi Apple naslijediti od implicitne osnovne klase System.Object. Drugo, objekat System.Type može se dobiti metodom GetType. Ovo je naslijeđeno od System.Object metoda vam omogućava da radite sa objektima, a ne klasama. Bilo koji od dva isječka ispod može se koristiti za dobivanje objekta Sistem. Tip.

II Dobivanje System.Type objekta zasnovanog na definiciji klase. Tip t1 = typeof (Apple);

// Dobivamo objekt System.Type iz objekta. Apple jabuka = ​​novi AppleQ; Tip t2 = apple.GetTypeO;

sizeof

Operater sizeof koristi se za dobivanje veličine navedenog tipa u bajtovima. Pri tome imajte na umu dva izuzetno važna faktora. prvo, sizeof može se primijeniti samo na tipove dimenzija. Stoga, iako se može koristiti za članove klase, ne može se koristiti za klase kao takve. drugo, sizeof može se koristiti samo u metodama ili blokovima koda označenim kao nesigurno. WITH Sa ovakvom vrstom koda ćemo se upoznati u poglavlju 17. Evo primjera korištenja operatora sizeof u metodi klase označene kao nesigurno:

korištenje sistema;

klasa BasicTypes (

// Napomena: Kod koji koristi operator sizeof // mora biti označen kao nesiguran, statički nesiguran public void ShowSizesQ (

Console.WriteLine ("\ nPa3Mephi glavni tipovi"); Console.WriteLine ("Pa3Mep short = (0)", sizeof (short)); Console.WriteLine ("Pa3Mep int = (0)", sizeof (int)); Console.Writel_ine ("Pa3Mep long = (0)", sizeof (long)); Console.WriteLine ("Pa3Mep bool = (0)", sizeof (bool)); ))

class UnsafeUpp

{

nesigurna javna statička void MainQ

{

BasicTypes.ShowSizes ();

} }

Evo izlaza iz ove aplikacije:

Veličine osnovnih tipova Veličina kratka = 2 Veličina int = 4 Veličina duga = 8 Veličina bool = 1

Operater sizeof može se koristiti za definiranje dimenzija ne samo za jednostavne ugrađene tipove, već i za prilagođene tipove dimenzija kao što su strukture. Međutim, rezultati sizeof možda nije očigledno:

// Korištenje operatora sizeof. korištenje sistema;

struct StructWithNoMembers

struct StructWithMembers

{

kratki s;

int i;

long 1;

bool b; )

struct CompositeStruct

{

StructWithNoMembers a; StructWithMembers b;

StructWithNoMembers c; )

klasa UnSafe2App (

nesigurna javna statička void Main () (Console.WriteLine ("\ nPa3Mep StructWithNoMembers struktura = (0)",

sizeof (StructWithNoMembers)); Console.WriteLine ("\ nPa3Mep StructWithMembers struktura = (0)",

sizeof (StructWithMembers)); Console.WriteLine ("\ nPa3Mep CompositeStruct struktura = (0)",

sizeof (CompositeStruct)); ))

Iako se može pretpostaviti da će ova aplikacija ispisati 0 za strukturu bez članova (StructWithNoMembers), 15 za strukturu sa četiri člana baznih tipova (StructWithMembers) i 15 za strukturu koja agregira dvije prethodne (CompositeStruct), u stvarnosti će rezultat biti ovakav:

Veličina StructWithNoMembers struktura = 1 Veličina StructWithMembers struktura = 16

Veličina kompozitne strukture = 24

Objašnjenje za ovo je način da se konstrukt zadrži struct kompajler u izlaznoj datoteci gdje prevodilac primjenjuje poravnanje i padding. Na primjer, ako je struktura veličine 3 bajta i poravnata na granicama od 4 bajta, kompajler će automatski dodati 1 bajt strukturi, a operator sizeofće označiti da je veličina strukture 4 bajta. Ne zaboravite to imati na umu kada dimenzionirate strukture u C #.

provjereno i neprovjereno

Ova dva operatora kontrolišu provjeru prelijevanja prilikom izvođenja matematičkih operacija.

Matematički operatori

C #, kao i većina drugih jezika, podržava osnovne matematičke operatore: množenje (*), dijeljenje (/), sabiranje (+), oduzimanje (-) i modul (%). Svrha prva četiri operatora je jasna iz njihovih imena; operator modula formira ostatak celobrojnog dijeljenja. Evo malog koda koji ilustruje upotrebu matematičkih operatora:

korištenje sistema;

klasa MathOpsApp

{

javna statička void MainQ

{

// Klasa System.Random je dio // biblioteke klasa .NET Framework. U svom zadanom konstruktoru, // Next metoda koristi trenutni datum/vrijeme kao svoju // početnu vrijednost. Random Rand = novi RandomO; int a, b, c;

a = rand.Sljedeće () % stotinu; // Granična vrijednost 99. b = rand.NextO % stotinu; // Granična vrijednost 99.

Console.WriteLine ("a = (0) b = (1)", a, b);

c = a * b;

Console.WriteLineC "a * b = (0)", c);

// Imajte na umu da se ovdje koriste cijeli brojevi. // Stoga, ako je a manji od b, rezultat će uvijek // biti 0. Da biste dobili precizniji rezultat // morate koristiti varijable tipa double ili float, c = a / b; Console.WriteLineC "a / b = (0)", c);

Console.WriteLineC "a + b = (0)", c);

Console.WriteLineC "a - b = (0)", c);

Console.WriteLineC "a X b = (0)", c); >>

Unary Operators

Postoje dva unarna operatora: plus i minus. Unarni minus operator govori kompajleru da je broj negativan. Dakle, u sljedećem kodu aće biti jednako -42:

korištenje sistema; korištenje sistema;

klasa UnarylApp (

javna static void Main ()

{

int a = 0;

a = -42;

Console.WriteLine ("(0)", a); ))

Međutim, nejasnoća se pojavljuje u ovom kodu: koristeći System;

klasa Unary2App<

public static void Main () (

int a; int b = 2; int c = 42;

a = b * -c;

Console.WriteLine ("(0)", a); >>

Izraz a= b * -c nije sasvim jasno. Opet, korištenje zagrada će pojasniti ovaj izraz:

// Kada koristite zagrade, očito je da // množimo b negativnim brojem c. a = b * (-c);

Ako unarni minus vraća negativnu vrijednost operanda, možda mislite da unarni plus vraća pozitivno. Međutim, unarni plus samo vraća operand u njegovom originalnom obliku i ne radi ništa drugo, odnosno ne utiče na operand. Na primjer, pokretanje ovog koda će dati vrijednost -84:

korištenje sistema;

klasa UnarySapp (

javna statička void MainQ (

a = b * (+ c);

Console.WriteLine ("(0)", a); ))

Da biste dobili pozitivnu vrijednost, koristite funkciju Math.Abs. Ovaj kod će ispisati vrijednost 84:

korištenje sistema;

klasa Unary4App

{

javna static void Main ()

int a; int b = 2; int c = -42;

a = b * Math.Abs ​​(c); Console.Writel_ine ("(0)", a); ))

Posljednji unarni operator koji sam spomenuo je T (x). Ovo je varijacija operatora zagrade koji vam omogućava prebacivanje s jednog tipa na drugi. Budući da se može preopteretiti kreiranjem prilagođene transformacije, raspravljat ćemo o tome u 13. poglavlju.

Složeni operatori dodjele

Složeni operator dodjeljivanja - to je kombinacija binarnog operatora i operatora dodjeljivanja (=). Sintaksa za ove operatore je sljedeća:

refren = y

gdje op - ovo je operater. Imajte na umu da kada to radite, lijeva vrijednost (lvalue) nije zamijenjeno pravom (rvalue), složeni iskaz ima isti učinak kao:

x = x op at

i lvalue koristi se kao osnova za rezultat operacije.

Zapazite da sam rekao "ima isti efekat." Kompajler ne prevodi izraz kao što je x + = 5 in X= x + 5, ponaša se logično. Posebnu pažnju treba obratiti na slučajeve kada lvalue je metoda. Uzmite u obzir kod:

korištenje sistema;

klasa CompoundAssignmentlApp (

zaštićeni lnt elementi;

public int GetArrayElementO

{

povratni elementi;

}

CompoundAssignment1App () (

elementi = novi int;

elemenata = 42;

}

public static void Main () (

CompoundAssignmentlApp app = nova CompoundAsslgnment1App ();

Console.WrlteLine ("(0>", app.GetArrayElement ());

app.GetArrayElement () = app.GetArrayElement () + 5; Console.WriteLine ("(0)", app.GetArrayElement ()); ). )

Obratite pažnju na označenu liniju - poziv metode Compound-AssignmentlApp.GetArrayElement a zatim mijenjam prvi element - ovdje sam koristio sintaksu:

x = x op at

Ovako će se generirati MSIL kod: // Neučinkovita tehnika: x = x op y.

Upravljana metoda public hldebyslg static void Main () 11

Entrypolnt

// Veličina koda 79 (Ox4f) .maxstack 4

Lokalno stanovništvo (klasa CompoundAssignmentlApp V_0)

IL_0000: newobj instanca void CompoundAssignmentlApp ::. Ctor () IL_0005: stloc.O IL_0006: Idstr "(OG IL_OOOb: ldloc.0 ILJJOOc: poziv instance int32

CompoundAssignmentlApp :: GetArrayElementO

IL_0011: ldc.14.0

IL_0012: Idelema ["mscorlib"] System.Int32

IL_0017: okvir [1 mscorlib "] System.Int32

IL_001c: call void ["mscorlib 1] System.Console :: WriteLine (klasa System.String, klasa System.Object)

IL_0021: ldloc.0

IL_0022: poziv instance int32 CompoundAssignmentlApp :: GetArrayElementO

IL_0027: Idc.i4.0

IL_0028: ldloc.0

IL_0029: poziv instance int32 CompoundAssignmentlApp:: GetArrayElementO

IL_002e: Idc.i4.0

IL_002f: ldelem.14

IL_0030: ldc.14.5

IL_0031: dodaj

IL_0032: stelem.14

IL_0033: Idstr "(0)"

IL_0038: ldloc.0

IL_0039: poziv

instanca int32 CompoundAssignmentlApp :: GetArrayElement () IL_003e: ldc.14.0

IL_003f: Idelema ["mscorlib"] Systera.Int32 IL_0044: box ["msoorlib"] System.Int32 IL_0049: call void ["mscorlib"] System.Console :: WriteLine

(klasa System.String, klasa System.Object) IL_004e: ret

) // kraj metode "CompoundAssignmentlApp :: Main"!

Pogledajte isprepletene linije: metoda CompoundAssignmentlApp.Get-ArrayElement se zapravo zove dvaput! Ovo je u najmanju ruku neefikasno, a možda i štetno u zavisnosti od toga šta još metoda radi.

Sada pogledajmo drugi kod koji koristi sintaksu složenog operatora dodjeljivanja:

korištenje sistema;

klasa CompoundAssignment2App (

zaštićeni int elementi;

public int GetArrayElementO

povratni elementi;

}

CompoundAssignment2App () (

elementi = novi int;

elemenata = 42;

}

public static void Main () (

CompoundAssignment2App app = nova CompoundAssignment2App ();

Console.WriteLine ("(0)", app.GetArrayElement ());

app.GetArrayElement () + = 5; Console.WriteLine ("(0)", app.GetArrayElement ()); ))

Upotreba složenog operatora dodjeljivanja rezultirat će mnogo efikasnijim MSIL kodom:

// Efikasnija tehnika: x op = y.

Metoda public hidebysig static void Main () il upravlja

\ {

\ .ulazna tačka

I // Veličina koda 76 (Ox4c) \ .maxstack 4

Lokalni (klasa CompoundAssignmentlApp V_0, int32 V_1) \ IL_0000: newobj instanca void CompoundAssignmentlApp ::. Ctor () \ IL_0005: stloc.O 1 IL_0006: Idstr "(0)" 1 IL_OOOb: ldloc.0 IL_OOOc: poziv instance int32

CompoundAssignmentlApp :: GetArrayElementO IL_0011: Idc.i4.0

IL_0012: Idelema [mscorlib "] System.Int32 IL_0017: kutija [> mscorlib -] System.Int32 lL_001c: call void [" mscorlib "] System.Console :: WriteLine

(klasa System.String, klasa System.Object) IL_0021: ldloc.0 IL_0022: instanca poziva int32

CompoundAssignmentlApp :: GetArrayElement ()

IL_0027: dup

IL_0028: stloc.1

IL_0029: Idc.i4.0

IL_002a: ldloc.1

IL_002b: Idc.i4.0

IL_002c: ldelem.14

IL_002d: ldc.14.5

IL_002e: dodaj

IL_002f: stelem.14

IL_0030: Idstr "(0)"

IL_0035: ldloc.0

IL_0036: poziv instance int32

CompoundAssignmentlApp :: GetArrayElementO IL_003b: Idc.i4.0

IL_003c: Idelema ["mscorlib"] System.Int32

IL_0041: okvir [mscorlib "] System.Int32

IL_0046: call void ["mscorlib"] System.Console :: WriteLine

(klasa System.String, klasa System.Object)

IL_004b: ret) // kraj metode "CompoundAssignmentlApp :: Main"

Možete vidjeti korištenu MSIL naredbu dup. Duplicira gornju stavku na steku, stvarajući tako kopiju vrijednosti koju je metod vratio CompoundAssignmentlApp.Get element niza. I

To pokazuje da iako u suštini x + = y ekvivalentno x = x + y, MSIL kod je različit u oba slučaja. Ova razlika bi vas trebala natjerati da se zapitate koju sintaksu koristiti u svakom slučaju. Opće pravilo i moja preporuka je da koristite složene operatore dodjele kad god i gdje god je to moguće.

Operatori povećanja i dekrementa

Uvedeni u C i preneseni na C++ i Javu, operatori inkrementa i dekrementa omogućavaju vam da koncizno izrazite da želite da povećate ili smanjite brojčanu vrijednost za 1. To jest, / ++ je ekvivalentno dodavanju 1 trenutnoj vrijednosti od / ".

Prisustvo dva oblika operatora povećanja i dekrementa ponekad je zbunjujuće. Prefiks i postfix tipovi ovih operatora se razlikuju u trenutku u kojem se vrijednost mijenja. U verziji s prefiksom operatora inkrementa i dekrementa (++ ai - a odnosno) prvo se izvodi operacija, a zatim se kreira vrijednost. U postfix verziji (a ++ i a-) prvo se kreira vrijednost, a zatim se izvodi operacija. Razmotrimo primjer:

korištenje sistema;

klasa IncDecApp (

javna statička praznina Foo (int j)

{

Console.WriteLine ("IncDecApp.Foo j = (0)", j);

>

public static void Main () (

int i = 1;

Console.WriteLineC "flo poziva Foo (i ++) = (0)", i);

Console.WriteLine ("Nakon poziva Foo (i ++) = (0)", i);

Console.WriteLine ("\ n");

\ Console.WriteLineC "flo poziva Foo (++ i) = (0)", i);

\ Foo (++ l);

\ Console.WrlteLine ("Nakon poziva Foo (++ i) = (0)", i);

l Rezultat će izgledati ovako:

Prije pozivanja Foo (i ++) = 1

IncDecApp.Foo j = 1

Nakon poziva Foo (i ++) = 2

Prije pozivanja Foo (-n-i) = 2

IncDecApp.Foo j = 3

Nakon poziva Foo (++ i) = 3

Razlika je kada vrijednost se kreira i operand se mijenja. Prilikom poziva Foo (i ++) vrijednost / "je proslijeđen (nepromijenjen) metodi Foo i poslije metoda return / se povećava za 1. Pogledajte gornji MSIL kod: naredbu dodati izvršava se nakon što je vrijednost gurnuta u stek.

IL.0013: ldloc.0

IL.0014: dup

IL_0015: Idc.i4.1

IL_0016: dodaj

IL_0017: stloc.O

IL_0018: call void IncDecApp :: Foo (int32)

Pogledajmo sada prefiks operatorski obrazac koji se koristi u pozivu Foo (++ a). U ovom slučaju, MSIL kod će biti drugačiji. U ovom slučaju, komanda dodati izvedeno prije kako se vrijednost gura u stog za kasnije pozivanje metode Foo.

IL.0049: ldloc.0

IL_004a: Idc.i4.1

IL_004b: dodaj

IL_004c: dup

IL_004d: stloc.O

IL_004e: call void IncDecApp :: Foo (int32)

Relacioni operatori

Većina operatora vraća numeričke vrijednosti. Što se tiče relacijskih operatora, oni generiraju boolean rezultat. Umjesto / umjesto izvođenja matematičkih operacija nad skupom operanda, / relacijski operatori analiziraju odnos između operanada i povratak značenje tačno, ako je omjer tačan, lažno - ako je / lažno.

Operatori poređenja

Za relacijske operatore pozvane operatori poređenja, referirati) -je "manje" (<), «меньше или равно» (<=), «больше» (>), Veće ili Jednako (> =), Jednako (==) i Nije jednako (! =). Primjena ovih operatora na brojeve je razumljiva, ali kada se koriste sa objektima, njihova implementacija nije tako očigledna. Evo primjera:

korištenje sistema;

klasa NumericTest (

javni numerički test (int 1)

{

this.i = i;

>

zaštićeni int 1; )

klasa RelationalOpslApp (

public static void Main () (

NumericTest testl = novi NumericTest (42); NumericTest test2 = novi NumericTest (42);

Console.WriteLine ("(0)", testl == test2); >)

Ako ste Java programer, znate šta će se ovde dogoditi. Međutim, C++ programeri će najvjerovatnije biti iznenađeni kada vide rezultat false. Dozvolite mi da vas podsjetim da kada kreirate instancu objekta, dobijate vezu do njega. To znači da kada naiđe na operator relacije koji upoređuje dva objekta, C# kompajler ne upoređuje sadržaj objekata, već njihove adrese. Da biste ovo bolje razumjeli, razmotrite MSIL kod:

\ .method public hldebysig static void MainQ il upravlja

\ .entrypoint

\ // Veličina koda 39 (0x27)

1 .maxstack 3

Lokalno stanovništvo (klasa NumericTest V_0, \ klasa NumericTest V_1, 1 bool V_2)

ILJJOO: Idc.i4.s 42

1L_0002: newobj instanca void NumericTest ::.Ctor (int32)

IL_0007: stloc.O

IL_0008: Idc.i4.s 42

IL_OOOa: newobj instanca void NumericTest ::. Ctor (int32)

IL_OOOf: stloc.1

IL_0010: Idstr "(0)"

IL_0015: ldloc.0

IL_0016: ldloc.1

IL_0017: ekv

IL_0019: stloc.2

IL_001a: Idloca.s V_2

IL_001c: box ["mscorlib"] System.Boolean

IL_0021: call void ["mscorlib"] System.Console :: WriteLine

(klasa System.String, klasa System.Object)

IL_0026: ret) // kraj metode "RelationalOpslApp :: Main"

Pogledaj liniju .locals. Kompajler ukazuje da je metoda Main tri lokalne varijable. Prva dva su objekti numerički test, treća je logička varijabla. Sada pređimo na linije IL_0002 i IL_0007. Ovo je mjesto gdje se objekt instancira testl, i povežite se na njega sa stloc je pohranjen u prvoj lokalnoj varijabli. Važno je, međutim, da MSIL zadrži adresu novokreiranog objekta. Zatim u redovima IL_OOOa i IL_OOOf vidite MSIL kodove za kreiranje objekata test2 i pohranjivanje vraćene reference u drugu lokalnu varijablu. Konačno, u redovima 1LJ) 015 i IL_0016 lokalne varijable se guraju u stog naredbom idloc, i u redu IL_0017 tim siva uspoređuje dvije vrijednosti na vrhu steka (tj. reference objekata testl i testl). Povratna vrijednost se pohranjuje u treću lokalnu varijablu, a zatim se prikazuje metodom System.Console. WriteLine.

Ali kako uporediti članove dvaju objekata? Odgovor je korištenje implicitne osnovne klase svih .NET Framework objekata. Naime, za ove svrhe, klasa System.Object postoji metoda Jednako. Na primjer, sljedeći kod uspoređuje sadržaj objekata i izlaza, kao što biste očekivali, istina: ja

korištenje sistema; /

klasa RelationalOps2App

(/ public static void Main () (

Console.WriteLine ("(0)", testl.Equals (test2));

Primjer RelationalOpslApp koristi "domaću" klasu (Nu-mericTest) i u drugom primjeru - .NET klasa (Decimalno). Poenta je da metoda System.Object.Equals treba zaobići da bi se napravilo pravo poređenje članova. Otuda metoda Jednako sa klasom Nume-ricTest neće raditi jer nismo nadjačali metodu. A evo i časa Decimala nadjačava metodu koju nasljeđuje jednako, i u ovom slučaju će sve raditi.

Drugi način za poređenje objekata je korištenje preopterećenje operatera(preopterećenje operatera). Preopterećenje operatora definira operacije koje se izvode na objektima određenog tipa. Na primjer, za objekte string+ operator ne vrši sabiranje, već spaja nizove. Preopterećenje operatera ćemo pokriti u 13. poglavlju.

Jednostavni operatori dodjeljivanja

Poziva se vrijednost na lijevoj strani operatora dodjeljivanja vrijednost, a na desnoj strani - rvalue. As rvalue može biti bilo koja konstanta, varijabla, broj ili izraz čiji je rezultat kompatibilan lvalue. U međuvremenu lvalue mora biti varijabla određenog tipa. Poenta je da se vrijednost kopira s desne strane na lijevu. Stoga se za novu vrijednost mora dodijeliti fizički adresni prostor. Na primjer, možete napisati / "= 4, pošto ima mjesta za / u memoriji - bilo na steku ili na hrpi - ovisno o vrsti /. A evo i operatera 4 = 1 ne može se izvršiti jer je 4 vrijednost, a ne varijabla čiji se sadržaj u memoriji može promijeniti. Usput, primijetit ću da u C # kao lvalue može biti varijabla, svojstvo ili indekser. Za više informacija o svojstvima i indeksatorima, pogledajte Poglavlje 7. U ovom poglavlju koristim varijable radi jednostavnosti. Dok je dodeljivanje numeričkih vrednosti dovoljno jasno, objekti su komplikovaniji. Dozvolite mi da vas podsjetim da kada imate posla sa objektima, manipulišete stavkama koje se ne naslagaju koje je lako kopirati i premještati. U slučaju objekata, zapravo imate samo reference na neke od entiteta za koje je memorija dinamički dodijeljena. Stoga, kada pokušate dodijeliti objekt (ili bilo koji tip reference) varijabli, ne kopiraju se podaci, kao što je slučaj s dimenzionalnim tipovima, već reference.

Recimo da imate dva objekta: testl i test2. Ako navedete testl= test2, testl neće biti kopija test2. Oni će se poklopiti! Objekt testl ukazuje na istu memoriju kao test2, i sve promjene na objektu testlće dovesti do promjena test2. Evo programa koji to ilustruje:

korištenje sistema;

klasa Foo (

public int i; )

klasa RefTestlApp (

javna statička praznina MainO (

Foo testl = novi Foo (); testl.i = 1;

Foo test2 = novi Foo (); test2.i = 2;

Console.WriteLine ("Prije dodjele objekata"); Console.WriteLine ("test1.i = (0>", testl.i); Console.WriteLine ("test2.i = (0)", test2.i); Console.WriteLine ("\ n");

testl = test2;

Console.Writel_ine ("Nakon dodjeljivanja objekata");

Console.WriteLine ("test1.i = (0)", testl.i); Console.WriteLine ("test2.i = (0)", test2.i); Console.WriteLine ("\ n");

testl.i = 42; ;"

Console.WriteLine ("Nakon promjene samo člana TEST1"); Console.WriteLine ("test1.i = (0)", testl.i); Console.WriteLine ("test2.i = (0)", test2.i); Console.WriteLine ("\ n"); ))

Pokretanjem ovog koda vidjet ćete:

Prije dodjele objekta

test1.i = 1

test2.i = 2

Nakon dodjele objekta

testt.i = 2

test2.i = 2

Nakon promjene samo člana TEST1

test1.i = 42

test2.i = 42

Hajde da vidimo šta se dešava u svakoj fazi ovog primera. foo - to je jednostavna k klasa sa samo jednim članom, /. U metodi Main kreiraju se dvije instance ove klase: testl i test2- i njihovi članovi i su postavljene na 1 i 2 respektivno. Ove vrijednosti se zatim izlaze, i prema očekivanjima testl.i jednako 1, a test2.i- 2. I tu počinje zabava! U sljedećem redu do objekta testl dodeljeno test2. Java čitaoci znaju šta je sledeće. Međutim, većina C++ programera će očekivati ​​člana/objekat testl je sada jednako članu objekta test2(pod pretpostavkom da kada se takva aplikacija kompajlira, neka vrsta operatora za kopiranje članova objekta će biti izvršena). Izgleda da prikazani rezultat to potvrđuje. Međutim, u stvarnosti, veza između objekata sada je mnogo dublja. Članu dodijelite vrijednost 42 testl.i i ponovo odštampajte rezultat. I?! Kada se objekat promeni testl promijenio i testZ To se dogodilo zbog činjenice da je objekat testl dosta. Nakon dodjele test2 objekat testl je izgubljen jer više nije referenciran u aplikaciji i kao rezultat toga je "očišćen" od strane sakupljača smeća (GC). Sad testl i test2 pokažite na istu memoriju na hrpi. Stoga, kada se promijeni jedna varijabla, korisnik će vidjeti i drugu promjenu.

Obratite pažnju da se štampaju poslednja dva reda: iako se samo vrednost promenila u kodu testl.i, značenje test2.i takođe promenio. Još jednom, obje varijable sada ukazuju na istu memorijsku lokaciju - ovo je ponašanje koje bi Java programeri očekivali. Međutim, to nimalo ne ispunjava očekivanja C++ programera, budući da se u ovom jeziku vrši upravo kopiranje objekata: svaka varijabla ima svoju jedinstvenu kopiju svojih članova i promjene u jednom objektu ne utiču na drugi. Budući da je ovo ključ za razumijevanje načina na koji objekti rade u C #, hajde da malo skrenemo i vidimo šta se dešava kada prosledite objekat metodi:

korištenje sistema;

klasa Foo (

public int i; )

klasa RefTest2App (

public void ChangeValue (Foo f)

{

f.i = 42;

}

public static void Main () (

RefTest2App app = nova RefTest2App ();

Foo test = novi Foo (); test.i = 6;

Console.WriteLine ("Prije pozivanja metode"); Console.WriteLine ("test.i = (0)", test.i); Console.WriteLine ("\ n");

app.ChangeValue (test);

Console.WriteLine ("Nakon pozivanja metode"); Console.WriteLine ("test.i = (0)", test.i); Console.WriteLine ("\ n"); >)

U većini jezika osim Jave, ovaj kod će kopirati kreirani objekat testirati u lokalni skup metoda RefTest2App.ChangeValue. U ovom slučaju, objekt test, kreiran u metodi glavni, nikada neće vidjeti promjene na / objektu napravljene u metodi ChangeValue. Međutim, još jednom ponavljam da je metoda Main prosljeđuje referencu na objekt dodijeljen hrpi test. Kada je metoda Changevalue manipulira svojom lokalnom varijablom //, također direktno manipulira objektom test metoda Main.

Hajde da sumiramo

Ključ svakog programskog jezika je način na koji se izvode zadaci, matematičke, logičke i relacijske operacije — sve što je potrebno za pokretanje aplikacija u stvarnom svijetu. U kodu su ove operacije predstavljene operatorima. Faktori koji utiču na izvršavanje operatora uključuju prioritet i asocijativnost (desno i lijevo) operatora. Moćan skup predefiniranih operatora u C # može se proširiti korisnički definiranim implementacijama, o čemu ćemo govoriti u 13. poglavlju.

Top srodni članci