it-swarm-eu.dev

C # - Mohou být skryté veřejně zděděné metody (např. Soukromé pro odvozené třídy)

Předpokládám, že mám BaseClass s veřejnými metodami A a B a vytvořím DerivedClass prostřednictvím dědičnosti.

např.

public DerivedClass : BaseClass {}

Nyní chci vyvinout metodu C v DerivedClass, která používá A a B. Existuje způsob, jak mohu přepsat metody A a B, aby byly soukromé v DerivedClass tak, že pouze metoda C je vystavena někomu, kdo chce použít můj DerivedClass?

54
Lyndon

Není to možné, proč?

V jazyce C # je na vás vynuceno, že pokud zdědíte veřejné metody, musíte je zveřejnit. V opačném případě očekávají, že nebudete pocházet z třídy na prvním místě.

Namísto použití vztahu is-a byste museli použít vztah has-a. 

Návrháři jazyků to neumožňují záměrně, abyste mohli dědičnost používat lépe. 

Například jeden by mohl omylem zaměnit třídu Car odvodit od třídy Engine dostat jeho funkčnost. Ale motor je funkce, kterou používá auto. Takže byste chtěli použít vztah has-a. Uživatel automobilu nechce mít přístup k rozhraní motoru. A samotné auto by nemělo zaměňovat metody motoru s jeho vlastním. Norské budoucí derivace. 

Nedovolují, aby vás chránila před špatnými hierarchiemi dědictví.

Co byste měli dělat?

Místo toho byste měli implementovat rozhraní. To vám ponechává svobodu mít funkčnost pomocí vztahu has-a.

Jiné jazyky:

V C++ jednoduše specifikujete modifikátor před základní třídou soukromých, veřejných nebo chráněných. Tím jsou všichni členové základny, kteří byli veřejně přístupní k dané úrovni přístupu. Zdá se mi hloupé, že v C # nemůžete udělat totéž.

Reštrukturalizovaný kód:

interface I
{
    void C();
}

class BaseClass
{
    public void A() { MessageBox.Show("A"); }
    public void B() { MessageBox.Show("B"); }
}

class Derived : I
{
    public void C()
    {
        b.A();
        b.B();
    }

    private BaseClass b;
}

Rozumím, že jména výše uvedených tříd jsou trochu moot :)

Další návrhy:

Jiní navrhli dělat A() a B() veřejné a házet výjimky. Ale to neznamená, že by přátelská třída mohla být používána a nemá smysl. 

68
Brian R. Bondy

Když se například pokusíte zdědit po List<object> a chcete skrýt přímého člena Add(object _ob):

// the only way to hide
[Obsolete("This is not supported in this class.", true)]
public new void Add(object _ob)
{
    throw NotImplementedException("Don't use!!");
}

Není to opravdu nejvhodnější řešení, ale dělá to. Intellisense stále přijímá, ale při kompilaci dostanete chybu:

chyba CS0619: 'TestConsole.TestClass.Add (TestConsole.TestObject)' je zastaralý: 'Toto není podporováno v této třídě.'

22
nono

To zní jako špatný nápad. Liskov nebude ohromen. 

Pokud nechcete, aby spotřebitelé DerivedClass mohli přistupovat k metodám DeriveClass.A () a DerivedClass.B () navrhl bych, aby DerivedClass implementoval nějaké veřejné rozhraní IWhateverMethodCIsObout a spotřebitelé DerivedClass by ve skutečnosti měli mluvit s IWhateverMethodCIsAbout and know nic o implementaci BaseClass nebo DerivedClass vůbec.

6
Hamish Smith

Co potřebujete je složení ne dědictví.

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

Nyní, pokud potřebujete speciální druh letadla, jako je ten, který má PairOfWings = 2, ale jinak dělá všechno, co letadlo může .. Zdědíte letadlo. Tímto prohlašujete, že vaše derivace splňuje smlouvu základní třídy a může být nahrazena bez blikání tam, kde se očekává základní třída. např. LogFlight (Plane) bude pokračovat v práci s instancí BiPlane.

Pokud však potřebujete pouze chování Fly pro nového ptáka, kterého chcete vytvořit, a nejste ochotni podpořit smlouvu o základní třídě, místo toho budete skládat. V tomto případě refactor chování metod znovu použít do nového typu Flight. Nyní vytvořte a podržte odkazy na tuto třídu jak v rovině, tak i v ptákovi. Nemůžete zdědit, protože Bird nepodporuje kompletní smlouvu o základní třídě ... (např. Nemůže poskytnout GetPilot ()). 

Ze stejného důvodu, nemůžete omezit viditelnost metod základní třídy, když přepíšete .. můžete přepsat a vytvořit základní privátní metodu veřejnou v derivaci, ale ne naopak. např. V tomto příkladu, pokud odvodím typ Plane "BadPlane" a pak přepíše a "Skrýt" GetPilot () - učinit ho soukromým; metoda klienta LogFlight (Plane p) bude fungovat pro většinu letadel, ale vyhodí do povětří "BadPlane", pokud se stane, že implementace LogFlight potřebuje/zavolá GetPilot (). Vzhledem k tomu, že se očekává, že všechny derivace základní třídy budou 'substituovatelné' tam, kde se očekává základní parametr param, musí to být zakázáno.

5
Gishu

Jediný způsob, jak toho dosáhnout, je použít vztah Has-A a implementovat pouze funkce, které chcete vystavit.

3
Nescio

@Brian R. Bondy mě upozornil na zajímavý článek o Skrytí skrze dědictví a klíčové slovo new.

http://msdn.Microsoft.com/en-us/library/aa691135(VS.71).aspx

Jako řešení bych navrhl:

class BaseClass
{
    public void A()
    {
        Console.WriteLine("BaseClass.A");
    }

    public void B()
    {
        Console.WriteLine("BaseClass.B");
    }
}

class DerivedClass : BaseClass
{
    new public void A()
    {
        throw new NotSupportedException();
    }

    new public void B()
    {
        throw new NotSupportedException();
    }

    public void C()
    {
        base.A();
        base.B();
    }
}

Tímto způsobem kód jako tento způsobí NotSupportedException:

    DerivedClass d = new DerivedClass();
    d.A();
3
Jorge Ferreira

Řekl bych, že pokud máte codebase, kterou chcete udělat, není to nejlépe navržený kód základny. Je to typicky znamení třídy v jedné úrovni heirarchy, která potřebuje určitý veřejný podpis, zatímco jiná třída odvozená z této třídy ji nepotřebuje. 

Nadcházející kódovací paradigma se nazývá "Kompozice nad dědičností". To přímo vychází z principů objektově orientovaného vývoje (zejména principu Single Responsibility a Open/Closed Principle). 

Bohužel, jak se mnozí z nás vývojáři učili objektovou orientaci, vytvořili jsme si zvyk okamžitě přemýšlet o dědictví místo kompozice. Máme tendenci mít větší třídy, které mají mnoho různých odpovědností jednoduše proto, že by mohly být obsaženy ve stejném objektu „Skutečného světa“. To může vést k třídním hierarchiím, které jsou hluboko 5+.

Nešťastným vedlejším efektem, který vývojáři normálně nemyslí, když se zabývají dědictvím, je to, že dědictví tvoří jednu z nejsilnějších forem závislostí, které můžete kdykoliv vložit do svého kódu. Vaše odvozená třída je nyní silně závislá na třídě, z níž byla odvozena. To může z dlouhodobého hlediska učinit váš kód křehkým a vést k matoucím problémům, kdy změna určitého chování v základní třídě přeruší odvozené třídy v obskurních cestách.

Jeden způsob, jak zlomit váš kód, je přes rozhraní, jak je uvedeno v jiné odpovědi. Je to chytrá věc, kterou byste měli dělat, protože chcete, aby se externí závislosti třídy vázaly na abstrakce, nikoli na konkrétní/odvozené typy. To vám umožní změnit implementaci bez změny rozhraní, to vše bez ovlivnění řádku kódu ve vaší závislé třídě. 

Já bych raději než udržoval systém se stovkami/tisíci/ještě více třídami, které jsou všechny malé a volně spojené, než se zabývat systémem, který používá těžké polymorfismy/dědictví a má méně tříd, které jsou pevněji spojeny. 

Snad nejlepší zdrojů tam na objektově orientovaného vývoje je Robert C. Martin kniha, Agile Software Development, Zásady, vzory a postupy

1
Jason Olson

Skrytí je dost kluzký svah. Hlavní otázky, IMO, jsou:

  • Je to závislé na typu deklarace V instanci, Znamená, že když děláte něco jako BaseClass obj = new SubClass (), pak Call obj .A (), skrytí je poraženo. Bude provedena BaseClass.A ().

  • Skrytí může velmi snadno zakrýt Chování (nebo změny chování) v Základním typu. To je zjevně [….] Méně znepokojující, když vlastníte obě strany rovnice, nebo pokud je volání 'base.xxx' součástí vašeho člena.

  • Pokud vlastně děláte vlastníte obě strany základní/podtřídní rovnice, pak byste měli být schopni navrhnout lépe zvládnutelné řešení než institucionalizované skrytí/stínování.
1
Jared

Zatímco odpověď na otázku je "ne", je zde jeden tip, který bych chtěl upozornit na ostatní přijíždějící sem (vzhledem k tomu, že OP byl jaksi odkazem na přístup shromáždění třetími stranami). Když jiní odkazují na shromáždění, Visual Studio by mělo být ctít následující atribut, takže to nebude zobrazovat v intellisense (skrytý, ale STILL být volán, tak pozor):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

Pokud jste neměli jinou možnost, měli byste být schopni použít new na metodě, která skryje metodu základního typu, vrátí => throw new NotSupportedException(); a zkombinuje ji s atributem uvedeným výše.

Další trik závisí na tom, zda NENÍ dědí ze základní třídy, pokud je to možné, kde má základna odpovídající rozhraní (například IList<T> pro List<T>). Implementace rozhraní "explicitně" také skryje tyto metody před inteligencí na typu třídy. Například:

public class GoodForNothing: IDisposable
{
    void IDisposable.Dispose() { ... }
}

V případě var obj = new GoodForNothing() nebude metoda Dispose() k dispozici v obj. Bude však k dispozici každému, kdo explicitně zadá obj do IDisposable.

Kromě toho můžete místo toho, abyste z něj zdědili, zabalit základní typ a poté některé metody skrýt:

public class MyList<T> : IList<T>
{
    List<T> _Items = new List<T>();
    public T this[int index] => _Items[index];
    public int Count => _Items.Count;
    public void Add(T item) => _Items.Add(item);
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden)
    /*...etc...*/
}
0
James Wilkins

Pokud jsou v původní třídě definovány jako veřejné, nemůžete je přepsat jako soukromé ve své odvozené třídě. Mohli byste však učinit veřejnou metodu výjimku a implementovat vlastní soukromou funkci.

Edit: Jorge Ferreira je správný.

0
Cody Brocious