Používání properties v C# vs. PHP rubrika: Folklór

12 Kit
položil/-a 24.1.2015

V C# je běžné používat tuto konstrukci:

class Registr {
    public string Alfa {get; set;}
    public int Beta {get; set;}
}
 
// použití:
registr.Alfa = "string";
Console.WriteLine(registr.Alfa);

Když udělám podobnou konstrukci v PHP:

class Registr {
    private $alfa;
    private $beta;
 
    function __get($key) {
        switch ($key) {
            case 'alfa': return $this->alfa;
            case 'beta': return $this->beta;
        }
        throw new Exception('Chyba');
    }
 
    function __set($key, $value) {
        switch ($key) {
            case 'alfa': $this->alfa = $value; return;
            case 'beta': $this->beta = $value; return;
        }
        throw new Exception('Chyba');
    }
}
 
// použití:
$registr->alfa = "string";
echo $registr->alfa;

tak mám najednou pocit, jako by to bylo špatně. Přitom sémanticky je to totéž. Jak se k tomu stavíte?

Komentáře

  • Vašek Ch. : IDE ti tímhle způsobem nebude schopné properties napovídat. 24.1.2015
  • Kit : Vim s tím problémy nemá. 24.1.2015
  • nixbody : @Kit: Myslím, že v tom setteru máte chybu, na konci case chybí break, takhle Vám nastavení atributu alfa nastaví tu samou hodnotu i atributu beta. 24.1.2015
  • nixbody : @Vašek Chromický: Pokud to IDE nezvládne napovídat, tak se dá použít anotace @property, které většina IDE rozumí. 24.1.2015
  • Kit : @Nobody: Díky, opraveno. Psal jsem to z hlavy. 24.1.2015
  • nixbody : @Kit: Ještě bych dal tu výjimku do default, takhle Vám ji to vyhodí vždy :) 24.1.2015
  • error414_1 : no ono se bezne pouziva "case 'alfa': $this->alfa = $value; return $this" takze ta vyjimka by byla dobre, ale to neni predmet dotazu. asi bych tu uvital poslani PM, ono tohle opravovani nepatri do diskuze 24.1.2015
  • nixbody : @error414_1: Omlouvám se, příště to tak udělám. Vracet $this v magickém setteru mi přijde poněkud zbytečné. 24.1.2015
  • Kit : Tak snad už OK. Je asi vidět, že gettery a settery téměř nepoužívám. Dotaz byl míněn trochu jinak. 24.1.2015
  • error414_1 : Nobody: mas pravdu spletl sem si to s __call 24.1.2015
odkaz
8 v6ak
odpověděl/-a 24.1.2015

Asi hlavní problém get a set jsou interfaces. Pokud budeš chtít z třídy extrahovat interface, tak více nebo méně narazíš. (Nejsem si jist podporou @property u interfaces v různých IDE, ale tak jako tak tam bude něco smrdět.) Možná by se ale dala většina problémů vyřešit použitím traitu ve stylu Nette\Object a nějakým lintem.

Druhý problém může být s viditelností. Na druhou stranu, private není až takový problém (mám přesně vymezeno, kdo to může používat, a v případě potřebu mohu podchytit všechny výskyty, kterých by u dobře navržené třídy nemělo obvykle být až tolik.) a protected používám spíše výjimečně.

Třetí problém je jistá dynamická magie odvozená od viditelnosti, ale tomu se dá do jisté míry předcházet (byť to může být otrava hlídat ručně). $this->alfa určitě odkazuje na field. V kódu mimo třídu bude $x->alfa určitě ukazovat na getter. Kam bude v kódu třídy odkazovat $x->alfa? Jsme v dynamickém jazyce, takže asi na getter, ale kdo si je s tím jistý? Kdo si je jistý, že na takové problémy nenarazí při čtení kódu? A problém není až tak nejistota (dá se vyřešit googlením nebo experimentem), mnohem větší problém je falešná jistota – víra, že moje interpretace je jasná a jediná možná, zatímco správná interpretace bude jiná.

Komentáře

  • Kit : Šlo mi o to, že v C# jsou ty problémy stejné, přesto je to propagovaná a používaná konstrukce. 24.1.2015
  • Kit : "extrahovat interface" - to už jsem dnes někde četl. Co to má být? 24.1.2015
  • v6ak : Nejsou. S interfaces problémy nejsou, properties jsou zde podporované. Viditelnost – IMHO taky nebude problém. Dynamická magie – opravdu dovoluje C# pojmenovat field a property stejně? 24.1.2015
  • v6ak : Extrahovat interface – mám třídu, třeba GitRepository, ale vidím, že by bylo užitečné mít i jinou implementaci, třeba DatabaseRepository. Jenže GitRepository neimplementuje žádné rozhraní, takže prvně udělám rozhraní Repository, které bude mít dvě implementace – GitRepository a DatabaseRepository. 24.1.2015
  • Kit : Aha, takže "grep function > interface.php" plus drobná editace. Jak prosté. To bych si mohl udělat jako plugin do Vimu, třeba se mi to bude někdy hodit. 24.1.2015
  • v6ak : No, zhruba. Extrakce rozhraní je obecně lidská práce – zejména nemusím chtít extrahovat všechny metody, někdy se bude hodit nějaké zobecnění některých metod. Běžná IDE často umějí asistovat (např. něco jako „use generalized supertype where possible“). (A navíc v případě GitRepository je dost možné, že jsem ho původně pojmenoval pouze Repository a teď to budu chtít změnit.) I tak mi – zejména u statických jazyků – nepřijde, že zrovna toto by mělo kdovíjaký přínos oproti ruční práci. Psát si plugin do Vimu (který bude umět malou část zmíněného nebo jehož napsání bude trvat dlooouho) se IMHO nevyplatí. 24.1.2015
  • Kit : Ve Vimu je to práce na 2 minuty. Až se dostanu k PC, tak si to udělám společně s refaktoringem na extrakci metod a extrakci výrazů. Na Rootovi mi dnes vyčítali, že to Vim neumí :-) 24.1.2015
  • v6ak : Práce na 2 minuty bude možná nějaká základní verze, která asi nebude umět toto: https://www.youtube.com/watch?v=It83Vl_lEuk 24.1.2015
  • Kit : Nejde mi to přehrát, asi to bude chtít rychlejší spojení. Mám raději primitivní nástroje na 2 řádky než komplexní molochy. V tomto případě se to dá napsat v PHP s využitím jeho parseru a do Vimu jen namapovat na příkaz. 24.1.2015
  • arron : Ne vždycky extrahuješ interface tupě jenom tak, že vezmeš public metody a uděláš z nich interface. To je, jako kdyby jsi extrahoval funkce z jedné velké a udělal to podle toho, kde máš volný řádek :-) Zpravidla probíhá i nějaký refactoring ať už na úrovni pojmenování metod nebo jiného (chytřejšího) rozdělení odpovědností na jednotlivé metody. Původní třída totiž nemusí být nutně napsaná 100% čistě, pokud člověk dopředu netušil, že bude dělat víc implementací. Ale uřčitě se najde i pár případů, kdy to půjde fakt mechanicky, to zase jo, otázka je, kolik takových případů bude. 24.1.2015
  • Kit : Však ten výstup nemusí být přesný. Je to jen pomůcka, jejíž výstup se dá editovat. Mám už utilitu, která mi z interface generuje stuby, tak to můžu zkusit udělat i naopak. 24.1.2015
  • madpaulie : @v6ak: "opravdu dovoluje C# pojmenovat field a property stejně?" - asi to byla řečnická otázka, ale ano i ne, je to syntactic sugar; kompilátor vygeneruje do IL property s random názvem.. 30.1.2015
  • v6ak : Nejsem .NETař, takže jsem se zeptal. Kit psal, že problémy jsou stejné, a nechtělo se mi věřit, že by toto C# dovolovalo. Že něco takového jde na úrovní bytecode mě nepřekvapuje (znám JVM bytecode a tam jsou i trošku větší zvláštnosti), šlo mi o to, jestli se v C# může programátor takto omylem „střelit do nohy“. 30.1.2015
  • Honza Břešťan : @madpaulie: Jenom pro upresneni, neni to random nazev. Pokud mam property "MyProperty", ten backing field bude vzdycky "<MyProperty>k__BackingField". Je to validni nazev memberu v IL, ale ne v C#, takze nikdy* nehrozi kolize backing fieldu autogenerovane property s jinym memberem v te tride. *) s vyjimkou kodu, ktery v C# primo generuje IL napr. pres tridy v System.Reflection.Emit, tam omezeni C# neplati. 30.1.2015
  • Honza Břešťan : @v6ak: Nejde mit v jedne tride field a property stejneho jmena (bez ohledu na nazev toho generovaneho backing fieldu), takze kolize/ambiguita v PHP popsana v odpovedi nehrozi v C#. 30.1.2015

Pro zobrazení všech 10 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.