Jakie są praktyczne zastosowania „nowego” modyfikatora w języku C # w odniesieniu do ukrywania?

21

Współpracownik i ja przyglądaliśmy się zachowaniu newsłowa kluczowego w języku C #, ponieważ odnosi się ono do koncepcji ukrywania. Z dokumentacji :

Użyj nowego modyfikatora, aby jawnie ukryć element członkowski odziedziczony z klasy podstawowej. Aby ukryć dziedziczony element członkowski, zadeklaruj go w klasie pochodnej, używając tej samej nazwy, i zmodyfikuj go za pomocą nowego modyfikatora.

Przeczytaliśmy dokumentację i rozumiemy, co ona właściwie robi i jak to robi. Tym, na co tak naprawdę nie mogliśmy sobie poradzić, jest powód, dla którego musisz to zrobić. Modyfikator istnieje od 2003 roku i oboje pracujemy z .Net dłużej niż to i nigdy się nie pojawi.

Kiedy takie zachowanie byłoby konieczne w sensie praktycznym (np. W odniesieniu do uzasadnienia biznesowego)? Czy jest to funkcja, która przeżyła swoją przydatność, czy też to, co robi po prostu dość rzadko w tym, co robimy (w szczególności robimy formularze internetowe i aplikacje MVC oraz niektóre małe czynniki WinForms i WPF)? Próbując tego słowa kluczowego i bawiąc się nim, znaleźliśmy pewne zachowania, które pozwalają na to, że wydają się trochę niebezpieczne, jeśli zostaną niewłaściwie użyte.

Wydaje się to nieco otwarte, ale szukamy konkretnego przypadku użycia, który można zastosować w aplikacji biznesowej, która uzna to narzędzie za przydatne.

Joel Etherton
źródło
9
Być może też go przeczytałeś, ale jest interesujący artykuł Erica Lipperta (programisty kompilatora C #) na temat tego, dlaczego ukrywanie metod zostało dodane do C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/... . Odpowiada to na część twojego pytania, ale nie mam dla ciebie przygotowanej walizki biznesowej, więc umieściłem to w komentarzu.
Jalayn
3
@Jayayn: Przypadek biznesowy jest omawiany przez Erica w tym poście: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

Odpowiedzi:

22

Możesz go użyć do naśladowania kowariancji typu zwrotu. Wyjaśnienie Erica Lipperta . Eric podaje następujący przykładowy kod:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

To jest obejście. public override Fish Contents() { ... }nie jest legalne, mimo że jest bezpieczne.

Ogólnie rzecz biorąc, należy nie używać metody ukrycia, jak to jest mylące dla konsumentów klasy (przykład specyficzna powyżej nie cierpi z tego problemu). Po prostu nazwij swoją nową metodę inną nazwą, jeśli nie chcesz zastąpić istniejącej metody.

Prawdopodobną sytuacją w świecie, w której trzeba ukryć metodę, jest to, że dostawca klasy podstawowej dodał metodę ogólną, którą już dodałeś do klasy pochodnej. Taki program skompiluje się (i wyświetli ostrzeżenia) bez nowego słowa kluczowego, ale dodanie newmówi: „Wiem, że moja wersja tej metody zastępuje wersję klasy podstawowej. To jest okropne i mylące, ale utknęliśmy z tym”. To wciąż lepsze niż zmuszanie klasy pochodnej do zmiany nazwy metody.

Dopuszczenie, aby metoda pochodna była traktowana jako przesłonięcie, spowodowałoby problemy. Ignorując wszelkie obawy związane z implementacją kompilatora, nowa metoda różni się semantycznie od metody podstawowej, ale polimorfizm spowodowałby wywołanie nowej metody, gdy zostanie poproszony o wywołanie metody o tej samej nazwie.

Sytuację tę szczegółowo omawia w tym poście Eric Lippert.

Brian
źródło
1
+1 za newbycie markerem „to jest okropne i mylące”.
Avner Shahar-Kashtan
Myślę, że przykład Erica jest bardzo pomocny, choćby dlatego, że jestem w podobnej sytuacji ... Mam klasę podstawową implementującą nietrywialną metodę, która zwraca TBase, oraz klasę pochodną, ​​która powinna zwrócić TDerived. W tym przypadku wydaje się, że jest złem koniecznym.
Kyle Baran
4

Myślę, że jest tam na wypadek, gdybyś potrzebował go do zrobienia czegoś, o czym nie pomyśleli projektanci języka. C # było pod wieloma względami reakcją na wczesne wersje java. Jedną z rzeczy, które zrobiła java, było bardzo wyraźne zaszufladkowanie programistów, aby wyeliminować możliwość, że programiści strzelą sobie w nogi. C # przyjął nieco inne podejście i dał programistom nieco więcej mocy, pozwalając programistom na kilka dodatkowych okazji do strzelania sobie w stopy. Jednym z przykładów jest unsafesłowo kluczowe. To newsłowo kluczowe jest inne.

Teraz prawdopodobnie nie jest tak przydatny, unsafeale kiedy wejdziesz w specyfikację języka, trudno jest wydostać się ze specyfikacji języka.

Wyatt Barnett
źródło
5
Java nie ma nowego, ponieważ Java traktuje wszystkie metody jako wirtualne. Według Erica Lipperta motywacją do wspierania nowego jest rozwiązanie kruchego problemu z klasą podstawową. Istnienie nowego jest niezbędne do rzeczywistego użytku biznesowego, a nie tylko do korzystania z bibliotek niskiego poziomu. Java nie posiadająca nowych (i nie posiadających nie-wirtualnych metod) oznacza, że ​​jeśli klasa podstawowa wprowadzi nową metodę, która jest już używana w klasach pochodnych, istniejący kod może się zepsuć, pomimo faktu, że należy zezwolić twórcy klasy podstawowej być nieświadomym kodu, który go zużywa.
Brian
3

Mówi czytelnikowi, że „celowo ukryłem implementację tej metody przez klasę podstawową”, a nie przypadkowo.

Vegio
źródło
5
Uderza mnie to jako żebranie pytania. PO wie, co robi, ale chce wiedzieć, dlaczego.
Brian
0

Możesz chcieć, aby poprzedni członek był dostępny pod inną nazwą:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

Twoje zdrowie.

umlcat
źródło
Wiem, jak to działa, ale moje pytanie naprawdę brzmi: dlaczego chcesz, aby poprzedni członek był dostępny pod inną nazwą? Łamie to wszelkiego rodzaju umowy, czyni niektóre elementy ogólne bezużytecznymi.
Joel Etherton
@Jel Etherton: Jak zapewne wiesz, czasami programiści muszą „wybierać” inne osoby programujące kod. I możemy nie być upoważnieni do modyfikowania, może rozszerzania klas. I może wymagać użycia zarówno poprzedniego, jak i nowego członka.
umlcat,
0

Uznałem, że jest to bardzo przydatne podczas tworzenia zestawu testów dla starszego kodu. Pozwala mi ukryć zależności zewnętrzne, takie jak zapytania do bazy danych wewnątrz metody używanej przez inne metody. Aby móc przetestować inne metody, mogę ukryć oryginalną logikę zależną od zasobów zewnętrznych, bez potrzeby dodawania wirtualnego słowa kluczowego do tych metod w klasach testowych.

Spacy
źródło