Ahoj, bylo mi řečeno že používat out parametry je to nejhorší co
můžu udělat. A tak jsem tak nějak zkoušel a nevím teda co mam
používat... byl bych vděčný kdyby mi někdo řekl co je z těchto 3ech
verzí nejlepší a proč díky.
// main.cs
Trida t = new Trida();
int cislo = t.UkazCislo();
// Trida.cspublicint UkazCislo()
{
return5;
}
nebo
// main.cs
Trida t = new Trida();
int i;
t.UkazCislo(i);
// Trida.cspublicvoid UkazCislo(out i)
{
i = 5;
}
a nebo...
// main.cs
Trida t = new Trida();
int i;
t.UkazCislo(ref i);
// Trida.cspublicint UkazCislo(ref i)
{
i = 5;
return i;
}
První verze je nejhezčí, ostatní použiješ jen v hodně speciálních
případech a běžně se nepoužívají.
Normálně se v C# parametry předávají hodnotou - když zavoláš nějakou
funkci, tak se vytvoří kopie parametrů a ty se té funkci předají, takže
když je změníš, tak je změníš jen lokálně uvnitř funkce.
Když je předáváš přes ref, tak se předává odkaz -
co změníš uvnitř funkce, změní i ty data mimo ni, protože měníš data
na místě, kam ta reference ukazuje (a ne jen kopii těch dat).
Pozor na chování, pokud v parametrech předáváš (bez
ref) třídu (na rozdíl od structu, kdy se kopírují jeho
data), tak se nevytváří kopie jejích dat, ale kopie ukazatele na ni. Pokud
do ní přiřadíš null nebo nějaký jiný objekt, tak to děláš jen
lokálně v té funkci, ale pokud měníš její data, tak ty už měníš
přímo na tom objektu i mimo funkci.
out je obdoba ref a používá se, když se předpokládá,
že v době vstupování ta proměnná ještě neexistuje, musíš ji uvnitř
funkce vytvořit.
Nevím - jak jsem psal - nikdy jsem to nepotřeboval. Proč je out třeba v
TryParse? Všude jinde se normálně vrací -1 při nezdařeném pokusu - proč
je TryParse výjimkou? Atd...
TryParse vrací zda byla hodnota úspěšně naparsována. Pokud ano, pak se
uloží do zadané proměnné a já s ní můžu pak pracovat. Pokud se nepovede
parsování, tak se provede jiná větev. To je krásný příklad, kde to
použít. Pokud toho potřebuju vrátit víc. Зайчик: Je lepší používat return. ref a out jen tam, kde toho
potřebuješ vrátit víc. Je to takové přirozenější. A i si můžeš
všimnout, že se nepoužívají tak hojně, protože to není většinou
třeba.
Když toho potřebuju vracet víc, tak dávám většinou přednost udělat
si na výsledek nějakej zabalovací struct/třídu, kterým si těch víc
hodnot vrátím, ale dá se smozřejmě použít i ref nebo out.
Jj tak to určitě. Já spíš myslel třeba, když jsem si dělal input
message box. Tak mi vracel DialogResult a jako ref jsem si nastavil výchozí
text pro textbox, ten se pak dal případně změnit. Když těch parametrů
není moc tak je to takhle v pohodě imho.
Tohle co popisuješ je perfektní příklad pro vhodné využití ref - pokud
se má vracet hodnota a ještě jedna proměnná, která se ale dá nastavit
při volání.
Out a ref jsou zbytečné a vždy se jim dá vyhnout pomocí výjimek nebo
vrácením nějaké kolekce/objektu. Každý jazyk má něco, co by se nemělo
používat. TryParse je zkrácení kódu za cenu snížení jeho kvality, asi by
to tam být nemělo.
Není žádný důvod, proč by se out a ref nemělo nepoužívat.
Out a ref prasárna nejsou, právě naopak, prasárna by bylo použít na
vracení výsledku funkce výjimku.
Název výjimka vychází z toho, že se stávají výjimečně...
Taky mají výjimky o něco vyšší režii.
Pokud se out používá jen k předání chybového stavu (jako v TryParse,
přesněji nahrazuje původní návratovou hodnotu metody, která nahrazuje
výjimku), je to prasárna. K tomu jsou výjimky Pokud chci, aby mi funkce
vrátila 2 věci (přesněji tedy 3: 2 hodnoty a informaci o chybě), většinou
je někde chyba v návrhu.
Ne, vyvolávat výjimku při drobný akci - jako je třeba parsování
stringu je prostě prasárna, za tím si stojím.
Výjimky maj ohromnou režii - schválně jsem to teď testoval.
Rozdíl doby trvání těchto dvou kódů:
if (!Int32.TryParse(parse, out res))
res = 42;
a
try
{
res = Int32.Parse(parse);
}
catch
{
res = 42;
}
je v případě vyvolání výjimky více než 600 (!) násobek v neprospěch
výjimky.
600 už mi přijde jako docela velkej důvod pro nepoužívání výjimek
zbytečně.
A když bys ty kódy spustil v Visual Studiu, tam ten rozdíl v času je (u
mě) 50 000 násobek, protože se chyba vypisuje do konzole.
Není to jen tím, že tolikrát zasebou vytváříš a opouštíš
chránněnou sekci? Obvykle mám metodu co mi naparsuje vstup a pokud to někde
upadne, moc mě nazajímá co přesně je zadáno špatně. Takže mám v jednom
try-catch bloku parsování všech hodnot. Navíc v GUI se už moc neparsuje, to
je spíše konzolová záležitost nebo záležitost souborů, kde jsem stejně
ve schráněnné sekci.
Když ti to umře na parsování jedný věci, tak se to pak ale přeruší
úplně celý, takže kdybych dal do try-catch celej cyklus, tak to po pokusu o
naparsování první hodnoty vykočí.
Java např. nic takového nemá, v těhle vyšších jazycích k tomu
slouží výjimky a na rychlost se nehledí. Chápu, že tobě to jako
lowlevelákovi přijde hrozné, ale až tak špatné to není
Matesi a dost. Teď jsem viděl tvůj vejžblebtek o zbytečnosti "var", tady
jsou zase zbytečné "out" a "ref".
Víš proč je TryParse řešené přes out? Protože:
int hodnota;
if (int.TryParse(str, out hodnota) && (hodnota > 0))
....
Srovnej si eleganci tohohle řešení s těmi tvými vyjímkami (vím, že
obecně řídíš kód výjimkami, protože jseš prase, ale ne, není to
správné).
Pro případ, kdy chceš aby špatný formát vstupu vyhodil výjimku, tu
máš int.Parse. Try znamená "zkusit", takže každému s mozkem asi doje,
proč TryParse nevyhazuje výjimku. Borec tvého
formátu si samozřejmě před voláním Try projde v cyklu znaky stringu a
podle ASCII si zkontroluje, jestli jsou to jen čísla a znménko -. Před
voláním DateTime.TryParseExact si můžeš udělat regex. A já zatím
klidně použiju to out.
Proč má třeba Dictionary TryGetValue(key, out value), místo aby nevím
vlastně co vracel?
Takhle přehledného zápisu s jinou konstrukcí nedosáhneš, nehledě na
výkon.
Ref je taky rychlejší než vytváření kopie. Tebe, Matesi, samozřejmě
výkon netrápí, ale jestli víš co je třeba .NET MicroFramework, tak tam už
ref smysl má.
Líbí se mi, kolik lidí by tady umělo Microsoftu poladit .NET. Nechápu,
proč už tam dávno nešéfujete vývoji.
Prasárna je neustále větvit - a také to, že to odporuje logice -
TryParse - Try == pokus omyl. Try == Try blok... Ne větvení - které nepokryje
všechny možnosti... Try blok vyhodí zprávu o problému - a díky této
zprávě víme přesně o co go...
Neznám jediné valné využití var, ref a out... Cpeš sem vzhled kódu a
ukecanost, která ale k C# patří. Už jen proto, že je silně typovaný
atd...
je zajímavé sledovat jak se tu lidi oháněj tím že je něco prasárna
obvykle je to ale pouze něco čeho se zoufale bojí nebo to radši
nepoužívají protože nechápou význam dané věci...
Ehm, boolean je chybový stav? Ty jseš teda srandista Na CHYBOVÝ stav samozřejmě
existuje řešení - int.Parse. Pokud TryParse vrací false, není to žádný
chybový stav, ale prostá informace o tom, že vstup není textová
reprezentace integeru. Tečka. Za to, že ty v tom vidíš nějaký chybový
stav, .NET ani lidi co chápou out nemůžou.
Ne, je to chybový stav, který je zcela špatně zaměněn za hodnotu funkce
a ta se místo pomocí return vrací v parametru, čehož je násilně docíleno
pomocí modifikátoru out.
Nevím co přesně myslíš neustálým větvením - možná něco jako je
ten tvůj HTML parser? Nojono, s tím by se i souhlasit dalo.
Tobě připadá, že když metoda Try... vrací boolean, odporuje to
logice?
Přátelé, nevím kolik z vás se programováním živí, a kdo jenom kecá
jako Mates, ale fakt mě zaráží názory některých tady. Nikdo vás přece
nenutí používat var, dynamic, ref, out, C# ani Windows. Dělejte si pokusy o
parsování čísel v Try-Catch. Dokud půjde o práce do školy nebo e-shopy
pro deset zákazníků, bude to asi jedno. Pokud se někdy propracujete k
něčemu zajímavějšímu, praxe vás sama naučí co je nejlepší. Do té
doby ale neshazujte něco, co neumíte správně používat.
A otázka na konec - jak by tedy mělo být napsáno to, co teď normální
bílý muž zná jako:
Dictionary<T1, T2>.TryGetValue(T1 key, out T2 value)
Java ref, out ani var nemá a jedou v ní systémy, kde je určitě více
než deset uživatelů. Je to nejžádanější jazyk z ohledu businessu
mimochodem To že jazyk
něco umí neznamená, že je to dobře. Do PHP v jedné z posledních verzí
také přidali GOTO
Hm. To bych vážně chtěl být tvůj zákazník, s tím co tu
předvádíš.
Jasně, GOTO je úplně špatná věc, protože vždycky když jsem ho
použil, tak jsem se potom ve svém kódu nedokázal vyznat a proto to
používají jen jelimani a ne machři jako jsem já. Programovací jazyky ho
maj proto, že je vymejšlej taky jelimani.
Osobně jsem GOTO nepoužil několik let, ale nevidím jediný důvod, proč
by nemělo existovat.
Že Java ref, out ani var nemá a přesto je skvělá, přece není vůbec
nic zarážejícího. Zarážející je, když si někdo vybere pro práci C# a
.NET a potom se ošklíbá nad věcmi, které ten jazyk umožňuje a framework
používá.
Oháněl jsi se tu businessem, tak ti vysvětluji, že out a ref není nic,
co business potřebuje. Jinak kromě toho zpraseného TryParse jsem to nikde v
.NETu neviděl.
Ale já právě vůbec neříkám, že je Java díky absenci ref a out
horší nebo lepší než C#, je to prostě jiný jazyk. Jiný v hodně věcech,
zdaleka ne jen v tom, že chybí možnost předávat parametry referencí. Vím
toho o Javě poměrně málo, ale není to třeba dáno taky tím, že Java už
hodně dávno uměla anonymní typy, navíc pojaté použitelněji než v C#,
takže bylo vždy snadnější vracet z metody strukturu?
To, že jsi neviděl out jinde než v TryParse vypovídá jen o tom, že
znáš .NET z rychlíku. Ale hlavně že máš názor na to, co používají
jenom jelimani.
Dal jsem příklad s Dictionary.TryGetValue a pořád čekám, že mi tu
nějaký borec vysvětlí, jak to udělat bez prokletého satana out. Předem
říkám, že za lepší řešení nepovažuju takové, kdy se kolekce klíčů
prohledává dvakrát, nebo se při neexistujícím klíči vyhazuje
výjimka.
No jistěěě Foreach
používají jen jelimani, co nevědí jak dlouhou mají kolekci. A IEnumerable
je stejně jen pro blbce, kteří si neumí na začátku správně alokovat
pole.
For je vlastně takový while pro lemply. A while je jenom jinak zapsané
goto s jedním jediným if.
Try-catch je taky taková lemplovská obezlička. Správný programátor si
umí ošetřit vstupy tak, aby chyba nikdy nenastala.
Jak už jsem psal jinde - zbytečné (a obvykle neefektivní!) je úplně
všechno, co je nad strojovým kódem. Jenže já tady všade možně vidím,
jak se tok programu řídí výjimkami, za OOP se považuje mapování názvů
kontrolních prvků na nějaké offsety podle slovníku, místo obyčejného
threadu se dělá instance Timeru na jeden tick, ale ref a out, to slušný
člověk nepoužije protože to Java taky nemá. Ha ha ha.
Nedochází zde k žádnému narušení principů objektově orientovaného
programování, je to jen zjednodušující konstrukce. Jako příklad je to
úplně nesmyslné. Ref a out používají vstupní parametry funkce k výstupu
a to je prasárna, protože to úplně mění původní koncepci programu. Vaše
argumenty nedávají smysl, jak jsem řekl, vracejte si 10 hodnot parametry
funkce když se vám to líbí.
Chybový stav to je, protože očekávám vstup čísla a to číslo jsem
nedostal. Místo čísla dostanu jen jakýsi boolean. To číslo třeba
potřebuji dosadit do vzorce, ale ten vzorec se bez čísla nemá provést ani
srandistům. Musím kvůli tomu testovat hodnotu toho boolean ještě před
dosazením výsledku parsování do vzorce a to je pakárna. Musím kvůli tomu
zbytečně zakládat dvě proměnné.
Vracení hodnot přes parametry narušuje principy objektového
programování? Jaké konkrétně? Nám nic takového neříkali, asi jsem
chodil na špatnou školu...
Co je prasárna na vracení (částí) výsledku přes parametry? Ve
výsledku se to stejně všechno předává stejným způsoběm - přes
zásobník, případně přes registry.
A odpověz prosím na příspěvek m.tecla - cituji
"A otázka na konec - jak by tedy mělo být napsáno to, co teď normální
bílý muž zná jako:"
Dictionary<T1, T2>.TryGetValue(T1 key, out T2 value)
Mě by to zajímalo taky
Kit : Když očekávám číslo a číslo to není, tak s
vyhozením výjimky souhlasím. Ale co když očekávám něco, co číslo být
může, ale nemusí? Pak nemůžu jeden stav oznamovat výjimkou - výsledek,
že to číslo není neznamená chybu a potřebuju nějak zjistit, jestli to
bylo číslo a pokud ano, tak jaké. Řešit to ve dvou funkcích - nejdřív
zjištěním, jestli to číslo je a pak zjištěním, o jaké číslo se jedná
není zrovna efektivní - parsovalo by se to dvakrát - lepší je rozparsovat
to jednou a dát vědět, jestli je to číslo.
V tomhle případě by výjimka byla prasárna, protože bych ji používal k
řízení toku programu a ne k zachytávání výjimek, protože s variantou,
že to číslo není jsem počítal a jsem na to připraven, není to pro mě
výjimka, ale stav.
A srandistům se některé (=ty, u kterých mi nevadí, že
tu hodnotu ze stringu nemám) vzorce i bez čísla přece můžou provádět - s
nějakou defaultní hodnotou, kterou si tam dosadí, když řetězec nebyl
číslo .
A na co zakládat dvě proměnné, když stačí jen ta jedna, ve které je
to číslo?
int number;
if (!Int32.TryParse(str, out number))
number = 42;
elsereturn; // nebo jak chci zareagovat na to, když to číslo není..
Ano, vracení výstupu vstupem překvapivě opravdu narušuje principy OOP.
Místo TryGetKey bych si předtím zavolal KeyExists nebo něco podobného,
případně použil výjimku jako bílý muž.
Vidíš, jak ses do toho sám zamotal. Do větve else ti to vstoupí, když
to je číslo.
Pokud na vstupu očekávám volitelně číslo nebo textový řetězec,
např. pokud bych dělal interpretr, asi bych raději použil tokenizer. Ten mi
nebude vracet boolean, ale hotový token s rozlišením typu symbolu.
Například číslo, řetězec, klíčové slovo, operátor apod.
Aha. Takže ty do nějakého svého vzorece potřebuješ nějaké číslo, to
číslo možná vůbec číslo není, .NET ti nabízí metodu Parse, která by
ti normálně vyhodila výjimku, ale ne - ty musíš volat TryParse a pak si
stěžuješ, že není napsaná stejně jako Parse. No, tak to vážně nevim,
co ti poradit...
A teda když mi int.Parse vrátí -1 a já očekávám jen kladná čísla,
tak to znamená, že int.Parse je napsané špatně, protože může vracet
"chybový stav"? Zajímavé pojetí práce s cizím kódem, vskutku.
Přestaň Matesit a hoď odkaz na smysluplný zdroj, kde by se dalo dočíst,
že TryParse vrací chybový stav, nebo že předávání paramterů referencí
porušuje principy OOP.
Když mi int.Parse vrátí -1, tak to znamená, že jsem obdržel hodnotu -1.
Chybový stav to není, je to hodnota -1. Chybou se to stane až pokud se
pokusím tuto hodnotu použít třeba jako index prvku v poli.
Aha, takže ty dáš přednost dvojímu prohledávání kolekce před tím
zlým zlým škaredým out?
Jak vůbec skalní odpůrci out řeší takovéhle kešování?
private Dictionary<string, string> _chineseTranslations = new Dictionary<string, string>();
privatestring GetChineseTranslation(string text)
{
string translated;
if (!_chineseTranslations.TryGetValue(text, out translated))
{
translated = TranslatorServerClient.Current.TranslateToChinese(text);
_chineseTranslations.Add(text, translated);
}
return translated;
}
Opravdu bys to přepsal na try - catch, ačkoliv je jasné, že ty výjimky
se tam budou sypat každou chvíli? A co kurnik global warming - víš co
výkonu ti ty výjimky požerou? A víš co je úplně nejlepší? Že ty to TryGetValue vůbec
používat nemusíš a můžeš zůstat prasátkem - C# má to out
navíc, nesetkal jsem se s případem, kdy by to bez
něj nešlo.
Jenže pokud použiji int.Parse, nepotřebuji ani první proměnnou a
nemusím tam mít ani if. Pokud očekávám na vstupu číslo a je tam něco
jiného, je to chyba a je nutné s ní tak i zacházet.
Ale ty jsi napsal, že ti TryParse vadí, protože jeho hodnotu chceš
dosazovat do nějakého vzorce. A já ti říkám, že .NET opravdu nemůže za
to, že ty chceš, aby byl napsaný jinak.
Sakra, tys mě normálně rozsekal bravurní argumentací. Co si teď jenom
počnu? Můj svět postavený na Dictionary se zhroutil - ona se místo něj
používá databáze
Databáze je hodně široký pojem. Na takové záležitosti často
používám NoSQL databáze. Některé z nich pracují v operační paměti a
jsou rychlejší než kolekce.
No tím jsem chtěl Kitovi naznačit, že asi nemá smysl řešit, že metoda
vrací výstup vstupem. Teď už je to ale jedno. Jsem rozsekanej a jdu si
nainstalovat na Netduino SQL Server, abych se zbavil těch ošklivých
Dictionary. Nebo mi tam líp poběží Oracle? Kluci, nevíte někdo?
Proto stále čekám na nějaký odkaz, kde něco takovýho najdu, nic o
argumentech jsem nenašel (byť jsem googlil jen velmi krátce), kromě zmínky,
že se mám snažit, aby jich bylo co nejméně.
Když se nad tím zamyslíš, tak přijdeš na to, že každý běžný
slovník v databázi na disku se při běžné práci nacachuje do RAM. Na
běžném stroji (Atom 330) tak mohu přeložit cca 200 000 slov za sekundu. To
bych neviděl jako moc velkou ztrátu výkonu.
Kristepane, šlo o příklad. Snažíš se stočit
konverzaci úplně mimo původní téma, což bývá ve fórech považováno za
neslušnost.
____________________
Navíc pořád nevím, jak to udělám na tom Netduinu.
U velkého množství dat si nemůžeš dovolit pokaždé všechno natahovat
ze souboru do kolekce, protože to dlouho trvá a často se to tam ani nevejde.
Taková data prostě musíš uložit do databáze.
Najdi si nějaké dobré praktiky, určitě se ti budou hodit, náhodně jsem
něco našel třeba tady: http://excalibur.apache.org/…actices.html . Dále
návrhové vzory a metodiky vývoje softwaru.
První odkaz:
"...the correct application of out and ref parameters requires intermediate
design and coding skills. Library architects who design for a general audience
should not expect users to master working with out or ref parameters."
hodně volný překlad: "...použití out a ref vyžaduje střední znalosti
návrhu a kódění, nemáme od běžného publika očekávat zvládnutí práci
s out a ref parametry" - nic o tom, že by se to používat nemělo, jen že
není vhodný to vytahovat před začátečníkama, protože to nejspíš
nechápou.
"Methods that implement the Try<Something> pattern, such as
Int32TryParse, do not raise this violation"
asi netřeba překládat / komentovat
Druhý odkaz: "... but in any given instance ref and out are OK."
Třetí odkaz: tam jsem nic k tématu ref / out nenašel.
Při startu programu v kolekcích žádná data nejsou, proto je tam musíš
natáhnout buď ze souboru nebo z databáze. To může být u velkého objemu
zdržující a je lepší natahovat z disku jen data, která jsou v daný
okamžik potřebná.
Tohle už záleží případ od případu - někdy stačí načítat všechna
data ze souboru, někdy tahat je lepší je tahat z už běžící databáze,
ale jsme už zase koukám někde úplně mimo téma.
Chlape, poslal jsem ti dokument přímo od Microsoftu, kde píší, že se to
nemá používat. Kód, který nedoporučuje používat Microsoft, bude asi
nějakým způsobem závadný. Psát kód tak, aby byl srozumitelný jen pro
experty, je špatné. Už nevím, co víc pro tebe mohu udělat, pokud neumíš
prohrávat, tak s tím ti nepomohu
Prohrávat umím, ale nebudu to dělat, když mám pravdu.
Nebo alespoň zatím nikdo nepodal žádný argument, který by potvrzoval, že
ji nemám.
Je to doporučení jen pro public /
protected ("Cause: A public or protected method in a public
type has an out parameter.", odkaz: http://msdn.microsoft.com/…s182131.aspx)
Např. metody typu TryParse to neporušují ("Methods that implement the
Try<Something> pattern, such as Int32TryParse, do not raise this
violation.", odkaz http://msdn.microsoft.com/…s182131.aspx)
Přímo na webu MS je zmínka, že v určitých případech bys měl
zvážit jejich použití ("When you work with parameters that are large
structures, the additional resources that are required to copy these structures
could cause a performance effect when you pass by value. In these cases, you
might consider using ref or out parameters.", odkaz: http://msdn.microsoft.com/…s182146.aspx )
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.