W c# słowo kluczowe ‘new’ nie służy wyłącznie do alokacji zasobów. W klasach istnieje koncepcja wirtualnych metod, które deklaruje się za pomocą słowa virtual. Są to podstawy polimorfizmu więc w tym poście nie będę opisywał już słówka virtual a wyłącznie new, które używa się również w połączeniu z metodą.
Metoda oznaczona new po prostu przykrywa metodę bazową. Najlepiej wyjaśnić to na przykładzie 2 klas:
class Cat:Animal { public void Print() { Console.WriteLine("Cat"); } } class Animal { public void Print() { Console.WriteLine("Animal"); } }
Nie mamy słówka virtual ani override. Po prostu zadeklarowaliśmy dwie takie same metody. Kod się skompiluje jednak z pewnym warning: “‘ConsoleApplication2.Cat.Print()’ hides inherited member ‘ConsoleApplication2.Animal.Print()’. Use the new keyword if hiding was intended”. Aby uniknąć tego należy oznaczyć metodę słówkiem new:
class Cat:Animal { public new void Print() { Console.WriteLine("Cat"); } } class Animal { public void Print() { Console.WriteLine("Animal"); } }
Jeszcze raz zaznaczam, ze oba fragmenty kodu się skompilują i będą działały identycznie. Słówko new jednak jest zalecane aby jawnie powiedzieć, że metoda przysłania inną metodę.
Co to znaczy jednak przysłonięcie? Przysłonięcie to przeciwieństwo polimorfizmu. Teraz typ zmiennej decyduje, która implementacja Print zostanie wykonana a nie na co wskaźnik wskazuje. Przykład:
Animal animal = new Cat(); animal.Print();
Gdybyśmy wykorzystali polimorfizm, Print wyświetliłby tekst “Cat’”. W tym jednak przypadku, ujrzymy “Animal” bo takiego typu jest zmienna.
Trzeba przyznać, że jest to dziwne i nieeleganckie. Dlaczego Microsoft zdecydował się na wprowadzenie tego w c#? Starałem się znaleźć jakieś praktyczne przykłady. Osobiście nigdy nie musiałem z tego skorzystać ale faktycznie w .NET Framework są przykłady użycia “new”. Zacznijmy od najbardziej popularnego:
interface IEnumerable<T> : IEnumerable { new IEnumerator<T> GetEnumerator(); }
Foreach używa GetEnumerator do zwrócenia interfejsu IEnumerator. Problem w tym, że kiedyś istniała wyłącznie metoda zwracającą typ object a w .NET 2.0 wprowadzano generics. W generycznym interfejsie chcemy zatem przykryć starą implementację – w interfejsie zadeklarujemy metodę o identycznej nazwie i tych samych parametrach.
Drugim przykładem jest klasa SQLConnection która dziedziczy po DBConnection. DBConnection ma takie metody jak np.:DbCommand CreateCommand(). Z kolei SQLConnection ma taką samą metodę z tym, że zwraca ona inny typ (SQLCommand). Rozważmy podobny, uproszczony przypadek:
public class DbConnection { public DbCommand Command{get;} } public SqlConnection { new public SqlCommand Command{get{return (SqlCommand)Command;}} }
Dla instancji SqlConnection warto stworzyć hiden method, która zwraca już obiekt po rzutowaniu. Dzięki temu, mamy specyficzne dla SqlCommand metody i nie musimy np.:
SqlConnection connection = new SqlConnection(); SqlCommand cmd = (SqlCommand)connection.Command; cmd.SqlSeverMethod();
Z metodą przysłaniającą wystarczy:
SqlConnection connection = new SqlConnection(); connection.Command.SqlServerMethod();
Konwersja (a konkretnie kowariancja) jest najpopularniejszym scenariuszem użycia.
Na jednym z forum znalazłem jednak jeszcze inny przykład, wynikający z błędnej architektury kodu niezależnego od nas. Wyobraźmy sobie, że klasa Employee zawiera pole Salary i przekazujemy instancję tej klasy do kontrolki PropertyGrid. Nie chcemy jednak wyświetlać tej właściwości w kontrolce. Wtedy z jednym rozwiązań jest przysłonięcie tej właściwości i dodanie atrybutu:
public class Manager : Employee { [Browsable(false)] public new int Salary { get { return base.Salary; } set { base.Salary = value; } } }
Jeśli ktoś zna inne przykłady, zachęcam do podzielenia się tą wiedzą w komentarzach.
W pierwszym zdaniu zabrakło chyba “new”:)
W c# słowo kluczowe —>new<— nie służy wyłącznie
Poprawione. Dzieki wielkie!
Wielkie dzięki za praktyczne przykłady użycia. Bardzo często na rozmowach pytają o różnice new/override, a jeszcze nigdy nie trafiłem na prawidłowe użycie “new”.
Chciałem skomentować, ale w końcu wyszedł mi z tego wpis: http://paskol.robi.to/?p=147.