Pouze tento týden sleva až 80 % na e-learning týkající se Javy. Zároveň využij akce až 50 % zdarma při nákupu e-learningu - více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
java week + discount 50

Lekce 4 - Referenční a hodnotové datové typy

V předešlém cvičení, Řešené úlohy k 3. lekci OOP v C# .NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Začínáme pracovat s objekty a objekty jsou referenčními datovými typy, které se v některých ohledech chovají jinak, než typy hodnotové (např. int). Je důležité, abychom přesně věděli, co se uvnitř programu děje, jinak by nás v budoucnu mohlo leccos překvapit.

Zopakujme si pro jistotu ještě jednou, co jsou to hodnotové typy. Obecně jsou to jednoduché struktury, např. jedno číslo, jeden znak. Většinou se chce, abychom s nimi pracovali co nejrychleji, v programu se jich vyskytuje velmi mnoho a zabírají málo místa. V anglické literatuře jsou často popisovány slovy light-weight. Mají pevnou velikost. Příkladem jsou např. int, float, double, char, bool a další.

Aplikace (resp. její vlákno) má operačním systémem přidělenou paměť v podobě tzv. zásobníku (stack). Jedná se o velmi rychlou paměť s přímým přístupem, její velikost aplikace nemůže ovlivnit, prostředky jsou přidělovány operačním systémem. Tato malá a rychlá paměť je využívána k ukládání lokálních proměnných hodnotového typu (až na výjimky při iteracích, kterými se nebudeme zabývat). Proměnnou si v ní můžeme představit asi takto:

Zásobník vb paměti počítače

Na obrázku je znázorněna paměť, kterou může naše aplikace využívat. V aplikaci jsme si vytvořili proměnnou a typu int. Její hodnota je 56 a uložila se nám přímo do zásobníku. Kód by mohl vypadat takto:

int a = 56;

Můžeme to chápat tak, že proměnná a má přidělenu část paměti v zásobníku (velikosti datového typu int, tedy 32 bitů), ve které je uložena hodnota 56.

Vytvořme si novou konzolovou aplikaci a přidejme si k ní jednoduchou třídu, která bude reprezentovat uživatele nějakého systému. Pro názornost vypustím komentáře a nebudu řešit viditelnosti:

class Uzivatel
{
    public int vek;
    public string jmeno;

    public Uzivatel(string jmeno, int vek)
    {
        this.jmeno = jmeno;
        this.vek = vek;
    }

    public override string ToString()
    {
        return jmeno;
    }
}

Třída má 2 jednoduché veřejné atributy, konstruktor a přetížený ToString(), abychom uživatele mohli jednoduše vypisovat. Do našeho původního programu přidejme vytvoření instance této třídy:

int a = 56;
Uzivatel u = new Uzivatel("Jan Novák", 28);

Proměnná u je nyní referenčního typu. Podívejme se na novou situaci v paměti:

Zásobník a halda v paměti počítače

Vidíme, že objekt (proměnná referenčního datového typu) se již neukládá do zásobníku, ale do paměti zvané halda. Je to z toho důvodu, že objekt je zpravidla složitější než hodnotový datový typ (většinou obsahuje hned několik dalších atributů) a také zabírá více místa v paměti.

Zásobník i halda se nacházejí v paměti RAM. Rozdíl je v přístupu a velikosti. Halda je prakticky neomezená paměť, ke které je však přístup složitější a tím pádem pomalejší. Naopak zásobník je paměť rychlá, ale velikostně omezená.

Proměnné referenčního typu jsou v paměti uloženy vlastně nadvakrát, jednou v zásobníku a jednou v haldě. V zásobníku je uložena pouze tzv. reference, tedy odkaz do haldy, kde se poté nalézá opravdový objekt.

Např. v C++ je velký rozdíl mezi pojmem ukazatel a reference. C# žádné ukazatele naštěstí nemá a používá termín reference, ty se paradoxně principem podobají spíše ukazatelům v C++. Pojmy ukazatel a reference zde zmíněné tedy znamenají referenci ve smyslu C# a nemají s C++ nic společného.

Můžete se ptát, proč je to takto udělané. Důvodů je hned několik, pojďme si některé vyjmenovat:

  1. Místo ve stacku je omezené.
  2. Když budeme chtít použít objekt vícekrát (např. ho předat jako parametr do několika metod), nemusíme ho v programu předávat jako kopii. Předáme pouze malý hodnotový typ s referencí na objekt místo toho, abychom obecně paměťově náročný objekt kopírovali. Toto si vzápětí ukážeme.
  3. Pomocí referencí můžeme jednoduše vytvářet struktury s dynamickou velikostí, např. struktury podobné poli, do kterých můžeme za běhu vkládat nové prvky. Ty jsou na sebe navzájem odkazovány referencemi, jako řetěz objektů.

Založme si 2 proměnné typu int a 2 proměnné typu Uzivatel:

int a = 56;
int b = 28;
Uzivatel u = new Uzivatel("Jan Novák", 28);
Uzivatel v = new Uzivatel("Josef Nový", 32);

Situace v paměti bude následující:

Referenční hodnoty v C# v paměti počítače

Nyní zkusme přiřadit do proměnné a proměnnou b. Stejně tak přiřadíme i proměnnou v do proměnné u. Hodnotový typ se v zásobníku jen zkopíruje, u objektu se zkopíruje pouze reference (což je vlastně také hodnotový typ), ale objekt máme stále jen jeden. V kódu vykonáme tedy toto:

int a = 56;
int b = 28;
Uzivatel u = new Uzivatel("Jan Novák", 28);
Uzivatel v = new Uzivatel("Josef Nový", 32);
a = b;
u = v;

V paměti bude celá situace vypadat následovně:

Referenční hodnoty v C# v paměti počítače

Přesvědčme se o tom, abyste viděli, že to opravdu tak je :) Nejprve si necháme všechny čtyři proměnné vypsat před a po změně. Protože budeme výpis volat vícekrát, napíši ho poněkud úsporněji. Mohli bychom dát výpis do metody, ale ještě nevíme, jak deklarovat metody přímo v Program.cs a zpravidla se to ani moc nedělá, pro vážnější práci bychom si měli udělat třídu. Upravme tedy kód na následující:

            // založení proměnných
            int a = 56;
            int b = 28;
            Uzivatel u = new Uzivatel("Jan Novák", 28);
            Uzivatel v = new Uzivatel("Josef Nový", 32);
            Console.WriteLine("a: {0}\nb: {1}\nu: {2}\nv: {3}\n", a, b, u, v);
            // přiřazování
            a = b;
            u = v;
            Console.WriteLine("a: {0}\nb: {1}\nu: {2}\nv: {3}\n", a, b, u, v);
            Console.ReadKey();
    class Uzivatel
    {
        public int vek;
        public string jmeno;

        public Uzivatel(string jmeno, int vek)
        {
            this.jmeno = jmeno;
            this.vek = vek;
        }

        public override string ToString()
        {
            return jmeno;
        }
    }

Na výstupu programu zatím rozdíl mezi hodnotovým a referenčním typem nepoznáme:

Konzolová aplikace
a: 56
b: 28
u: Jan Novák
v: Josef Nový

a: 28
b: 28
u: Josef Nový
v: Josef Nový

Nicméně víme, že zatímco v a a b jsou opravdu 2 různá čísla se stejnou hodnotou, v u a v je ten samý objekt. Pojďme změnit jméno uživatele v a dle našich předpokladů by se měla změna projevit i v proměnné u. K programu připíšeme:

            // změna
            v.jmeno = "John Doe";
            Console.WriteLine("u: {0}\nv: {1}\n", u, v);
                class Uzivatel
    {
        public int vek;
        public string jmeno;

        public Uzivatel(string jmeno, int vek)
        {
            this.jmeno = jmeno;
            this.vek = vek;
        }

        public override string ToString()
        {
            return jmeno;
        }
    }

Změnili jsme objekt v proměnné v a znovu vypíšeme u a v:

Konzolová aplikace
a: 56
b: 28
u: Jan Novák
v: Josef Nový

a: 28
b: 28
u: Josef Nový
v: Josef Nový

u: John Doe
v: John Doe

Spolu se změnou v se změní i u, protože proměnné ukazují na ten samý objekt. Jestli se ptáte, jak vytvořit opravdovou kopii objektu, tak nejjednodušší je objekt znovu vytvořit pomocí konstruktoru a dát do něj stejná data. Dále můžeme použít klonování, ale o tom zas až někdy jindy. Připomeňme si situaci v paměti ještě jednou a zaměřme se na Jana Nováka.

Referenční hodnoty v C# v paměti počítače

Co se sním stane? "Sežere" ho tzv. Garbage collector.

Garbage collector

Garbage collector a dynamická správa paměti

Paměť můžeme v programech alokovat staticky, to znamená, že ve zdrojovém kódu předem určíme, kolik jí budeme používat. Doposud jsme to tak vlastně dělali a neměli jsme s tím problém, hezky jsme do zdrojového kódu napsali potřebné proměnné. Brzy se ale budeme setkávat s aplikacemi (a už jsme se vlastně i setkali), kdy nebudeme před spuštěním přesně vědět, kolik paměti budeme potřebovat. Vzpomeňte si na program, který zprůměroval zadané hodnoty v poli. Na počet hodnot jsme se uživatele zeptali až za běhu programu. CLR tedy musel za běhu programu pole v paměti založit. V tomto případě hovoříme o dynamické správě paměti.

V minulosti, hlavně v dobách jazyků C, Pascal a C++, se k tomuto účelu používaly tzv. pointery, neboli přímé ukazatele do paměti. Vesměs to fungovalo tak, že jsme si řekli operačnímu systému o kus paměti o určité velikosti. On ji pro nás vyhradil a dal nám její adresu. Na toto místo v paměti jsme měli pointer, přes který jsme s pamětí pracovali. Problém byl, že nikdo nehlídal, co do paměti dáváme (ukazatel směřoval na začátek vyhrazeného prostoru). Když jsme tam dali něco většího, zkrátka se to stejně uložilo a přepsala se data za naším prostorem, která patřila třeba jinému programu nebo operačnímu systému (v tom případě by naši aplikaci OS asi zabil - zastavil). Často jsme si však my v paměti přepsali nějaká další data našeho programu a program se začal chovat chaoticky. Představte si, že si uložíte uživatele do pole a v tu chvíli se vám najednou změní barva uživatelského prostředí, tedy něco, co s tím vůbec nesouvisí. Hodiny strávíte tím, že kontrolujete kód pro změnu barvy, poté zjistíte, že je chyba v založení uživatele, kdy dojde k přetečení paměti a přepsání hodnot barvy.

Když naopak nějaký objekt přestaneme používat, musíme po něm místo sami uvolnit, pokud to neuděláme, paměť zůstane blokovaná. Pokud toto děláme např. v nějaké metodě a zapomeneme paměť uvolňovat, naše aplikace začne padat, případně zasekne celý operační systém. Taková chyba se opět špatně hledá, proč program přestane po několika hodinách fungovat? Kde tu chybu v několika tisících řádků kódu vůbec hledat? Nemáme jedinou stopu, nemůžeme se ničeho chytit, musíme projet celý program řádek po řádku nebo začít prozkoumávat paměť počítače, která je v binárce. Brrr. Podobný problém nastane, když si někde paměť uvolníme a následně pointer opět použijeme (zapomeneme, že je uvolněný, to se může lehce stát), povede někam, kde je již uloženého něco jiného a tato data budou opět přepsána. Povede to k nekontrolovanému chování naší aplikace a může to dopadnout i takto:

Blue Screen Of Death – BSOD ve Windows

Můj kolega jednou pravil: "Lidský mozek se nedokáže starat ani o správu paměti vlastní, natož aby řešil memory management programu." Měl samozřejmě pravdu, až na malou skupinu géniů lidi přestalo bavit řešit neustálé a nesmyslné chyby. Za cenu mírného snížení výkonu vznikly řízené jazyky (managed) s tzv. garbage collectorem, jedním z nich je i C# a Java. C++ se samozřejmě nadále používá, ale pouze na specifické programy, např. části operačního systému nebo 3D enginy komerčních her, kde je potřeba z počítače dostat maximální výkon. Na 99% všech ostatních aplikací se hodí C#, kvůli možnosti používat .NET a hlavně automatické správě paměti. Používat .NET bylo umožněno i v C++, hovoříme o tzv. managed C++, kde výsledná aplikace používala garbage collector. Projekt se však neuchytil, protože C++ tak již nemělo žádné výhody oproti C#, který je modernější.

Garbage collector

Garbage collector je vlastně program, který běží paralelně s naší aplikací, v samostatném vlákně. Občas se spustí a podívá se, na které objekty již v paměti nevedou žádné reference. Ty potom odstraní. Ztráta výkonu je minimální a značně to sníží procento sebevražd programátorů, ladících po večerech rozbité pointery. Zapnutí GC můžeme dokonce z kódu ovlivnit, i když to není v 99% případů vůbec potřeba. Protože je jazyk řízený a nepracujeme s přímými pointery, není vůbec možné paměť nějak narušit, nechat ji přetéct a podobně, interpret se o paměť automaticky stará.

Hodnota null

Poslední věc, o které se zmíníme, je tzv. hodnota null. Referenční typy mohou, na rozdíl od hodnotových, nabývat speciální hodnoty a to null. Klíčové slovo null označuje, že reference neukazuje na žádná data. Když nastavíme proměnnou v na null, zrušíme pouze tu jednu referenci. Pokud na náš objekt existuje ještě nějaká reference, bude i nadále existovat. Pokud ne, bude uvolněn GC. Změňme ještě poslední řádky našeho programu na:

            // změna
            v.jmeno = "John Doe";
            v = null;
            Console.WriteLine("u: {0}\nv: {1}\n", u, v);

Výstup:

Konzolová aplikace
a: 56
b: 28
u: Jan Novák
v: Josef Nový

a: 28
b: 28
u: Josef Nový
v: Josef Nový

u: John Doe
v:

Vidíme, že objekt stále existuje a ukazuje na něj proměnná u, v proměnné v již není reference. Hodnota null se bohatě využívá jak uvnitř .NET, tak v databázích. K referenčním typům se ještě jednou vrátíme.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 761x (25.3 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Řešené úlohy k 3. lekci OOP v C# .NET
Všechny články v sekci
Objektově orientované programování v C# .NET
Přeskočit článek
(nedoporučujeme)
Kvíz - Úvod, konstruktory, metody, datové typy v C# .NET OOP
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
219 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

Komentáře

Avatar
Lukáš Hruda
Tvůrce
Avatar
Lukáš Hruda:17.10.2012 21:21

Ne že bych se chtěl učit C# ale kdyby jo tak z tohohle by to určitě šlo, tutorial hodně dobře napsanej řekl bych... ;) Ale jednu věc bych chtěl podotknout. Píšes tu že reference v C# je jako pointer v C++ a s referncí v C++ nemá nic společnýho... to imho neni pravda, reference v C++ funguje do jistý míry stejně jako v C#, je to odkaz na určitou proměnnou nebo objekt, rozdíl je jenom v tom že v C++ určuješ na co se odkazuje jenom v inicializaci později to už nejde, pak můžeš měnit už jenom to na co se odkazuje. Pointer je na druhou stranu v podstatě obyčejná proměnná, jenom v sobě nemá obyčejnou hodnotu ale adresu v paměti. Na tej adrese může bejt cokoliv, nemusí to vůbec bejt platná proměnná nebo objekt, pointer typu int* může klidně držet adresu proměnný typu double nebo char, stejně tak může mít v sobě adresu na který nejsou žádný platný data. S C# referncí má pointer společný jenom to že můžeš kdykoliv změnit na co ukazuje.
Navíc nechápu co všichni máte proti pointerům :D ...pointer je fajn věc a správa paměti přes ně podle mě neni zase takovej problém, stačí si dávat pozor a nemít v kódu bordel :D Nebo je dobrý si třeba pro pole napsat třídu (nebo spíš šablonu třídy), u ní pořešit správu paměti a pak jenom používat...
Ale jinak tutorial dobrej, očividně máš C# rád, to je tvoje věc :)

 
Odpovědět
17.10.2012 21:21
Avatar
Kit
Tvůrce
Avatar
Odpovídá na Lukáš Hruda
Kit:17.10.2012 21:53

Pokud programovací jazyk patří mezi vyšší programovací jazyky, nezná pojem "pointer". Vždy se jedná o odkaz na objekt. Na první pohled je to stejné, ale o správu se stará interní údržba.

Odpovědět
17.10.2012 21:53
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Lukáš Hruda
Tvůrce
Avatar
Odpovídá na Kit
Lukáš Hruda:17.10.2012 22:05

Pokud vim tak v C/C++ se tomu pointer říká normálně a C# pointery nemá.

 
Odpovědět
17.10.2012 22:05
Avatar
Kit
Tvůrce
Avatar
Odpovídá na Lukáš Hruda
Kit:17.10.2012 22:13

Proto se C# počítá mezi vyšší programovací jazyky.

Odpovědět
17.10.2012 22:13
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Lukáš Hruda
Tvůrce
Avatar
Odpovídá na Kit
Lukáš Hruda:17.10.2012 22:29

A já sem v tom prvním komentáři neříkal nic o pointerech v C# :) Rozdělení na nižší a vyšší jazyky je dost relativní jelikož mi přijde že to každej rozděluje jinak. Podle někoho je C/C++ vyšší, podle někoho nižší a podle někoho něco mezi. Nevim jestli existuje nějaký oficiální rozdělení ale já za nižší programovací jazyky vždycky považoval jenom Assembler a Strojovej kód, takže bych řek že i C++ je vyšší ale těžko říct, nepřijde mi to podstatný...

 
Odpovědět
17.10.2012 22:29
Avatar
Kit
Tvůrce
Avatar
Odpovídá na Lukáš Hruda
Kit:17.10.2012 22:50

Jazyk C je vlastně jen nadstavbou Assembleru a proto se počítá mezi jazyky nižší úrovně. C++ na tom není ohledně správy paměti o mnoho lépe a proto se považuje za "něco mezi".

Rozdělení mezi nižší a vyšší se často dělá podle toho, zda se programátor musí starat o správu operační paměti nebo ne. Podle této kategorizace bych C# zařadil mezi "vyšší střední" zejména kvůli nedořešeným destruktorům.

Odpovědět
17.10.2012 22:50
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Lukáš Hruda
Tvůrce
Avatar
Lukáš Hruda:17.10.2012 22:59

Nás ve škole učili a četl jsem to i na některých nezávyslích webech (nemám na mysli wikipedii), že nižší jazyky jsou ty ve kterých musí programátor psát každou instrukci vykonávanou procesorem a program napsanej v takovym jazyku neni spustitelnej na jinym procesoru. Kromě assembleru a storojovýho kódu žádnej jinej takovej jazyk neznam. Podle tohohle beru C i C++ a vlastně všechno krom ASM a strojovýho kódu jako vyšší jazyk... ale jak řikam, nevim podle čeho se to dělí oficiálně.

Editováno 17.10.2012 23:00
 
Odpovědět
17.10.2012 22:59
Avatar
Kit
Tvůrce
Avatar
Odpovídá na Lukáš Hruda
Kit:17.10.2012 23:10

Hranice se posunují. Jazyk C byl kdysi považován za vysokoúrovňový (vyšší úroveň abstrakce), dnes je považován za nízkoúrovňový, protože se sám nedokáže postarat o správu paměti v požadované míře. C++ je na tom o něco lépe, ale právě existence pointerů sráží jeho hodnotu na nižší úroveň.

Dnešní jazyky nesmí programátora vůbec zatěžovat správou paměti, musí to dělat ve vlastní režii.

Odpovědět
17.10.2012 23:10
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Lukáš Hruda
Tvůrce
Avatar
Odpovídá na Kit
Lukáš Hruda:17.10.2012 23:31

Já osobně se správou paměti nějak problémy nemívam a C++ mi vyhovuje. Každopádně tohle už nemá moc společnýho s tím co sem psal původně a víc sem ani psát nechtěl.

 
Odpovědět
17.10.2012 23:31
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Lukáš Hruda
David Čápka:18.10.2012 6:30

Problémy s tím jsou a velké, to je důvod, proč vznikly řízené jazyky :) Ale nebudeme to rozmazávat, díky za komentář.

Odpovědět
18.10.2012 6:30
One of the most common causes of failure is the habit of quitting when one is overtaken by temporary defeat.
Avatar
bohmladislav
Člen
Avatar
bohmladislav:22.12.2012 12:41

Pokud vím, tak i C# může obsahovat céčkovské pointry. Stačí například třídu označit jako unsafe a potom můžete s atributy označenými * pracovat stejně jako s pointry v C++.
Unsafe nemusí být celá třída, ale stačí pouze určitý blok:

unsafe
{
int* ptrA;
}
 
Odpovědět
22.12.2012 12:41
Avatar
Kit
Tvůrce
Avatar
Odpovídá na bohmladislav
Kit:22.12.2012 12:49

Jenže ten, kdo si chce zachovat zbytky zdravého rozumu, takové hacky nepoužívá.

Odpovědět
22.12.2012 12:49
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
bohmladislav
Člen
Avatar
bohmladislav:22.12.2012 13:37

Je pravda, že se to moc nepoužívá, ale není to hack, nýbrž normální součást tohoto jazyka. Například pokud někdo potřebuje provádět matematické výpočty, kde záleží na výkonu, a nechce dělat celý (třeba už hotový) program v C, tak to pro něj může být užitečné. Stačí mu udělat si část výpočtů s použitím pointerů a zbytek dělat v klasickém type safe kódu.

 
Odpovědět
22.12.2012 13:37
Avatar
Kit
Tvůrce
Avatar
Odpovídá na bohmladislav
Kit:22.12.2012 17:58

Pokud budu potřebovat dělat náročné matematické výpočty, tak tu dotyčnou funkci napíšu ve Fortranu a přilepím k aplikaci.

Odpovědět
22.12.2012 17:58
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Kit
Tvůrce
Avatar
Kit:22.12.2012 18:16

Základní rozdíl mezi zásobníkem a haldou je v tom, že likvidace proměnných na zásobníku musí probíhat přesně v opačném pořadí, než v jakém vznikaly. To je velmi výhodné pro procesor, ale nevýhodné pro programátora, který potřebuje objekty likvidovat v pořadí dle vlastního uvážení. Proto vznikla i halda, která si však musí umět poradit s fragmentací paměti po uvolnění objektu. Defragmentaci řeší garbage collector.

Odpovědět
22.12.2012 18:16
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Odpovídá na Kit
Luboš Běhounek Satik:23.12.2012 15:32

Někdy vám nic jiného, než používat takovéhle "hacky", nezbyde, pokud používáte nějakou cizí knihovnu napsanou v C++ apod.

A ne vždy musí tyto unsafe operace být zlo, pokud by to mělo být jen pár řádků, tak dám přednost napsat to v unsafe C# než kvůli tomu vytvářet, importovat a volat knihovnu napsanou v jiném jazyce.

Odpovědět
23.12.2012 15:32
https://www.facebook.com/peasantsandcastles/
Avatar
noName
Neregistrovaný
Avatar
noName:20.10.2013 11:49

Je nějaký rozdíl mezi:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
       {
           GC.Collect();
       }

a nebo když například instanci přiřadím hodnotu null?

Class1 C1 = new Class1();
...
C1 = null;

Jde mi o uvolnění paměti před zavřením daného Formu. (Form1 není hlavní form => po zavření Form1 program pokračuje)

 
Odpovědět
20.10.2013 11:49
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na noName
David Čápka:1.11.2013 15:27

Ani jeden z těch kódů paměť neuvolní. V prvním voláš GC.Collect, ale v tu chvíli Form stále existuje. Druhý kód jen dosadí do reference na formulář null, ale formulář dále existuje v paměti, dokud se nespustí GC.

Odpovědět
1.11.2013 15:27
One of the most common causes of failure is the habit of quitting when one is overtaken by temporary defeat.
Avatar
phoer
Člen
Avatar
phoer:24.11.2013 1:20

Je nějaký rozdíl mezi tímto :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Uzivatel z = new Uzivatel("", 23);
            Uzivatel x = new Uzivatel("", 42);

            z.jmeno = "Pepa Novotny";
            x.jmeno = "Pepa Masek";

            Console.WriteLine("z:{0}\nx:{1}\n", z, x);

            z = x;

            z.jmeno = "Petr Novotny";
            Console.WriteLine("z:{0}\nx:{1}\n", z, x);
            x.jmeno = "Karel Hynek";
            Console.WriteLine("z:{0}\nx:{1}\n", z, x);

            Console.ReadKey();

        }
    }
}

a tím co je v článku ? :)

Editováno 24.11.2013 1:21
Odpovědět
24.11.2013 1:20
„Co slyším, to zapomenu. Co vidím, si pamatuji. Co si vyzkouším, tomu rozumím.“ - Konfucius
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na phoer
David Čápka:27.11.2013 9:29

Co je v článku se dá číst.

Odpovědět
27.11.2013 9:29
One of the most common causes of failure is the habit of quitting when one is overtaken by temporary defeat.
Avatar

Člen
Avatar
:16.1.2015 8:39

Díky za článek, C# mi zatím přijde oproti C dosti sympatický, tak uvidíme jak dál. Jen pár postřehů, ten kód není třeba kontrolovat řádku po řádce, když se použije mudflap nebo valgrind nebo jiná alternativa. A uvolněný pointer by měl člověk správně po uvolnění nastavit na NULL.

 
Odpovědět
16.1.2015 8:39
Avatar
pracansky
Člen
Avatar
pracansky:12.4.2015 21:45

Dovolil bych si říct že stejný program v C# mi zabere třetinu času než v C a bude v něm méně chyb přesto že C znám lépe. Jednoduše se můžu zabývat tím co je podstatné a neřešit provozní problémy.

To že se programátor nemusí starat o správu paměti je ale zavádějící. Když začne pracovat s větším množstvím dat, je potřeba se zamyslet nad tím kdy a proč se paměť alokuje.

Ani alokace ani Garbage collector totiž nejsou zadarmo.

Osobně si myslím že psát aplikace pro PC v C/C++ dnes může jen masochista:)

 
Odpovědět
12.4.2015 21:45
Avatar
Hello World
Člen
Avatar
Hello World:28.8.2015 11:41

Petřeboval bych poradit jak se dá nějakému refernčnímu datovému typu přiřadit hodnota tak, aby se nevytvořil odkaz na jiný objekt, ale aby se hodnota "zkopírovala." Mám kód

IntPtr handle = window.Key; //window je položka v IDictionary<IntPtr, string>
string title = window.Value;

icons.Add(new TaskbarIcon(Icon.FromHandle(handle)) { ToolTip = title });

Jenže po tom co se ikona přiřadí k TaskBarIcon mi to vyvolá ObjectDispose­dException, takže bych potřeboval vědět, jak se dá hodnota zkopírovat z jednoho objektu na druhý.

Odpovědět
28.8.2015 11:41
Jo! Zkompilovalo se to!
Avatar
MrPabloz
Člen
Avatar
Odpovědět
28.8.2015 13:23
Harmonie těla a duše, to je to, oč se snažím! :)
Avatar
Hello World
Člen
Avatar
Odpovědět
28.8.2015 13:28
Jo! Zkompilovalo se to!
Avatar
RooBoo
Člen
Avatar
RooBoo:27.1.2016 13:23

Ahojte neviem ci to tak ma byt ale ak napisem

public override string ToString()
        {

        }

tak mi VS automaticky hodi do {}

return base.ToString();
 
Odpovědět
27.1.2016 13:23
Avatar
pocitac770
Tvůrce
Avatar
Odpovídá na RooBoo
pocitac770:27.1.2016 16:21

Každá metoda, která má návratový typ (v tomto případě .ToString()) ho musí i vracet, na to je tam ono slovo return, a aby nenastala chyba kvůli chybějícímu "návratovému" řádku kódu, tak tam rovnou vloží ten předdefinovaný, který si můžeš upravit podle potřeby na co chceš.

Editováno 27.1.2016 16:22
 
Odpovědět
27.1.2016 16:21
Avatar
blazoid
Člen
Avatar
blazoid:10.5.2016 20:23

Ahoj, mám problém s opětovným vytvořením instance objektu, konkrétně jde o zatížení operační paměti. V kódu níž (jde o Windows forms aplikaci) se při stisknutí tlačítka "loadFileBtn" provede načtení datalogu do listu - otevřu dialogové okno, vyberu soubor, cestu k souboru následně předám v konstruktoru třídy sprava_dat, instance třídy se jmenuje "databáze". Problém nastane, když chci načíst za běhu programu jiný soubor. Stisknu opět tlačítko load... a dám načíst totožný soubor jako před tím. Try/catch blok již však není proveden, krokováním jsem zjistil, že provádění selže právě u metody databaze.Nacti­Log(), právě v této metodě probíhá stream reading načítaného souboru a ukládání do listu. Dle ukazatele využití paměti ve VS nedojde po opětovném vytvoření instance třídy sprava_dat k uvolnění. Zkusil jsem po přiřazení databaze = null, což následuje hned po vytvoření nové instance, zavolat garbage collector, situace se však nezměnila. Načítaný csv soubor má poměrně dost řádků (cca 8400).

Píšu to sem, neboť se mně zdá, že právě tento článek se problematiky dotýká...

Díky moc za případnou pomoc.

private void loadFileBtn_Click_1(object sender, EventArgs e)
{

    // procedura pro výběr souboru, vytvoření instance databáze a načtení hodnot do databáze

    OpenFileDialog opf = new OpenFileDialog();

    opf.Filter = "Choose File(*.csv) | *.csv";


    if (opf.ShowDialog() == DialogResult.OK)
    {
        fileSource = opf.FileName;
        loadedFileNameLabel.Text = fileSource;

        fileCreationTimeLabel.Text = Convert.ToString(File.GetCreationTime(fileSource));
    }

    databaze = null;

    GC.Collect();

    databaze = new sprava_dat(fileSource);



    int pocet_zaznamu = 0;
    try
    {
        pocet_zaznamu = databaze.NactiLog();
       // datalogListBox.Items.Clear();
        //datalogListBox.Items.AddRange(databaze.VratVsechny());

        pocetZaznamuTB.Text = pocet_zaznamu.ToString();


        dataGridView1.DataSource = databaze.VratVsechny();

        loadingDataLogGridView.DataSource = databaze.VypisLoadingLog(infoCheckBox.Checked, warningCheckBox.Checked, errorsCheckBox.Checked);


    }
    catch
    {
        MessageBox.Show("Databázi se nepodařilo načíst, soubor zřejmě neexisituje.",
                "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);

        pocetZaznamuTB.Text = "-";
    }

}
 
Odpovědět
10.5.2016 20:23
Avatar
Miroslav Mazal:16.8.2018 21:49

Celkem to chápu. Má to nějaké praktické uplatnění? Například ovlivní to způsob, jak něco programátor programuje, nebo to má pouze pomoci pochopit práci počítače a jeho paměti?

 
Odpovědět
16.8.2018 21:49
Avatar
vosa53
Člen
Avatar
Odpovídá na Miroslav Mazal
vosa53:17.8.2018 0:16

Ano je to velice praktické. :) Když například v nějaké hře máte jednu instanci třídy Tank (předáváno referencí) na několika místech a na jednom z nich mu snížíte zivot, tak přirozeně očekáváte že ho bude mít snížený i na všech ostatních místech. Oproti tomu když předáváte jen nějaké číslo (předáváno hodnotou), tak je nežádoucí aby se jeho možná pozdější změna projevila všude kam bylo předáno.

 
Odpovědět
17.8.2018 0:16
Avatar
Miroslav Mazal:17.8.2018 15:39

Asi chápu, tu základní myšlenku, kterou jsi mi napsal. Ale úplně to asi pochopím, až to prakticky budu muset někde použít.

 
Odpovědět
17.8.2018 15:39
Avatar
Rojo Violencia:5.1.2019 18:32

Web Itnetwork mi prijde az moc rozsahly, a tak jsem si pustil 4 a pul hodinove vyukove video o C# kde jsem se toho naucil opravdu hodne za pomerne maly cas (v anglictine).Nicmene ani jednou jsem nepouzil "public override string" a nejsem si presne jisty k cemu slouzi. Tak jsem si to tady cele precetl a nejak jsem nepobral ten potencial, v cem je ta vyhoda oproti tomuto reseni ?

 
Odpovědět
5.1.2019 18:32
Avatar
Odpovídá na Rojo Violencia
Neaktivní uživatel:5.1.2019 20:36

Většina metod, která potřebuje z jakéhokoliv důvodu získat řetězec, zpravidla používá <code>object.ToS­tring()</code> (teda pokud se nepletu, nijak podrobně jsem to neprocházel), kde <code>object</code> je samozřejmě hodnota, kterou metoda dostala v parametrech. Takže je jednodušší napsat jen <code>u</code>, a ne <code>u.jmeno</co­de>. A co kdybys chtěl místo jména použít (třeba) vzdělání? Bavilo by tě přepisovat skoro (ne úplně, tím se situace zhoršuje) každé <code>u.jmeno</co­de> na <code>u.vzdela­ni</code>? (nebo jméno a věk, to bývá běžnější)

Odpovědět
5.1.2019 20:36
Neaktivní uživatelský účet
Avatar
Odpovídá na Neaktivní uživatel
Rojo Violencia:5.1.2019 20:39

Uz jsem to pochopil :) Diky

 
Odpovědět
5.1.2019 20:39
Avatar
Libor Novák
Člen
Avatar
Libor Novák:21.6.2019 9:25

Tento článek ve mě vzbuzuje dojem, že jazyky Pascal a C/C++ jsou zastaralé a používání ukazatelů je přežitek. Rád bych zdůraznil, že jazyky C-sharp a Java vychází pravě z jazyka C/C++ a označují se za jazyky rodiny C či C like jazyky. Pokud budete umět C/C++ umíte 90% všech programovacích jazyků rodiny C a ještě rozumíte práci s ukazateli. To je obrovská výhoda. Jazyk C/C++ je stejně moderní jako C# nebo Java. Jen je to jazyk nižší úrovně, díky čemuž je ovšem mnohem mocnější a robustnější a umožňuje tak programovat velmi širokou škálu aplikací, které jsou navíc velmi rychlé a výkonně, protože kód v C/C++ je plně kompilován, nikoli částečně, jako v C# nebo v Javě. Možnost řízení práce s pamětí pomocí ukazatelů nám dává tu výhodu, že můžeme s pamětí pracovat velmi efektivně a tak může být aplikace velmi rychlá s nízkou režii. Nevýhoda jazyka C++ je v tom, že je oproti C# složitější na pochopení.
I když s ukazateli v C# nepracujeme přímo je dobré je znát. Mnozí programátoři, kteří ukazatele neznají, pak nechápou, co je to vlastně this a jak fungují reference na objekty a pak v programech vytváří velmi záludné chyby. Jazyky C/C++ stanovily principy programování, které převzaly i ostatní programovací jazyky. Jazyky Java a C# jsou na vyšší úrovni než C/C++ a jsou tedy vhodnější pro jiné typy aplikací než jazyk C/C++. To ale neznamená, že by byl jazyk C/C++ zastaralý a nemoderní. Je dokonce normován mezinárodní normou ANSII, což jazyky C# a Java nejsou.
Jazyk Pascal slouží pouze jako výukový jazyk, na kterém si začínající programátor osvojí principy a techniky programování. Pascal je jednodušší na pochopení než C a proto se používá při výuce programování například na Matfyzu nebo ČVUT. Z Pascalu se pak přechází většinou na C++ a Java či C# jsou volitelné jazyky. C# má o něco přívětivější syntaxi než C++ a proto pro program, který není náročný na výkon a poběží na nějaké platformě Microsoftu, je vhodnější použít C# než C++.

 
Odpovědět
21.6.2019 9:25
Avatar
Odpovídá na Libor Novák
Nositelka Změny:4.1.2020 13:33

"Jazyk C/C++ je stejně moderní jako C# nebo Java. Jen je to jazyk nižší úrovně, ..."
Tohle je trochu podezřelá věta. Troufám si říci, že C(++) nejsou moderní, ale ani zastaralé, protože nabízí možnosti, které v C#/Java ani nejdou udělat. Navíc jejich vývoj nebyl přerušen, v roce 2018 byla vydána poslední norma pro C a letos se chystá nová norma pro C++.
Taky jsem někde četl, že C++ sice nemá GC, ale má něco lepšího a je i proto rychlejší než C#/Java. Je to sice článek starý 6 let, ale nemyslím si, že zrovna tohle bylo jinak (spíš to bude ještě lepší :-) )

Odpovědět
4.1.2020 13:33
j.k.j
Avatar
Václav Pekárek:4.1.2020 13:46

Díky za článek :-)

Odpovědět
4.1.2020 13:46
Cogito ergo sum
Avatar
David H.
Člen
Avatar
David H.:9.4.2020 0:35

Díky za článek. ;-)

 
Odpovědět
9.4.2020 0:35
Avatar
Ondrej Chodura:18.4.2020 11:29

ahoj, nasel jsem tam chybku - "konstruktor a přetížený ToString(), abychom uživatele mohli jednoduše vypisovat."

  • jedna se o prekryvani metody, ne o pretizeni, to je uplne neco jineho.
 
Odpovědět
18.4.2020 11:29
Avatar
E.K.
Člen
Avatar
E.K.:21.4.2020 14:42

Ahoj. Díky za tutoriál. Chtěl jsem si ověřit, jak se chová v tomto případě datový typ string. Pokud je tedy referenční, proč v následujícím kódu je výsledek poroměnných S1 a S2 rozdílný. Neměly by být oba stringy "dcba". Tady to vypadá tak, jakoby se nepřiřadila reference, ale celý obsah stringu. Díky.
static void Main(string[] args)
{
string S1, S2;

S1 = "ABCD";
S2 = "EFGH";
Console.Write­Line("S1 = {0}, S2 = {1}",S1,S2);
S2 = S1;
Console.Write­Line("S1 = {0}, S2 = {1}",S1,S2);
S1 = "dcba";
Console.Write­Line("S1 = {0}, S2 = {1}",S1,S2);
Console.ReadLine();
}

 
Odpovědět
21.4.2020 14:42
Avatar
Petr Žákavec:2.5.2020 23:50

také by mě to zajímalo (string - referenční typ)

 
Odpovědět
2.5.2020 23:50
Avatar
Savi
Člen
Avatar
Savi:20.7.2020 9:55

Ahoj, tak jsem u další lekce a mám zase dotaz :-/.

int a = 56;
            int b = 28;
            Uzivatel u = new Uzivatel("Karel Havlíček", 50);
            Uzivatel v = new Uzivatel("Vlastimil Hebr", 30);

            Console.WriteLine("{0}  {1}  {2}  {3}", a, b, u, v);

            u = v;
            u.jmeno = "Václav Havel";


            Console.WriteLine("{0}  {1}  {2}  {3}", a, b, u, v);

            Console.ReadKey();

Z textu jsem pochopil, že u=v zařídí to, že u ukazuje na Vlastimila. Proč když změním jméno v u až "po u=v", tak se mi vypíše 2x Havel? Představil jsem si ten obrázek v článku, kdy mi obě reference ukazují na Vlastimila a já měním jméno nevyužitého objektu. Může za to ten GC? :-O Nebo to chápu úplně blbě ? Děkuji !!

 
Odpovědět
20.7.2020 9:55
Avatar
Radek Veverka
Tvůrce
Avatar
Odpovídá na Savi
Radek Veverka:20.7.2020 10:25

Představil jsem si ten obrázek v článku, kdy mi obě reference ukazují na Vlastimila a já měním jméno nevyužitého objektu.

Když ti obě reference ukazují na Vlastimila, tak jak můžeš měnit jméno nevyužitého objektu? Měníš jméno Vlastimila a je jedno jestli přes u nebo v.

 
Odpovědět
20.7.2020 10:25
Avatar
Savi
Člen
Avatar
Odpovídá na Radek Veverka
Savi:20.7.2020 10:50

Perfektní. Už to asi chápu. Koukal jsem na to trochu jinak ;) Děkuji !

 
Odpovědět
20.7.2020 10:50
Avatar
Odpovídá na E.K.
Bohumír Bednařík:22.7.2020 11:08

Řekl bych, že string je tak trošku jiný typ. Viz např. https://docs.microsoft.com/…ystem.string?…

Důležitá bude nejspíš ta pasáž:
The value of the String object is the content of the sequential collection of System.Char objects, and that value is immutable (that is, it is read-only).

Když do proměnné typu string zapíšeš nový řetzec, vlastně ji znovu vytváříš, bez ohledu na to, že jsi si ji před tím "zkopíroval" z jiné proměnné typu string.

Pokud se pletu, nechť mě někdo opraví (nejsem programátor).

 
Odpovědět
22.7.2020 11:08
Avatar
Poledno Martin:8.9.2021 12:40

Super článek! díky

 
Odpovědět
8.9.2021 12:40
Avatar
Petr Mašek
Člen
Avatar
Petr Mašek:10. července 18:24

Ahoj, mám dotaz.. řídil jsem se návodem a dokonce si i stáhnul soubor v lekci, a přesto mi VS vyhazuje chybu a nelze jej spustit. Věděl by někdo co s tím? Kódy mám identické a stejně nejede. Děkuji za odezvu

 
Odpovědět
10. července 18:24
Avatar
Martin McDermot:25. července 10:21

Jen si odložím, že klasické proměnné (int, float. atd) můžou také mít hodnotu "null".

Tato hodnota nepůjde

public int hodnota = null;

Ale tato ano

public int ?hodnota = null;
 
Odpovědět
25. července 10:21
Avatar
Odpovídá na Petr Mašek
Jaroslav Drobek:2. srpna 6:18

Máš tam namespace Ref_a_hodnot - pokud zařídíš stejný jmenný prostor i u třídy Uzivatel.cs, mělo by to fungovat. V poskytnutých zdrojácích (Program.cs, Uzivatel.cs) bylo namespace referencni_typy.

 
Odpovědět
2. srpna 6:18
Avatar
Petr Mašek
Člen
Avatar
Odpovídá na Jaroslav Drobek
Petr Mašek:2. srpna 8:24

Já jsem trubka.. díky už to funguje

 
Odpovědět
2. srpna 8:24
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 50 zpráv z 50.