Citat:
_v!rus_:
...e time sam mislio da se ruši "generičnost" jezika i frameworka čim hardkoduju kompajliranje za pojedine klase, mada mi ruku na srce zaista ne pada na pamet konkretan slučaj primene i kada bi bilo moguće napraviti svoj value tip. A i ta moja ideja je krenula od toga da su value types u stvari objekti, što se izgleda ispostavilo da je pogrešno. Na kraju krajeva, ništa bitno, samo 'nako, mislio sam da je neko najzad napravio generičan OO jezik, kada sam (pogrešno) pročitao da su simple types u stvari objekti.
@mmix
To da su svi simple(value) types u stvari objekti našao sam u Prof. C#, second edition (Wrox), pa sam shvatio zdravo za gotovo, tamo se ne pominju varijante sa "hard-coding"
Pazi komapjler to radi zato sto JIT/runtime to tako rade, to je do samog CLS-a. A sto se tice svog value type-a, veca primena bi bila u nasledjivanju postojeceg value tipa; npr hocu 32 bitni broj koji ima samo parne brojeve
. U principu nevazno, neko sigurno ima i neki pametniji primer, ali svejedno ne moze jer ne postoji nacin da se to objasni CLS-u (sem custom wrapper klase, ali to nije isto (vidi dole)).
Ono sto ce tek da te mozda zbuni je cinjenica da CLS dozvoljava da se value type upakuje (boxing) u "svoj" genericki objekat. Taj objekat onda ide na HEAP i ima svoju kopiju vrednosti (ne predstavlja "pointer" na varijablu od koje je nastao:
Code:
int x = 3;
object xo = (object)x;
x = 5;
xo = 6;
x.ToString();
na kraju ovog koda x je lokalna varijabla na steku ciji je
value-type Int32 i sa vrednoscu 5, dok ce xo biti referenca na objekat na HEAP-u ciji je
type Int32 i sa internom vrednoscu 6.
Ono sto takodje moze da promakne ovde je da pomislis da xo i posle x0 = 6 drzi referencu na isti objekat koji je drzao vrednost 3 i da je samo promenio internu vrednost na 6, to medjutim nije tacno, CLS je zapravo kroz boxing kreirao novi objekat sa internom vrednoscu 6 i njegovu referencu ubacio u xo, dok je stara referenca otisla u zaborav i ceka da je GC zdrobi.
Dakle, value-type mogu biti objekti, ali ne po defaultu, moras da se "pomucis" za to
Takodje imaj u vidu da ovde govorimo o implementaciji na PC-u na Windows-u. Niko tebe ne sprecava da napravis implementaciju CLSa koja uvek koristi objekte za value tipove
Citat:
_v!rus_:
Ovo mi nije baš najjasnije (distinction between metod klase i metod objekta, metod klase = static metod?, kako inace radi kada je ref. tip?), ako imas vremena plz pojasni, baš bi hteo da ukapiram kako se to ispod haube dešava....
Moj typo; oprosti, juce je bio mnogo plahovit dan
U ovom primeru sam mislio sam na metod, iliti objektni metod, nikakve veze sa statickim (class) metodama.
Ako znas kako radi poziv metoda (razlika u odnosu na funkcije/class metode); onda znas da se pri svakom pozivu kao prvi implicitni parametar na stek prosledjuje referenca na objekat. Taj parametar je "nevidljiv" i u samoj metodi se referencira sa this (ili Me za VB.NET; ako se secam Delphi-a dobro, tamo je bilo "self"). E sad zamisli da na stek ne ide referenca na objekat nego zaista sama vrednost value-type-a (za npr. x gore ide 4 bajta koji predtsavljaju binarnu vrednost 5). Samim tim to nije referenca i this u ToString nije referenca nego vrednost 5
Samo sto je ToString za Int32 "nasviran" i zna sta mu je na steku i kako da ga obradi.
Ovo je sve moguce zato sto .NET stek nije ogranicen na 32bit chunks, ako tako pozeli CLI moze da stavi 1 bajt na stek, a moze i 8 ili 10 ili koliko mu vec treba da smesti neki value type.
Ti ovo ne mozes da primenjujes zato sto evaluacija implicitnog parametra u this ide automtski za sve metode koje ti mozes da napises.
E sad, pogledaj xo.ToString();
U ovom slucaju xo je object i na stek ne ide value-type nego referenca na instancu Int32 klase i poziv metoda ToString se obavlja preko vTable kao i za "tvoje" metode. Nekom magijom u jezgru CLSa metod Int32.ToString() zna kad je dobio value-type a kad referencu (moja pretpostavka je da cak CLS to resava interno sa dva razlicita metoda).
Citat:
_v!rus_:
Šta je u tom slučaju sa "originalnim" textBox1.Font? GC će ga zgazi jer je refCount=0?
I još par stvari, ako neko ima vremena, čisto rasprave radi:
1. Na osnovu postova ovde vidim da izgleda postoji jasna razlika između reference i pointera. Jel može neko to samo malo da pojasni?
2. Ako sam dobro shvatio Dragog Tatu, rule of thumb bi bilo: object.Dispose() koristimo kada nam ne treba dalji rad sa objektom, actual memorija biti oslobođena tek kada GC stupi na scenu. Posle poziva object.Dispose() postaviti object = null da mi obavimo naš deo posla, a nek' framework brine o eventualnim svojim referencama. Jel to OK?
Ok, prva stvar, .NET nije COM2.0
.NET objekti ne koriste reference counting. Cak ni kad je .NET objekat publikovan kao COM objekat (u tom slucaju CCW pravi COM proxy u kome drzi reference counter). GC radi na sasvim drugaciji nacin. Vise detalja u ova dva clanka:
Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework
Garbage Collection—Part 2: Automatic Memory Management in the Microsoft .NET Framework
Ovde ima sve detaljno objasnjeno plus neke zanimljivosti o geenracijama i jedinstvenoj mogucnosti .NET objekata (mogucnosti da vaskrsnu
)
Ali ukratko:
1. Referenca pokazuje na objekat
. Kako to radi nije konkretno stvar C#-a nego implementacije CLS-a
Znam da je to zbunjujuce, ali postoji ta zona sivila izmedju samog standarda i njegove implementacije, posto je standard cross-platform, a implementacija platform dependant.
Na PC-u pod windows-om referenca je (uprosceno) pointer na pointer na memorijski blok na HEAP-u ako je lokalna varijabla, ili pointer na objekat ako je polje u drugom objektu
npr: Font x = new Font();
x je lokalna varijabla (referenca) i u stvari je pointer u ROOTS segment aplikacije (koji je negde tamo
). Taj pointer se nece promeniti dok god je varijabla x u scope-u. Lokacija u ROOTS-u na koju pointer u x-u pokazuje je u stvari pointer na HEAP gde se nalazi memorijski footprint instance klase Font.
Kad GC obavi cleanup, pomerice instancu na HEAP-u i promenice njenu adresu na odgovarajucoj lokaciji u ROOTS-u; samim tim referenca x i dalje pokazuje (preko dva koraka) na isti objekat iako je njegova fizicka lokacija u memoriji promenjena. A da ti ne bi lupao glavu oko toga sta je gde pomereno i kako, C# ti omogucava da koristis x = 3; umesto **x = 3;
Ovime je takodje omoguceno da GC ne mora da juri sve moguce pointere na HEAP po aplikaciji posto su svi lepo hijerarhijski poredjani u ROOTS-u
Posto svi "zivi objekti" moraju da postoje u ROOTS-u ili da budu polja u objektima koji su u ROOTS-u, GC moze da rekurzivno napravi memorijsku mapu zivih objekata (nasuprot svim objektima) i da tako bez reference counter-a odredi koji objekti su "mrtvi" i mogu da se izbace iz memorije. Vise detalja imas u gornja dva clanka.
2. Dispose nije deo .NET tehnologije, Dispose je pattern koji je veoma lepo primenljiv na ono sto je DragiTata napomenuo, potrebu da se oslobode unmanaged resursi (
Implementing Finalize and Dispose to Clean Up Unmanaged Resources) Posto se GC nece pokretati bez preke potrebe (na serverima sa dosta memorije to moze da bude tek kad se aplikacija ugasi sto moze da bude i par meseci nakon sto su sve reference nullovane i sve to vreme ce unamanged resurs biti zakljucan), Dispose se koristi da bi se ti resursi oslobodili bez potrebe da se ceka na GC. Sam .NET Framework koristi Dispose pattern u klasama koje barataju sa unmanaged resursima.
Jedina stvar koja mene neopisivo nervira u ovom paternu je to "ziv sam al sam mrtav" stanje u kome je objekat nakon poziva Dispose()
Chris je razjasnio neke zablude o Dispose-u u
http://blogs.msdn.com/clyon/archive/2004/09/21/232445.aspx
Tako da je odgovor na tvoje pitanje DA, posle dispose postavi sve reference na null jer je objekat ionako neupotrebljiv bez tih unmanged resursa koje si upravo oslobodio, a kad GC odradi svoj posao bas te pa briga
E sad, samo jos jedna stvar, cisto da smo nacisto zasto je CLS na PC/Windows nasviran da favorizuje value-types. Problem je sa cisto sa stanovista performansi. Pogledajmo memory footprint za x i xo iz gornjih primera:
x zauzima 4 bajta na steku
xo zauzima: 4 bajta na steku za prvi pointer, 4 bajta u ROOTs-u za HEAP pointer; na HEAP-u: 4 bajta za vTable, 4 bajta za SyncLock polje i 4 bajta za sam integer value
, dakle 12 bajtova za isntancu Int23 kalse plus 8 overhead, ukupno 20 bajtova.
Array od milion integera ce dakle zauzimati 4.000.012 bajtova (tih 12 bajtova posto je array i sam objekat), dok ce array od milion integer objekata zauzimati 12.000.012 bajtova. Pristupanje svakom elementu prvog niza je pristup vrednosti, pristup svakom elementu drugog niza je reference evaluation da bi se doslo do interne promenljive koja drzi vrednost. Da nema nasviranosti .NET bi bio spor koliko i VB6 ako ne i sporiji
Sloba je za 12 godina promenio antropološki kod srpskog naroda. On je od jednog
naroda koji je bio veseo, pomalo površan, od jednog naroda koji je bio znatiželjan, koji
je voleo da vidi, da putuje, da upozna,
od naroda koji je bio kosmopolitski napravio narod koji je namršten, mrzovoljan,
sumnjicav, zaplašen, narod koji se stalno nešto žali, kome je stalno neko kriv - Z.Đinđić