Prywatna klasa?

Prywatna klasa?

Dzisiejszy post będzie trochę inny niż większość. Temat, który poruszę nie jest może jakoś mocno praktyczny i nie wykorzystasz go każdego dnia. Ale z drugiej strony może posłużyć jako ciekawy pomysł na pytanie rekrutacyjne, dlatego warto się nim zainteresować 🙂

Na początku zastanówmy się, czy klasa faktycznie może być prywatna. Chwila zastanowienia i prawdopodobnie myślisz sobie, że chyba nie. Po co w ogóle coś takiego byłoby potrzebne? Odpalasz Visual Studio i próbujesz skompilować coś takiego:

private class

Od razu widzisz, że coś jest nie tak. Visual Studio podkreśla słowo kluczowe private oraz samą nazwę klasy. Próba komplikacja powoduje błąd:

private class errorMożna by wysnuć wniosek, że klasa nie może być prywatna, skoro Visual Studio nie chce skompilować takiego kodu. Ale jak wczytamy się bardziej w komunikat błędu, to zauważymy, że nie ma tam nic na temat tego, że klasa nie może być prywatna. Jest jedynie informacja, że element zdefiniowany w namespace nie może być między innymi prywatny. Skoro tak, to może jesteśmy w stanie w jakiś inny sposób utworzyć klasę, która nie będzie bezpośrednio w namespace? Po chwili czasu zapewne przypominasz sobie, że mamy w C# coś takiego jak klasa zagnieżdżona.

Klasa zagnieżdżona

Klasa zagnieżdżona, to taka klasa, która jest zdefiniowana w jakimś innym elemencie (np. innej klasie). Czyli możemy utworzyć jedną klasę i do jej środka dodać drugą. Coś na kształt tego:

Jak widać klasa OuterClass zawiera tylko jeden element, który również jest klasą (InnerClass). W tym przypadku klasa zagnieżdżona może już być prywatna, bo nie znajduje się bezpośrednio w namespace.

A co oznacza w tym przypadku, że klasa jest prywatna? Klasę tą możemy używać tylko wewnątrz klasy zewnętrznej. Podobnie jak innych elementów prywatnych klasy, takich jak pola, właściwości, metody.

Ok, ale czy to ma jakiś praktyczny sens?

Darmowy kurs Visual Studio

Pracując z setkami programistów zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio, aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 20 lekcji email, w których pokaże Ci w jaki sposób pracować efektywnej i szybciej w Visual Studio. Poznasz dodatkich, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Praktycznie użycie prywatnej klasy

Klas zagnieżdżonych używa się głównie w sytuacji, gdy dana klasa nie ma sensu istnieje bez głównej klasy, w której się znajduje. Prawda jest taka, że tych klas używa się bardzo rzadko (użyłeś/użyłaś jej kiedyś?). W moim przypadku używam ich głównie w jednym scenariuszu. A są nimi testy jednostkowe.

Czasami zdarza się, że aby coś przetestować musimy utworzyć nową klasę, która dziedziczy po już istniejącej. I tak było w jednym z ostatnich przypadków, w którym używałem prywatnej klasy zagnieżdżonej.

W systemie, który rozwijam na co dzień, używamy Identity Servera do uwierzytelniania użytkowników. W tokenie, który jest przekazywany między mikroserwisami znajdują się podstawowe informacje o użytkowniku. Między innymi znajduje się Id użytkownika. Dla łatwiejszego wyciągania tych danych stworzyliśmy prosty extension method, który to ułatwia. Wygląda on tak:

Nie ma tutaj nic skomplikowanego. Rozszerzamy ApiController, do którego dodajemy metodę GetUserClaim w dwóch wersjach.

W tym momencie pojawia się pytanie: jak przetestować te dwie metody? Jak sprawdzimy definicję klasy ApiController to okaże się, że klasa ta jest klasą abstrakcyjną. Czyli nie możemy utworzyć jej instancji. Dlatego potrzebujemy jakieś innej klasy.

Z jednej strony możemy użyć jakiegoś już istniejącego kontrolera w aplikacji. Ale nie jest to najlepsze wyjście. Po pierwsze: jak zdecydować, który kontroler będzie do tego najlepszy? A po drugie: co się stanie, gdy z jakiegoś powodu kiedyś usuniemy ten kontroler?

Dlatego najlepszym rozwiązaniem jest utworzenie dedykowanego kontrolera dla testów. I tutaj właśnie przydaje się klasa prywatna zagnieżdżona. Możemy taki testowy kontroler utworzyć w klasie z testami. Ustawić go jako prywatny, dzięki czemu poza klasą z testami nie będzie widoczny.

Najlepiej widać to na przykładzie:

TestController jest prywatny i znajduje się na końcu klasy z testami. Wszystkie testy wykorzystują go poprzez metodę Create.

Co fajne, takie rozwiązanie spowoduje, że nikt poza klasą z testami nie użyje tego kontrolera, dzięki czemu nie musimy się martwić, że jakieś zmiany w tych testach wpłyną na inne testy.

Czy klasy prywatne są potrzebne?

Oczywiście skorzystanie w przykładzie z klasy prywatnej nie było wymagane. Praktycznie ten sam efekty uzyskalibyśmy, gdyśmy TestController utworzyli w normalny sposób. Tylko niestety według mnie na dłuższą metę takie rozwiązanie mogłoby powodować problemy.

Nie raz widziałem jak programiści zamiast utworzyć swojej własnej klasy na potrzeby testów, korzystali już z jakieś gotowej z innych testów. Co niestety często później powodowało, że zmiany wpływały na dużo większą liczbę testów niż pierwotnie mogłoby się wydać. Co w efekcie powoduje dużo większe koszty utrzymania takich testów.

Dlatego osobiście w takich przypadkach bardzo chętnie korzystam z klas prywatnych 🙂

Przykład

Na githubie (https://github.com/danielplawgo/PrivateClass) znajduje się przykład, na którym możesz sprawdzić działanie klasy prywatnych. Do uruchomienia przykładu nie jest potrzebna żadna zmiana w projekcie.

Zachęcam do pobrania i pobawienia się 🙂

Podsumowanie

Mam nadzieje, że po tym wpisie, jak ktoś zada Ci pytanie, czy w C# możemy utworzyć klasę prywatną, będziesz wiedział co odpowiedzieć. Do tego podasz realny przykład, gdzie taka klasa może się przydać.

A może będziesz miał ciekawe pytanie rekrutacyjne? 😉

Użyłeś kiedyś klasy prywatnej w swoim kodzie?

3 thoughts on “Prywatna klasa?

  • Pingback: dotnetomaniak.pl
  • Ok. Trochę mi to rozjaśniło podejście do prywatnych klas. Myślę że w normalnym kodzie gdzie mamy wewnętrze bardziej rozbudowane zmienne lepiej użyć wewnętrznej klasy niż tysiąca ifów 🙂

  • Prywatne klasy mają zastosowanie również w przypadku, gdy chcemy zaaplikować jakiegoś customowego distincta lub ordera dodając odpowiednio implementacje interfejsów IEqualityComparer lub IComparer. Jeżeli robimy to w obrębie danej klasy, to prywatne klasy są wtedy najlepszym rozwiązaniem bo zachowujemy enkapsulację.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *