Kako postaviti pametne telefone i računala. Informativni portal

Matematičke operacije c#. Uvjetni izrazi

Relacijski i logički operatori

U notnom zapisu operator relacije i logički operator termin odnosa znači odnos koji može postojati između dva značenja i pojma logično- odnos između logičkih vrijednosti "true" i "false". A budući da relacijski operatori proizvode istinite ili netočne rezultate, često se koriste zajedno s logičkim operatorima. Iz tog razloga oni se razmatraju zajedno.

Sljedeći su relacijski operatori:

Booleovi operatori uključuju sljedeće:

Rezultat izvršavanja relacijskog ili logičkog operatora je Booleova vrijednost tipa bool.

Općenito, objekti se mogu usporediti na jednakost ili nejednakost pomoću relacijskih operatora == i !=. A operatori usporedbe, =, mogu se primijeniti samo na tipove podataka koji podržavaju odnos reda. Stoga se relacijski operatori mogu primijeniti na sve tipove numeričkih podataka. Ali bool vrijednosti mogu se uspoređivati ​​samo za jednakost ili nejednakost, jer istinite (istinite) i lažne (netočne) vrijednosti nisu poredane. Na primjer, usporedba true > false u C# nema smisla.

Razmotrite primjer programa koji pokazuje korištenje relacijskih i logičkih operatora:

Korištenje sustava; koristeći System.Collections.Generic; koristeći System.Linq; koristeći System.Text; namespace ConsoleApplication1 ( class Program ( static void Main(string args) ( short d = 10, f = 12; bool var1 = true, var2 = false; if (d f) Console.WriteLine("d > f"); // Uspoređivanje varijable var1 i var2 if (var1 & var2) Console.WriteLine("Ovaj tekst neće biti ispisan"); if (!(var1 & var2)) Console.WriteLine("!(var1 & var2) = true"); if (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. Unatoč tome, postoji niz operacija koje se izvode prema pravilima formalne logike. Ove logičke operacije mogu se izgraditi pomoću logičkih operatora podržanih u C#. Stoga C# nudi skup logičkih operatora koji je dovoljan za konstrukciju gotovo svake logičke operacije, uključujući implikaciju. implikacija je binarna operacija koja daje vrijednost false samo ako je njezin lijevi operand prava vrijednost, a desna je lažna. (Operacija implikacije odražava načelo da istinito ne može implicirati lažno.)

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

Booleovi operatori prečaca

C# također pruža posebne skraćeno, varijante logičkih AND i OR operatora dizajniranih za proizvodnju učinkovitijeg koda. Objasnimo ovo u sljedeći primjeri logičke operacije. Ako je prvi operand logičke operacije AND netočan, tada će njegov rezultat biti netočan, bez obzira na vrijednost drugog operanda. Ako je prvi operand logičke operacije ILI istinit, tada će njegov rezultat biti istinit bez obzira na vrijednost drugog operanda. Zbog činjenice da vrijednost drugog operanda u ovim operacijama ne treba izračunavati, štedi vrijeme i poboljšava učinkovitost koda.

Skraćena logička operacija I izvodi se pomoću operator &&, a skraćena logička operacija ILI - pomoću 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 po potrebi.

Zadnja izmjena: 19.06.2017

C# koristi većinu operacija koje se koriste u drugim programskim jezicima. Operacije predstavljaju određene akcije nad operandima - sudionicima operacije. Operand može biti varijabla ili neka vrijednost (na primjer, broj). Operacije su unarne (izvode se na jednom operandu), binarne - na dva operanda i ternarne - izvode se na tri operanda. Razmotrite sve vrste operacija.

Binarne aritmetičke operacije:

    Operacija zbrajanja dva broja:

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

    Operacija oduzimanja dva broja:

    int x = 10; int z = x - 6; // četiri

    Operacija množenja dva broja:

    int x = 10; int z = x * 5; // pedeset

    operacija dijeljenja dva broja:

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

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

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

    Iako se rezultat operacije na kraju smjesti u varijablu tipa dvostruko, što vam omogućuje spremanje frakcijskog dijela, ali sama operacija uključuje dva literala, koji se prema zadanim postavkama smatraju int objektima, odnosno cijelim brojevima, a rezultat će također biti cijeli broj.

    Da biste izašli iz ove situacije, potrebno je doslovno definirati literale ili varijable uključene u operaciju točno kao tipove double ili float:

    Dvostruki z = 10,0 / 4,0; //rezultat je 2,5

    Operacija iz koje se prima stanje cjelobrojno dijeljenje dva broja:

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

Tu su i broj unarne operacije, u kojem sudjeluje jedan operand:

    operacija povećanja

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

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

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

Operacija smanjivanja ili smanjenja vrijednosti za jedan. Postoji i prefiksni oblik dekrementa (--x) i postfiksni oblik (x--).

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

Pri izvođenju nekoliko aritmetičkih operacija odjednom treba voditi računa o redoslijedu njihova izvođenja. Prioritet operacija od najvišeg do najnižeg:

    povećanje, smanjenje

    Množenje, dijeljenje, ostatak

    Zbrajanje, 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 se radi o tri operacije: dekrement, oduzimanje i množenje. Prvo se dekrementira varijabla c, zatim množenje b*a i na kraju oduzimanje. To jest, zapravo je skup operacija izgledao ovako:

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

No sa zagradama bismo mogli 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 što će onda biti rezultat u izrazu:

int x = 10 / 5 * 2;

Trebamo li ovaj izraz tumačiti kao (10 / 5) * 2 ili kao 10 / (5 * 2)? Uostalom, ovisno o tumačenju, dobivamo različite rezultate.

Kada operacije imaju isti prioritet, redoslijed vrednovanja određen je asocijativnošću operatora. Ovisno o asocijativnosti, postoje dvije vrste operatora:

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

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

svi aritmetički operatori(osim inkrementa i dekrementa prefiksa) su lijevo-asocijativne, odnosno izvode se slijeva nadesno. Stoga se izraz 10 / 5 * 2 mora tumačiti kao (10 / 5) * 2, odnosno rezultat će biti 4.

Ne postoje implicitne booleove konverzije u C#, čak ni za cjelobrojne aritmetičke tipove. Stoga je potpuno ispravan unos u jeziku C++:

intk1 = 7;
ako (k1) Konzola. Napiši liniju(" u redu!");

ilegalno u C# programima. Doći će do pogreške tijekom koraka prevođenja jer je procijenjeni uvjet tipa int, i implicitna konverzija ovog tipa u tip bool nedostaje.

U C# se stroža pravila primjenjuju i na logičke operacije. Da, snimaj ako(k1 && (x> g)), ispraviti u C++, dovodi do greške u

C# programi jer je && operator definiran samo za operande tipa bool, a u ovom izrazu jedan od operanda ima tip int. U jeziku C#, u ovim situacijama, trebali biste koristiti notaciju:

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

Logičke operacije podijeljene su u dvije kategorije: neke se izvode na boolean vrijednostima operanda, druge izvode logičku operaciju na bitovima operanda. Iz tog razloga, postoje dvije unarne negacije u C# - logička negacija, određena operatorom "!", i negacija po bitovima, navedena operatorom "~". Prvi je definiran preko operanda tipa bool, drugi - preko operanda cjelobrojnog tipa, počevši od tipa int i viši (int, uint, dugo, ulong). Rezultat operacije u drugom slučaju je operand u kojem je svaki bit zamijenjen svojim komplementom. Evo primjera:

/// < Sažetak>
/// Booleovi izrazi
/// Sažetak>
javnostponištitilogika() {
// 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

Ovaj fragment komentirao je izjave koje dovode do pogrešaka. U prvom slučaju pokušalo se primijeniti bitnu operaciju negacije na izraz tipa bool, u drugom je logička negacija primijenjena na cjelobrojne podatke. Oba su ilegalna u C#. Obratite pažnju na različito tumačenje bitne negacije za nepredznačene i predznačene tipove cijelih brojeva. Za varijable j5 i j2 bitni niz koji daje vrijednost je isti, ali se drugačije tumači. Relevantni izlaz je:

uintj2 = 4294967288
intj5 = -8.

Binarne logičke operacije " && - uvjet I "i" || - uvjetni ILI" definiraju se samo nad tipom podataka bool. Operacije se nazivaju uvjetne ili kratke jer procjena drugog operanda ovisi o već procijenjenoj vrijednosti prvog operanda. Vrijednost uvjetnih Booleovih operacija leži u njihovoj učinkovitosti u vremenu izvršenja. Često vam omogućuju izračunavanje booleov izraz A koji ima smisla, ali gdje drugi operand nije definiran. Uzmimo kao primjer klasični problem pretraživanja po uzorku u nizu, kada se traži element sa zadanom vrijednošću (uzorkom). Takav element u nizu može, ali i ne mora postojati. Evo tipičnog rješenja ovog problema u pojednostavljenom obliku, ali prenoseći bit stvari:

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

Ako vrijednost varijable traži(uzorak) ne odgovara niti jednoj vrijednosti elementa niza ar, zatim posljednji test uvjeta petlje dokće se izvršiti s vrijednošću ja, jednak ar. Duljina. U ovom slučaju, prvi operand će dobiti vrijednost lažno, i iako drugi operand nije definiran, petlja će normalno izaći. Drugi operand nije definiran u zadnja provjera, jer je indeks elementa niza izvan raspona (u C#, indeksiranje elementa počinje od nule).

Tri binarne operacije po bitovima- «& - I» , « | - OR", "^ - XOR" koriste se na dva načina. Definirani su kao gore navedeni cjelobrojni tipovi int, i preko Booleovih tipova. U prvom slučaju se koriste kao bitne operacije, u drugom se koriste kao obične logičke operacije. Ponekad je potrebno da se oba operanda ipak procijene, tada su te operacije neizostavne. Evo primjera njihove prve upotrebe:

//Logike bitovne operacijeI, Ili, 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 sparivanja uzoraka pomoću logičkog I: ja= 0;

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

Zajamčeno je da će ovaj isječak imati uzorak traženja u nizu i isječak će se uspješno pokrenuti. U istim slučajevima kada niz ne sadrži element traži, iznimka će biti bačena. Smisao takvog postupka - pojava iznimke - može biti znak pogreške u podacima, što zahtijeva posebno postupanje u situaciji.

Ovaj odjeljak ukratko opisuje sintaksu i primjenu svih C# operacija, osim nekih od primarnih, koje su pokrivene kasnije u odgovarajućem materijalu.

inkrement i dekrement

Operatori povećanja (++) i smanjenja (--) povećavaju i smanjuju operand za jedan. Imaju dva oblika - prefiks kada je znak operacije napisan ispred operanda, i postfiks. U prefiksnom obliku prvo se mijenja operand, a zatim njegova vrijednost postaje rezultirajuća vrijednost izraza, dok je u postfiksnom obliku vrijednost izraza izvorna vrijednost operanda, nakon čega se mijenja.

Standardne operacije povećanja postoje za cijele, simboličke, stvarne i financijske vrijednosti.

Operacija nova

Operator new koristi se za stvaranje novog objekta. Format operacije:

novi tip ([ argumenti ])

Ovom operacijom možete kreirati objekte referentnog i vrijednosnog tipa, na primjer:

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

Dok radite operacije novi najprije se alocira potrebna količina memorije (za referentne tipove u hrpi, za vrijednosne tipove - na stogu), a zatim tzv. zadani konstruktor, što je metoda kojom se objekt inicijalizira. varijabla vrsta vrijednosti dodijeljen zadana vrijednost, što je nula odgovarajućeg tipa.

Operacije negacije

Aritmetička negacija(unarni minus - ) mijenja predznak operanda. Standardni operator negacije definiran je za tipove int, long, float, double i decimal. Može se primijeniti na količine drugih vrsta, ako je to za njih moguće. implicitna pretvorba ovim vrstama.

Booleov negacija(!) je definiran za tip bool. Rezultat operacije je lažan ako je operand istinit, a istinit ako je operand lažan.

Bitna negacija(~), koji se često naziva bitwise, invertira svaki bit u binarnoj reprezentaciji operanda tipa int, uint, long ili ulong.

Eksplicitna konverzija tipa

Operacija se koristi za eksplicitno pretvaranje vrijednosti iz jedne vrste u drugu. Ovo je potrebno kada ne postoji implicitna konverzija. Prilikom pretvorbe iz duljeg tipa u kraći, informacije se gube. Format operacije:

(vrsta) izraz

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

dugo b = 300; int a = (int) b; // podaci se ne gube 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 tipove int, uint, long, ulong, float, double i decimal. 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" tipu operanda, ali ne manji od int.

tablica 3.2. Simboli x i y označavaju konačne pozitivne vrijednosti, simbol z je rezultat realne operacije množenja. Ako je rezultat prevelik za predstavljanje dati tip, uzima se jednaka vrijednosti "beskonačno", ako je premala, uzima se kao 0. NaN (nije broj) znači da rezultat nije broj.

Tablica 3.2. Rezultati stvarnog 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 podjele(/ ) izračunava kvocijent dijeljenja prvog operanda s drugim. Standardni operator dijeljenja definiran je za tipove int, uint, long, ulong, float, double i decimal. Može se primijeniti na vrijednosti drugih tipova ako za njih postoji implicitna konverzija u te tipove. Vrsta rezultata određena je pravilima pretvorbe, ali ne manje od int.

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

Ako je barem jedan od operanda stvaran, razlomački dio ne odbacuje se rezultat dijeljenja, nego sve moguće vrijednosti dati su u tablici 3.3.

Tablica 3.3. Pravi rezultati podjele
/ +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 financijske vrijednosti (tip decimal), kada se dijele s 0 i prelijevaju, generiraju se odgovarajuće iznimke, kada nestanak reda rezultat je 0.

Rad ostatka dijeljenja(%) također se različito tumači za cjelobrojne, stvarne i financijske 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 operanda realan, rezultat operacije izračunava se formulom x - n * y , gdje je n najveći cijeli broj manji ili jednak rezultatu dijeljenja x s y . Sve moguće kombinacije vrijednosti operanda prikazane su u tablici 3.4.

Tablica 3.4. Rezultati stvarnog 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 financijske vrijednosti (tip decimal ) prilikom primanja ostatka dijeljenja s 0 i kod prelijevanja, generiraju se odgovarajuće iznimke, kada nestanak reda rezultat je 0. Predznak rezultata je predznak prvog operanda.

Zbrajanje i oduzimanje

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

Sve moguće vrijednosti za stvarne operande prikazane su u tablici 3.5.

Tablica 3.5. Stvarni rezultati zbrajanja
+ g +0 -0 + - NaN
x z x x + - NaN
+0 g +0 +0 + - NaN
-0 g +0 -0 + - NaN
+ + + + + NaN NaN
- - - - NaN - NaN
NaN NaN NaN NaN NaN NaN NaN

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

Ako su oba operanda cijeli ili decimalni, a rezultat operacije je prevelik da bi ga mogao predstaviti dani tip, izbacuje se System.OverflowException.

Sve moguće vrijednosti rezultata oduzimanja za stvarne operande prikazane su u tablici 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 ga mogao predstaviti dani tip, uzima se kao vrijednost "beskonačno" s istim predznakom kao x - y , ako je premalen, uzima se kao 0 s istim predznakom kao x - y .

Tablica 3.6. Stvarni rezultati oduzimanja
- g +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

Smjenske operacije

Smjenske operacije (<< и >> ) primjenjuju se na cijele operande. Oni pomiču binarnu reprezentaciju prvog operanda lijevo ili desno za broj bitova navedenih u drugom operandu.

Na pomak ulijevo (<< ) освободившиеся разряды обнуляются. При desni pomak(>> ) oslobođeni bitovi se popunjavaju nulama ako je prvi operand nepredznačenog tipa, a predznačenim bitom inače. Standardne operacije pomaka definirane su za tipove int, uint, long i ulong.

Relacijske operacije i testovi jednakosti

relacijske operacije (< , <= , >, >= , == , != ) usporedite prvi operand s drugim. Operandi moraju biti aritmetički tip. Rezultat operacije - Booleov tip, je istina ili laž. Pravila za izračunavanje rezultata data su u > y

istinito ako je x veće od y, lažno u suprotnom x<= y istinito ako je x manje ili jednako y, lažno u suprotnom x >= y istinito ako je x veće ili jednako y, lažno u suprotnom

Bitovne logičke operacije

Bitovne logičke operacije(& , | , ^ ) primjenjuju se i djeluju na cijelim operandima binarne reprezentacije. Prilikom izvođenja operacija, operandi se usklađuju bit po bit (prvi bit prvog operanda s prvim bitom drugog, drugi bit prvog operanda s drugim bitom drugog i tako dalje). Standardne operacije definirane su za tipove int, uint, long i ulong.

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

Na bitwise disjunkcija(| ), bit rezultata je 1 ako je odgovarajući bit barem jednog od operanda 1.

S bitovnim XOR-om() bit rezultata je 1 samo kada je odgovarajući bit samo jednog od operanda 1.

Uvjetne logičke operacije

Uvjetne logičke operacije Oba (&& ) i OR (|| ) najčešće se koriste s Booleovim tipom operanda. Rezultat logičke operacije je true true , tada će rezultat uvjetne operacije biti vrijednost drugog operanda, inače vrijednost trećeg operanda. Uvijek se vrednuje drugi ili treći operand. Njihov tip može varirati.

Vrsta rezultata operacije ovisi o vrsti drugog i trećeg operanda. Ako su operandi istog tipa, on postaje tip rezultata operacije.

Operacije dodjele

Operacije dodjele(= , += , -= , *= itd.) definirajte novu vrijednost za varijablu. Ove se operacije mogu koristiti u programu kao potpune izjave.

Format operacije jednostavan zadatak (= ):

varijabla = izraz

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

Osim pridjeljivanja, prvi operand se dodaje drugom operandu, a rezultat se upisuje u prvi operand, odnosno izraz a += b je kompaktniji zapis izraza a = a + b .

Rezultat složene operacije dodjele je vrijednost zapisana lijevom operandu.

Operacije dodjele desno-asocijativni, 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 samostalan rad studenata

  1. Gdje se mogu deklarirati varijable? Što je uključeno u deklaraciju varijable?
  2. Što se događa kada se operandi koriste u izrazu različite vrste? Navedite primjere.
  3. Navedite operacije jezika C# grupirajući ih prema prioritetu.
  4. Što je NaN? U kojim operacijama je rezultat NaN?
  5. Na koje su vrste operanda primjenjive operacije pomaka?
  6. Što su iznimke?
  7. Opišite principe rukovanja iznimkama.

POGLAVLJE 10 Izrazi i operatori

U ovom poglavlju ćemo pogledati temelj bilo kojeg programskog jezika - njegovu sposobnost da izvršava dodjele i usporedbe koristeći operatore. Vidjet ćemo što su operatori u C# i koji je njihov prioritet, a zatim ćemo se pozabaviti pojedinačnim kategorijama izraza za izvođenje aritmetičkih operacija, dodjeljivanje vrijednosti i usporedbu operanda.

Operatori

Operator je simbol koji označava operaciju koju treba izvršiti na jednom ili više argumenata. Kada se naredba izvrši, dobiva se rezultat. Sintaksa za primjenu operatora donekle se razlikuje od pozivanja metoda, a trebali biste poznavati format izraza koji sadrže operatore u C# kao svoj džep. Kao iu većini drugih jezika, semantika operatora u C# slijedi pravila i oznake koje su nam poznate iz škole. Osnovni operatori u C# uključuju množenje (*), dijeljenje (/), zbrajanje i unarni plus (+), oduzimanje i unarni minus (-), modul (%) i dodjeljivanje (=).

Operatori se koriste za dobivanje nove vrijednosti od vrijednosti na kojima se operacija izvodi. Ove početne vrijednosti nazivaju se operandi. Rezultat operacije mora biti pohranjen u memoriji. Ponekad je pohranjen u varijabli koja sadrži jedan od originalnih operanda. C# prevodilac generira poruku o pogrešci ako nova vrijednost nije definirana ili pohranjena kada se koristi operator. Kod u nastavku ne mijenja vrijednosti. Prevodilac će izdati poruku o pogrešci, budući da se aritmetički izraz koji ne mijenja barem jednu vrijednost obično smatra pogreškom.

klasa NoResultApp

{

javni statički void Main()

{

int i; int j;

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

Većina operatora radi samo na numeričkim tipovima podataka, kao što je Byte, Short, Long, Integer, Single, Double i Decimal. Izuzetak su operatori usporedbe (== i !=). Također, u C#, možete koristiti + i - operatore na klasi Niz pa čak i koristiti operatore povećanja (++) i (-) za takve neobične jezične konstrukcije kao što su delegati. O potonjem ću raspravljati u 14. poglavlju.

Prioritet operatora

Kada postoji više naredbi u istom izrazu, prevodilac mora odrediti redoslijed kojim se one izvršavaju. Pritom se prevoditelj vodi pravilima tzv prednost operatora. Razumijevanje prednosti operatora bitno je za ispravan pravopis izrazi - ponekad rezultat možda neće biti očekivani.

Razmotrite izraz 42 + 6 * 10. Ako zbrojite 42 i 6, a zatim zbroj pomnožite s 10, dobit ćete 480. Ako pomnožite 6 s 10 i rezultatu dodate 42, dobit ćete 102. Prilikom kompajliranja koda, posebna komponenta prevoditelja je leksički analizator - je odgovoran za redoslijed čitanja ovog koda. Leksički analizator je taj koji određuje relativno prvenstvo heterogenih operatora u jednom izrazu. Da bi to učinio, koristi neku vrijednost - prioritet - svakog podržanog operatora. Prvi se rješavaju operatori višeg prvenstva. U našem primjeru, operator * ima prednost nad operatorom + jer * upija(Sada ću objasniti ovaj pojam) njegove operande prije + radi to. Objašnjenje leži u općim aritmetičkim pravilima: množenje i dijeljenje stalno imaju veći prioritet od zbrajanja i oduzimanja. Vratimo se primjeru: kažu da je broj 6 apsorbiran operator * i u 42 + 6 * 10 i u 42 * 6 + 10, tako da su ovi izrazi ekvivalentni 42 + (6 * 10) i (42 * 6) + 10.

Kako se određuje prvenstvo u C#

Sada da vidimo kako je prednost operatora definirana u C#. Operatori su dolje navedeni prema silaznom redoslijedu prioriteta (tablica 10-1). Zatim ću detaljnije govoriti o različitim kategorijama operatora podržanih u C#.

tab. 10-1. Prednost operatora u C#.

Kategorija operatera Operatori
Jednostavan (x), x.y, f(x), a[x], x++, x -, novo, tipof, veličinaof, označeno, neoznačeno
unarni + , -, !, ++x, - x, (T)x
Multiplikativ *,/, %
aditiv +, -
Shift «, »
Stav <, >, <=, >=, je
Jednakost ==
Logičko I (I) &
Logički isključivi ILI (XOR) ^
Logički ILI (ILI) 1
Uvjetno I (I) &&
Uvjetno IL I (ILI) II
Stanje 9-
Zadatak = *= /= % = , + = , -= « = , » = , &=, ^ = , =

Lijeva i desna asocijativnost

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

Operator - je lijevo asocijativan, tj. prvo se izračunava 42-15, a zatim se od rezultata oduzima 6. Da ima desnu asocijativnost, prva bi se procijenila desna strana izraza (15-6), a zatim rezultat bi se oduzeo od 42.

Svi binarni operatori (operatori s dva operanda), osim operatora dodjele, - lijevo-asocijativno, tj. 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 dodaje zbroju S. operatori dodjele i uvjetne izjave - desno-asocijativno, odnosno obrađuju izraze s desna na lijevo. Drugim riječima, a=b=c ekvivalentno a = (b= S). Mnogi ljudi naiđu na ovo kada žele staviti više naredbi dodjele u isti redak, pa pogledajmo ovaj kod:

korištenje sustava;

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:

a=1 b=2 c=3

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

Vrednovanje izraza s desna na lijevo može isprva biti zbunjujuće, ali pristupimo tome na ovaj način: ako bi operator dodjele bio lijevo asocijativan, kompilator bi prvo morao procijeniti a = b, zatim a bilo bi 2 i tada b= sa i kao rezultat b bio bi 3. Krajnji rezultat bio bi a=2 b=3 c=3. Očito, to nije ono što očekujemo kada pišemo a= b= S, i zato su operatori dodjele i uvjetni operatori desnoasocijativni.

Praktična upotreba

Ništa nije tako problematično kao pronaći grešku koja je napravljena samo zato što programer nije znao pravila prvenstva i asocijativnosti. Naišao sam na objave na e-mail konferencijama gdje su naizgled razumni ljudi predložili neku vrstu mehanizma samodokumentiranja - korištenje razmaka za navođenje operatora koji, po njihovom mišljenju, imati staž. Na primjer, budući da znamo da operator množenja ima prednost nad operatorom zbrajanja, trebali bismo napisati nešto poput ovoga gdje razmaci označavaju podrazumijeva se radni staž:

a \u003d b * c + d;

Ovaj je pristup u osnovi pogrešan: prevodilac ne može ispravno analizirati kod ako određena sintaksa nije definirana. Prevodilac analizira kod prema pravilima definiranim od strane programera prevoditelja. S druge strane, postoje okrugle zagrade, koristi se za eksplicitno označavanje prvenstva i asocijativnosti. Na primjer, izraz a= b * c + d može se prepisati kao a \u003d (b * c) + d ili kako a= b * (c + d) a prevodilac će prvo evaluirati izraz u zagradama. Ako postoji više parova zagrada, prevodilac će prvo procijeniti izraze u zagradama, a zatim cijeli izraz, na temelju opisanih pravila prvenstva i asocijativnosti.

Čvrsto vjerujem da se zagrade uvijek trebaju koristiti kada postoji više operatora u izrazu. Preporučujem da to učinite čak i ako razumijete redoslijed izračuna, jer ljudi koji će održavati vaš kod možda nisu toliko pismeni.

C# operatori

Najbolje je uzeti u obzir operatore prema prioritetu. U nastavku ću opisati najčešće operatore.

Jednostavni operatori

  • (x) Ovo je vrsta operatora zagrada za kontrolu redoslijeda vrednovanja u matematičkim operacijama iu pozivima metoda.
  • x.y Operator točka koristi se za određivanje člana klase ili strukture. Ovdje x predstavlja entitet koji sadrži člana g.
  • f(x) Ova vrsta operatora zagrade koristi se za nabrajanje argumenata metode.
  • Oh] Uglate zagrade koriste se za indeksiranje niza. Ove se zagrade također koriste u kombinaciji s indeksatorima kada se objekti mogu tretirati kao niz. Pogledajte Poglavlje 7 za indeksatore.
  • x++ O operatoru inkrementa ćemo posebno govoriti u odjeljku “Operatori inkrementa i dekrementa”.
  • x- O operatoru dekrementa također ćemo raspravljati kasnije.
  • new Ovaj se operator koristi za stvaranje instanci objekta na temelju definicije klase.

vrsta

Odraz(odraz) je mogućnost dobivanja informacija o vrsti tijekom izvođenja. Ove informacije uključuju nazive tipova, klasa i članova strukture. NA. NET Framework ova je funkcionalnost povezana s klasom Sustav. tip. Ova klasa je korijen svih operatora refleksije i može se dobiti pomoću operatora vrsta. Nećemo sada ulaziti u pojedinosti razmišljanja (do toga ćemo doći u 16. poglavlju), ali evo jednostavnog primjera koji ilustrira koliko je lako koristiti vrsta"za dobivanje gotovo svih informacija o tipu ili objektu tijekom izvođenja:

korištenje sustava;

koristeći System.Reflection;

javna klasa Apple(

public int nSeeds;

javna praznina Ripen()

{

> >

javna klasa TypeOfApp(

public static void Main() (

Typet = typeof(Apple);

naziv klase niza = t.ToStringO;

Console.IgShipe("\nInformacije klase 0 (O)", className);

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

foreach (metoda MethodInfo u metodama)

Console.WriteLine(method.ToString());

}

Console.WriteLine("\nBce članovi (O)", className); konzola. Writel_ine("--------"); MemberInfo allMembers = t.GetMembersO; foreach (MemberInfo član u allMembers)

{

konzola. WriteLine(member.ToStringO);

} > }

Ovaj program sadrži klasu Jabuka, koji ima samo dva člana: polje nSjemenke i metoda Sazrijevati. Prvo, korištenje operatora vrsta i naziv klase, dobivam objekt Sustav. tip, koji se zatim sprema u varijablu t. IZ sada mogu koristiti predmet Sustav. tip da biste dobili sve metode i članove klase Jabuka. To se radi pomoću metoda GetMethods i GetMembers odnosno. Rezultati ovih metoda prikazani su na standardni uređaj izlaz ovako:

Informacije o klasa jabuka Appleove metode

Int32 GetHashCodeQ

System.String ToStringQ

Void RipenO

System.Type GetTypeO

Svi članovi Applea

Int32 nSeeds

Int32 GetHashCodeO

Booleovo jednako(Sustav.objekt)

System.StringToStringO

Void RipenO

System.Type GetTypeO

Prije nego što nastavim, želim dati dvije napomene. Prvo, primijetite da se naslijeđeni članovi klase također izvode. Budući da klasa nije eksplicitno izvedena iz druge klase, znamo da svi članovi koji nisu definirani u klasi Jabuka naslijeđen od implicitne osnovne klase System.Object. Drugo, objekt Sustav.Vrsta može se dobiti metodom Gettype. Ovo naslijeđeno od System.Object metoda vam omogućuje rad s objektima, a ne klasama. Bilo koji od sljedeća dva isječka može se koristiti za dobivanje objekta Sustav. tip.

II Nabavite System.Type objekt na temelju definicije klase. Typet1 = typeof(Apple);

// Dobivanje objekta System.Type iz objekta. jabuka jabuka= novi AppleQ; Tip t2 = jabuka.GetTypeO;

veličina

Operater veličina koristi se za dobivanje veličine navedenog tipa u bajtovima. Pritom zapamtite isključivo dva važni faktori. Prvo, veličina može se primijeniti samo na vrste dimenzija. Stoga, iako se može koristiti za članove klase, ne može se koristiti za klase kao takve. Drugo, veličina može se koristiti samo u metodama ili blokovima koda označenim kao nesiguran. IZ vidjet ćemo ovu vrstu koda u 17. poglavlju. Evo primjera korištenja operatora veličina u metodi klase označenoj kao nesigurno:

korištenje sustava;

klasa BasicTypes(

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

Console.WriteLine("\nPa3Mephi osnovni 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)); ) )

klasa UnsafeUpp

{

nesiguran javni statički void Main()

{

BasicTypes.ShowSizes();

} }

Ovo je rezultat pokretanja ove aplikacije:

Veličine osnovnih tipova Veličina short = 2 Veličina int = 4 Veličina long = 8 Veličina bool = 1

Operater veličina 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, dok su rezultati veličina možda nije očito:

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

struct StructWithNoMembers

struct StructWithMembers

{

kratke hlače;

int i;

dugo1;

bool b; )

struct CompositeStruct

{

StructWithNoMembers a; StructWithMembersb;

StructWithNoMembers c; )

classUnSafe2App(

nesigurni javni statički void Main() ( Console.WriteLine("\nPa3Mep StructWithNoMembers struktura = (0)",

sizeof(Struktura bez članova)); Console.WriteLine("\nPa3Mep StructWithMembers struktura = (0)",

sizeof(Struktura sa članovima)); Console.WriteLine("\nPa3Mep CompositeStruct struktura = (0)",

sizeof(CompositeStruct)); ) )

Iako se može pretpostaviti da će ova aplikacija ispisati 0 za strukturu bez članova (Struktura bez članova), 15 za strukturu s četiri člana osnovnog tipa (Struktura s članovima) i 15 za strukturu koja spaja dva prethodna (kompozitna struktura), u stvarnosti će rezultat biti ovakav:

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

Veličina strukture CompositeStruct = 24

Objašnjenje za to je način na koji se konstrukt sprema strukturirati prevodilac u izlaznoj datoteci, u kojoj prevodilac primjenjuje poravnanje i ispunu. Na primjer, ako je struktura veličine 3 bajta i postavljeno je poravnanje od 4 bajta, prevodilac će automatski dodati 1 bajt strukturi i izraz veličina označava da je veličina strukture 4 bajta. Imajte ovo na umu kada dimenzionirate strukture u C#.

provjereno i neprovjereno

Ova dva operatora kontroliraju provjeru preljeva 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 (/), zbrajanje (+), oduzimanje (-) i modul (%). Svrha prva četiri operatera jasna je iz njihovih naziva; modulo operator tvori ostatak cjelobrojnog dijeljenja. Evo koda koji ilustrira upotrebu matematičkih operatora:

korištenje sustava;

klasa MathOpsApp

{

javni statički void Main()

{

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

a = rand.Dalje() % 100; // Granična vrijednost 99. b = rand.NextO % 100; // 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. // Prema tome, ako je a manje od b, rezultat // uvijek će biti 0. Da biste dobili točniji rezultat, // morate koristiti varijable tipa dvostruko ili plutajuće, 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); > >

Unarni operatori

Postoje dva unarna operatora: plus i minus. Operator unarni minus govori prevoditelju da je broj negativan. Dakle u sljedećem kodu a bit će jednako -42:

korištenje sustava; korištenje sustava;

klasa UnarylApp(

javni statički void Main()

{

int a = 0;

a = -42;

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

Međutim, ovaj kod uvodi dvosmislenost: using System;

klasa Unary2App<

public static void Main() (

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

a = b * -c;

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

Izraz a= prije Krista nije sasvim jasno. Opet, upotreba zagrada će pojasniti ovaj izraz:

// Kada koristimo zagrade, očito je da // množimo b s negativnim brojem c. a \u003d b * (-c);

Ako se unarni minus vrati negativno značenje operand, mogli biste pomisliti da bi unarni plus vratio pozitivno. Međutim, unarni plus samo vraća operand u njegovom izvornom obliku i ne radi ništa drugo, tj. ne utječe na operand. Na primjer, izvršavanje ovog koda će ispisati vrijednost -84:

korištenje sustava;

classUnarySApp(

javni statički void MainQ(

a = b * (+c);

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

Da biste dobili pozitivnu vrijednost, koristite funkciju Matematika, trbušnjaci Ovaj kod će ispisati vrijednost 84:

korištenje sustava;

klasa Unary4App

{

javni statički void Main()

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

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

Zadnji unarni operator koji sam spomenuo je T(x). Ovo je vrsta operatora zagrada koji vam omogućuje pretvaranje jednog tipa u drugi. Budući da se može preopteretiti stvaranjem prilagođene transformacije, o tome ćemo govoriti u 13. poglavlju.

Složene izjave o dodjeli

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

refren = y

gdje op - to je operater. Imajte na umu da je u ovom slučaju lijeva vrijednost (lvrijednost) nije zamijenjeno pravom (rvrijednost), složeni operator ima isti učinak kao:

x = x op na

i lvrijednost koristi se kao osnova za rezultat operacije.

Primijetite da sam rekao "ima isti učinak." Prevodilac ne prevodi izraz kao x += 5 in x= x + 5, on postupa logično. IZ posebna pažnja treba uzeti u obzir kada lvrijednost je metoda. Razmotrite kôd:

korištenje sustava;

klasa CompoundAssignmentlApp(

zaštićeni lnt elementi;

public int GetArrayElementO

{

povratni elementi;

}

CompoundAssignment1App() (

elementi = novi int;

elementi = 42;

}

public static void Main() (

Aplikacija CompoundAssignmentlApp = new CompoundAsslgnment1App();

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

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

Obratite pažnju na istaknutu liniju - poziv metode Compound-AssignmentlApp.GetArrayElement a zatim mijenjanje prvog elementa - ovdje sam upotrijebio sintaksu:

x = x op na

Ovo je MSIL kod koji će se generirati: // Neučinkovita tehnika: x = x o y.

Metoda public hldebyslg static void Main() 11 upravljana

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: poziv void ["mscorlib 1 ]System.Console::WriteLine (klasa System.String, klasa System.Object)

IL_0021: ldloc.0

IL_0022: instanca poziva int32 CompoundAssignmentlApp::GetArrayElementO

IL_0027: Idc.i4.0

IL_0028: ldloc.0

IL_0029: instanca poziva int32 CompoundAssignmentlApp: :GetArrayElementO

IL_002e: Idc.i4.0

IL_002f: ldelem.14

IL_0030: ldc.14.5

IL_0031: dodati

IL_0032: čelik.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: okvir ["msoorlib"]System.Int32 IL_0049: poziv void ["mscorlib"]System.Console::WriteLine

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

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

Pogledajte istaknute retke: metoda CompoundAssignmentlApp.Get-ArrayElement zapravo se zove dva puta! To je barem neučinkovito, a moguće i štetno, ovisno o tome što još metoda radi.

Sada pogledajmo drugi kod koji koristi složenu sintaksu dodjele:

korištenje sustava;

klasa CompoundAssignment2App(

zaštićeni int elementi;

public int GetArrayElementO

povratni elementi;

}

CompoundAssignment2App() (

elementi = novi int;

elementi = 42;

}

public static void Main() (

Aplikacija CompoundAssignment2App = new CompoundAssignment2App();

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

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

Korištenje složenog operatora dodjele rezultirat će mnogo učinkovitijim MSIL kodom:

// Učinkovitija tehnika: xop = y.

Metoda public hidebysig static void Main() ili managed

\ {

\.ulazna točka

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

Lokalno (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: instanca poziva int32

CompoundAssignmentlApp::GetArrayElementO IL_0011: Idc.i4.0

IL_0012: Idelema [ mscorlib"]System.Int32 IL_0017: okvir [> mscorlib - ]System.Int32 lL_001c: poziv 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: dodati

IL_002f: čelik.14

IL_0030: Idstr "(0)"

IL_0035: ldloc.0

IL_0036: instanca poziva int32

CompoundAssignmentlApp::GetArrayElementO IL_003b:Idc.i4.0

IL_003c: Idelema["mscorlib"]System.Int32

IL_0041: kutija[mscorlib"]Sustav.Int32

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

(klasa System.String, klasa System.Object)

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

Možete vidjeti da je korištena naredba MSIL. dup. Duplicira gornji element na stogu, stvarajući tako kopiju vrijednosti koju vraća metoda CompoundAssignmentlApp.Get Array Element. ja

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

Operatori inkrementiranja i dekrementiranja

Uveden u C i prenesen na C++ i Java operateri inc 1 -rement i decrement omogućuju vam da sažeto izrazite da želite povećati ili smanjiti brojčanu vrijednost za 1. To jest, /++ je ekvivalentno dodavanju 1 trenutnoj vrijednosti /".

Postojanje dvaju oblika operatora inkrementiranja i dekrementiranja ponekad je zbunjujuće. prefiks i postfiks vrste ovih operatora razlikuju se u trenutku u kojem se vrijednost mijenja. U prefiksnoj verziji operatora inkrementiranja i dekrementiranja (++ ai - a odnosno) prvo se izvodi operacija, a zatim se stvara vrijednost. U postfix verziji (a++ i a-) prvo se stvara vrijednost, a zatim se izvodi operacija. Razmotrite primjer:

korištenje sustava;

klasa IncDecApp(

javni statički void Foo(int j)

{

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

>

public static void Main() (

int i = 1;

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

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

Console.WriteLine("\n");

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

\foo(++l);

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

l Rezultat izvršenja će biti sljedeći:

Prije pozivanja Foo(i++) = 1

IncDecApp.Foo j = 1

Nakon pozivanja Foo(i++) = 2

Prije pozivanja Foo(-n-i) = 2

IncDecApp.Foo j = 3

Nakon pozivanja Foo(++i) = 3

Razlika je kada vrijednost se stvara i operand se modificira. Na poziv foo(i++) vrijednost /" se prosljeđuje (nepromijenjena) metodi fuj i nakon return iz metode / povećava se za 1. Pogledajte sljedeći MSIL kod: naredba dodati izvršava se nakon guranja vrijednosti na stog.

IL.0013: ldloc.0

IL.0014: dup

IL_0015: Idc.i4.1

IL_0016: dodati

IL_0017: stloc.O

IL_0018: poziv nevažeći IncDecApp::Foo(int32)

Sada pogledajte oblik prefiksa operatora korištenog u pozivu foo(++a). U tom će slučaju MSIL kod biti drugačiji. Istovremeno, tim dodati izvedena prije kako se vrijednost gura na stog za kasnije pozivanje metode fuj.

IL.0049: ldloc.0

IL_004a: Idc.i4.1

IL_004b: dodati

IL_004c: dup

IL_004d: stloc.O

IL_004e: poziv void IncDecApp::Foo(int32)

Relacijski operatori

Većina operatora vraća numeričke vrijednosti. Što se tiče relacijskih operatora, oni generiraju booleov rezultat. Umjesto / izvođenja matematičkih operacija na skupu operanda, / relacijski operatori analiziraju odnos između operanda i povratak značenje pravi, ako je omjer istinit, lažno- ako/ je lažno.

Operatori usporedbe

Na relacijske operatore tzv operatori usporedbe, refer)-sya "manje" (<), «меньше или равно» (<=), «больше» (>), veće ili jednako (>=), jednako (==) i nije jednako (!=). Primjena ovih operatora na brojeve je jasna, ali kada se koriste s objektima, njihova implementacija nije tako očita. Evo primjera:

korištenje sustava;

klasa NumericTest(

javni NumericTest(int 1)

{

this.i = i;

>

zaštićeno int 1; )

klasa RelationalOpslApp(

public static void Main() (

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

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

Ako ste Java programer, znate što će se ovdje dogoditi. Međutim, C++ programeri vjerojatno će biti iznenađeni kad vide rezultat lažno. Da vas podsjetim: stvaranjem instance objekta dobivate referencu na njega. To znači da kada naiđe na relacijski operator koji uspoređuje dva objekta, C# prevodilac ne uspoređuje sadržaje objekata, već njihove adrese. Da biste ovo bolje razumjeli, razmotrite MSIL kod:

\ .metoda javna hldebysig static void MainQ ili upravljana

\ .ulazna točka

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

1.maxstack 3

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

ILJJOOO: 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: eq

IL_0019: stloc.2

IL_001a: Idloca.s V_2

IL_001c: box["mscorlib"]Sustav.Booleov

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

(klasa System.String, klasa System.Object)

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

Pogledajte liniju .mještani. Prevodilac specificira da metoda Glavni tri lokalne varijable. Prva dva su objekti numerički test, trećina je boolean varijabla. Sada prijeđimo na linije IL_0002 i IL_0007. Ovdje se instancira objekt testl, i povežite ga s stloc pohranjen u prvoj lokalnoj varijabli. Važno je da MSIL spremi adresu novostvorenog objekta. Zatim u redovima IL_OOOa i IL_OOOf vidite MSIL kodove za stvaranje objekta test2 i pohranjivanje vraćene reference u drugu lokalnu varijablu. Konačno, u redovima 1LJ-015 i IL_0016 lokalne varijable se naredbom guraju na stog IDloc, i u redu IL_0017 tim siva uspoređuje dvije vrijednosti na vrhu stoga (tj. reference objekta testl i testl). Povratna vrijednost pohranjuje se u treću lokalnu varijablu, a zatim je ispisuje metoda Sustav.Konzola. redak pisanja.

Ali kako usporediti članove dvaju objekata? Odgovor je korištenje implicitne osnovne klase svih objekata .NET Frameworka. Naime, za te namjene klas System.Object postoji metoda Jednako. Na primjer, / sljedeći kod uspoređuje sadržaje objekata i izlaza, kao / što biste očekivali, istina: ja

korištenje sustava; /

klasa RelationalOps2App

( / public static void Main() (

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

Primjer RelationalOpslApp koristi "samostalno napravljenu" klasu (Nu-mericTest) i u drugom primjeru - .NET klasa (Decimal). Stvar je u tome da metoda System.Object.Equals mora se nadjačati da bi se izvršila stvarna usporedba članova. Prema tome, metoda Jednako s razredom NumericTest neće raditi jer nismo poništili metodu. I ovdje je klasa Decimal nadjačava metodu koju nasljeđuje jednako, i u ovom slučaju sve će raditi.

Drugi način usporedbe objekata je korištenje preopterećenja operatera(preopterećenje operatera). Preopterećenje operatora definira operacije koje treba izvesti na objektima određenog tipa. Na primjer, za objekte niz Operator + ne izvodi zbrajanje, već spaja nizove. Obradit ćemo preopterećenje operatora u 13. poglavlju.

Jednostavni operatori dodjele

Poziva se vrijednost na lijevoj strani izjave o dodjeli lvrijednost, a s desne strane - rvrijednost. Kao rvrijednost može biti bilo koja konstanta, varijabla, broj ili izraz čiji je rezultat kompatibilan s lvrijednost. U međuvremenu lvrijednost mora biti varijabla određenog tipa. Poanta je da se vrijednost kopira s desne strane na lijevu stranu. Dakle, fizički adresni prostor mora biti dodijeljen za novu vrijednost. Na primjer, možete napisati /" = 4, jer / ima mjesto u memoriji - na hrpi ili na hrpi - ovisno o vrsti /. I ovdje je operater 4 = 1 ne može se izvršiti jer je 4 vrijednost, a ne varijabla čiji se sadržaj u memoriji može mijenjati. Usput, napominjem da u C # as lvrijednost može biti varijabla, svojstvo ili indeksator. Više o svojstvima i indeksatorima potražite u poglavlju 7. U ovom poglavlju koristim varijable radi jednostavnosti. Ako s dodjelom brojčane vrijednosti sve je dovoljno jasno, s predmetima je stvar kompliciranija. Kao podsjetnik, kada radite s objektima, ne manipulirate stavkama na hrpi, koje je lako kopirati i premještati. U slučaju objekata, zapravo imate samo reference na neke entitete kojima je dinamički dodijeljena memorija. Stoga, kada pokušate dodijeliti objekt (ili bilo koji tip reference) varijabli, ne kopiraju se podaci, kao što je slučaj s tipovima vrijednosti, već reference.

Recimo da imate dva objekta: testl i test2. Ako navedete testl= test2, testl neće biti kopija test2. Poklopit će se! Objekt testl ukazuje na isto sjećanje kao test2, i sve promjene na objektu testl dovesti do promjene test2. Evo programa koji to ilustrira:

korištenje sustava;

klasa Foo(

javni int i; )

klasa RefTestlApp(

public static void MainO(

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

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

Console.WriteLine("Prije nego što se objekti dodijele"); Console.WriteLine("test1.i=(0>", testl.i); Console.WriteLine("test2.i=(0)", test2.i); Console.WriteLine("\n");

testl = test2;

Console.Writel_ine("Nakon dodjele 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"); ) )

Kada pokrenete ovaj kod, 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

Pogledajmo što se događa u svakoj fazi izvođenja ovog primjera. fu- to je jednostavna k klasa s jednim članom, /. Dvije instance ove klase kreirane su u glavnoj metodi: testl i test2- i njihovi članovi ja postavljeni su na 1 odnosno 2. Ove vrijednosti se zatim izlaze, i kao što se očekuje, testl.i jednako 1, a test2.i- 2. I tu počinje zabava! NA sljedeći redak objekt testl dodijeljen test2.Čitatelji Java programiranja znaju što je sljedeće. Međutim, većina C++ programera će očekivati ​​član/objekt testl sada je jednak članu objekta test2(pod pretpostavkom da će se prilikom kompajliranja takve aplikacije izvršiti neka vrsta operatora kopiranja člana objekta). Čini se da izlazni rezultat to potvrđuje. Međutim, zapravo je veza između objekata sada puno dublja. Dodijelimo vrijednost 42 izrazu testl.i i ponovno ispišite rezultat. I?! Prilikom promjene objekta testl promijenio i testZ To se dogodilo jer objekt testl ne više. Nakon što mu je dodijelio test2 objekt testl se gubi jer se aplikacija više ne poziva na njega i kao rezultat toga ga "čisti" skupljač smeća (garbage collector, GC). Sada testl i test2 pokazuju na istu memoriju na hrpi. Stoga, kada se jedna varijabla promijeni, korisnik će vidjeti promjenu u drugoj.

Obratite pažnju na posljednja dva izlazna retka: iako je u kodu promijenjena samo vrijednost testl.i, značenje test2.i također promijenio. Još jednom, obje varijable sada pokazuju na istu memorijsku lokaciju, što je ponašanje koje bi Java programeri očekivali. Međutim, to uopće ne ispunjava očekivanja C++ programera, jer se u ovom jeziku vrši kopiranje objekata: svaka varijabla ima svoju jedinstvenu kopiju članova i promjene jednog objekta ne utječu na drugi. Budući da je ovo ključ za razumijevanje kako objekti rade u C#, vratimo se korak unatrag i vidimo što se događa kada objekt predamo metodi:

korištenje sustava;

klasa Foo(

javni int i; )

klasa RefTest2App(

public void ChangeValue(Foo f)

{

f.i = 42;

}

public static void Main() (

RefTest2App app = new RefTest2App();

Foo test = new 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 poziva metode"); Console.WriteLine("test.i=(0)", test.i); Console.WriteLine("\n"); > )

U većini jezika osim Jave, ovaj kod će kopirati stvoreni objekt testirati u metoda lokalni stog RefTest2App.ChangeValue. U ovom slučaju objekt test, stvoren u metodi glavni, nikada neće vidjeti promjene na objektu/učinjene u metodi PromijeniVrijednost. Međutim, ponavljam da metoda Glavni prosljeđuje referencu na objekt dodijeljen hrpi test. Kada se metoda PromijeniVrijednost manipulira svojom lokalnom varijablom // također izravno manipulira objektom test metoda glavni.

Sumirati

Glavna stvar u svakom programskom jeziku je način na koji se izvode dodjele, matematičke, logičke i relacijske operacije - sve što je potrebno za rad. stvarne primjene. U kodu su ove operacije predstavljene operatorima. Čimbenici koji utječu na izvođenje naredbi uključuju prvenstvo i asocijativnost (lijevo i desno) naredbi. Snažan skup unaprijed definiranih operatora u jeziku C# može se proširiti implementacijama definiranim od strane korisnika, što ćemo pokriti u 13. poglavlju.

Najpopularniji povezani članci