it-swarm-eu.dev

Kdy použít abstraktní třídy místo rozhraní s metodami rozšíření v C #?

„Abstraktní třída“ a „rozhraní“ jsou podobné pojmy, přičemž rozhraní je více abstraktem těchto dvou. Jedním rozlišovacím faktorem je, že abstraktní třídy poskytují implementace metod pro odvozené třídy, pokud je to potřeba. V C # však byl tento diferenciační faktor snížen nedávným zavedením metod rozšíření, které umožňují implementaci metod rozhraní. Dalším rozlišovacím faktorem je to, že třída může zdědit pouze jednu abstraktní třídu (tj. Neexistuje žádná násobná dědičnost), ale může implementovat více rozhraní. Díky tomu jsou rozhraní méně restriktivní a pružnější. Takže v C #, kdy bychom měli používat abstraktní třídy místo rozhraní s metodami rozšíření?

Pozoruhodný příklad modelu metody rozhraní + rozšíření je LINQ, kde je zajištěna funkčnost dotazu pro jakýkoli typ, který implementuje IEnumerable prostřednictvím velkého množství metod rozšíření.

70
Gulshan

Když potřebujete implementovat část třídy. Nejlepší příklad, který jsem použil, je vzorová metoda šablony .

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

Můžete tedy definovat kroky, které budou provedeny při vyvolání Do (), aniž byste znali specifika toho, jak budou implementovány. Odvozující třídy musí implementovat abstraktní metody, ale nikoli metodu Do ().

Metody rozšíření nutně nesplňují část „musí být součástí třídy“ rovnice. Kromě toho, iirc, metody rozšíření nemohou (zdá se) být ničím jiným než veřejným.

upravit

Otázka je zajímavější, než jsem původně udělil zásluhu. Po dalším zkoumání Jon Skeet odpověděl na takovou otázku na SO ve prospěch používání rozhraní + metody rozšíření. Také, potenciální nevýhoda používá odraz proti objektové hierarchii navržené tímto způsobem.

Osobně mám potíže s viděním výhody změny v současné běžné praxi, ale také v tom vidím málo až žádné nevýhody.

Je třeba poznamenat, že tímto způsobem je možné programovat v mnoha jazycích prostřednictvím Utility tříd. Rozšíření jen poskytují syntaktický cukr, aby metody vypadaly, že patří do třídy.

63
Steven Evers

Je to všechno o , co chcete modelovat:

Abstraktní třídy pracují dědičnost. Protože jsou jen speciální základní třídy, modelují nějaký vztah je-a - vztah.

Například pes je zvíře, takže máme

class Dog : Animal { ... }

Protože nemá smysl vlastně vytvářet generické zvíře (jak by to vypadalo?), Udělali jsme tuto Animal základní třídu abstract - ale stále je to základní třída. A třída Dog nedává smysl, aniž by byla Animal.

Rozhraní na druhé straně jsou jiný příběh. Nepoužívají dědičnost, ale poskytují polymorfismus (který lze implementovat také s dědičností). Nemodelují vztah je-a , ale více podporuje .

Vezměte např. IComparable - objekt, který podporuje porovnání s jiným .

Funkčnost třídy nezávisí na rozhraních, které implementuje, rozhraní poskytuje pouze obecný způsob přístupu k funkcím. Stále bychom mohli Dispose z Graphics nebo FileStream, aniž bychom museli implementovat IDisposable. Rozhraní pouze spojuje metody.

V zásadě by bylo možné přidávat a odebírat rozhraní stejně jako rozšíření, aniž by se měnilo chování třídy, jen obohacuje přístup k ní. Nemůžete však odnést základní třídu, protože objekt by se stal nesmyslným!

25
Dario

Právě jsem si uvědomil, že abstraktní roky nepoužívám abstraktní třídy (alespoň nevytvářené).

Abstraktní třída je poněkud podivné zvíře mezi rozhraním a implementací. V abstraktních třídách je něco, co brní můj pavouk. Řekl bych, že by člověk neměl „vystavovat“ implementaci za rozhraním žádným způsobem (nebo si dělat nějaké vazby).

I když existuje potřeba společného chování mezi třídami implementujícími rozhraní, použil bych spíše samostatnou pomocnou třídu než abstraktní třídu.

Chtěl bych jít na rozhraní.

3
Maglob

IMHO, myslím, že zde existuje smíšené pojetí.

Třídy používají určitý druh dědičnosti, zatímco rozhraní používají jiný druh dědičnosti.

Oba druhy dědictví jsou důležité a užitečné a každý z nich má své klady a zápory.

Začněme od začátku.

Příběh s rozhraními začíná dlouhou dobu zpět s C++. V polovině osmdesátých let, kdy byl vyvinut C++, nebyla myšlenka rozhraní jako jiného typu dosud realizována (přijde včas).

C++ měl pouze jeden druh dědičnosti, druh dědičnosti používaný třídami, nazývaný implementační dědičnost.

Aby bylo možné aplikovat doporučení GoF Book: „Chcete-li programovat pro rozhraní, a ne pro implementaci“, C++ podporovala abstraktní třídy a vícenásobnou implementační dědičnost, aby bylo možné dělat, jaká rozhraní v C # jsou schopná (a užitečná!) Dělat .

C # zavádí nový druh typu, rozhraní a nový druh dědičnosti, dědičnost rozhraní, která nabízí alespoň dva různé způsoby podpory „Programování rozhraní, nikoli implementace“, s jednou důležitou výzvou: Pouze C # podporuje vícenásobnou dědičnost s dědičností rozhraní (a ne s dědičností implementace).

Architektonickými důvody za rozhraními v C # je doporučení GoF.

Doufám, že to může pomoci.

S pozdravem, Gastone

Použití metod rozšíření na rozhraní je užitečné pro použití běžného chování napříč třídami, které mohou sdílet pouze společné rozhraní. Tyto třídy již mohou existovat a mohou být uzavřeny pro rozšíření jinými prostředky.

U nových návrhů bych upřednostňoval použití metod rozšíření na rozhraní. Pro hierarchii typů poskytují metody rozšíření prostředky pro rozšíření chování bez nutnosti otevírat celou hierarchii pro úpravy.

Metody rozšíření však nemohou získat přístup k soukromé implementaci, takže v horní části hierarchie, pokud potřebuji zapouzdřit soukromou implementaci za veřejné rozhraní, pak by jediná cesta byla abstraktní třída.

1
Ed James

Myslím, že v C # není podporována vícenásobná dědičnost, a proto je v C # představen koncept rozhraní.

V případě abstraktních tříd není podporována ani vícenásobná dědičnost. Je tedy snadné se rozhodnout, kdo bude používat abstraktní třídy nebo rozhraní.

0
Beginner