Jaka jest różnica między typem prostym (wartościowym) a referencyjnym?

Typy proste są przechowywane na stosie (stack) i bazują na wartości. Przypisując jedną zmienną do drugiej - zostanie przepisana jej wartość, np. 5. Typy referencyjne są przechowywane na stercie (heap). Nie przechowują wartości, ale wskazują one na obszar w pamięci. Jeśli przypiszemy taki obiekt do innego, to zmieniając wartość dowolnego - zmieni się w obu.

Jaka jest różnica między ref a out?

Oba przekazują zmienną przez referencję/adres w pamięci, zamiast przez wartość. Zmienna przekazana przez ref musi być wcześniej zainicjalizowana.

Jaka jest różnica między klasą a obiektem?

Klasa to definicja teoretyczna, a obiekt to instancja danej klasy.

Co to jest interfejs?

Interfejs - potocznie nazywany kontraktem - to opis zachowania (właściwości, metody, zdarzenia), które implementująca go klasa musi spełnić. Nie można utworzyć instancji interfejsu, a jego metody są domyślnie publiczne.

Czy metody interfejsu mogą korzystać z modyfikatorów dostępu?

Domyślnie wszystko w interfejsie jest public. Od wersji języka C# 8 została dodana obsługa private i protected.

Co to jest klasa abstrakcyjna?

Klasa abstrakcyjna zawiera abstrakcyjne metody/właściwości, które będą musiały zostać zaimplementowane w klasie dziedziczącej. Może posiadać zaimplementowane metody oraz implementować interfejsy. Nie można utworzyć instancji klasy abstrakcyjnej. Często mówi się na nią “klasa bazowa” (base class).

Co to jest metoda abstrakcyjna?

To sygnatura metody, którą będzie musiała zaimplementować klasa dziedzicząca.

Czym różni się interfejs od klasy abstrakcyjnej?

Klasa abstrakcyjna może posiadać zaimplementowane metody.

Czy klasa abstrakcyjna może zawierać metody nieabstrakcyjne?

Tak.

Czy klasa abstrakcyjna może implementować interfejs?

Tak.

Czym się różni interfejs od klasy?

Interfejs jest kontraktem, który dana klasa musi zaimplementować. Nie da się utworzyć instancji interfejsu.

Co to jest metoda wirtualna?

Jest to metoda, którą można nadpisać lub rozszerzyć w klasie dziedziczącej. Klasa dziedzicząca używa słowa kluczowego override i możemy zostawić bazowe wywołanie, lub je przysłonić.

Co to jest boxing i unboxing?

Boxing to zmiana typu wartościowego na object, a unboxing to w drugą stronę. Unboxing jest jawny, bo musimy podać typ, na który rzutujemy, np. (int).

Jaka jest różnica między const i readonly?

Const można stosować tylko do typów prostych (wartościowych) i jest skompilowany do biblioteki. Readonly jest ustawiany dopiero przy starcie aplikacji i może być stosowany zarówno do typów prostych jak i referencyjnych. Jak wiemy że coś jest stałe to lepiej zrobić const, np const float PI = 3.14

Czym się różni dynamic od object?

W uproszczeniu to do obu można przypisać dowolne wartości, ale poprawność objecta jest sprawdzana w czasie kompilacji, a dynamic dopiero w czasie wykonywania programu. Przydatne do integracji z innymi językami / COM interop.

Jak działają słowa kluczowe as i is?

As przypisze zmienną do typu który podamy, ale jak nie uda mu się to otrzymamy null zamiast wyjątku. Is zwraca bool, więc można go użyć w instrukcji warunkowej żeby sprawdzić czy coś jest danym typem.

Czym jest / co nam daje enkapsulacja?

Możemy ukryć implementację danej biblioteki / fragmentu kodu, bez udostępniania szczegółów implementacyjnych. W C# zaimplementowano ją poprzez modyfikatory dostępu.

Czym się różni enkapsulacja od hermetyzacji?

Niczym. Enkapsulacja to angielskie słowo, a hermetyzacja to jego polski odpowiednik.

Jak działa słowo kluczowe sealed?

Powoduje, że po klasie nie można dziedziczyć.

Opisz modyfikatory dostępu w C#
  • public - zmienna widoczna jest na zewnątrz klasy
  • private - zmienna jest widoczna tylko wewnątrz klasy, ukryta na zewnątrz
  • protected - zmienne chronione są widoczne też w klasie która podziedziczyła
  • internal - jak public, ale tylko w obrębie assembly (biblioteki)
  • protected internal - to kombinacja tych obu
Na czym polega YAGNI?

You Ain’t Gonna Need It. Nie pisz kodu dla potencjalnych przyszłych funkcjonalności. Zrób prostą implementację, a jeśli będzie potrzeba, to się ją zmieni.

Czym jest i jakie są zalety polimorfizmu?

Polimorfizm jest zdolnością do różnych zachowań, w zależności od kontekstu. Przykład: obiekty różnego typu mogą być sprowadzone do wspólnego mianownika, np. poprzez implementację interfejsu. Następnie metoda może przyjąć ten interfejs w parametrze, co prowadzi do skrócenia kodu programu. Np. wszystkie komendy i query w bibliotece MediatR implementują IRequest.

Dlaczego warto używać klasy StringBuilder?

Klasa StringBuilder jest bardziej wydajna, niż ręczne łączenie stringów znakiem ‘+’, chociaż w najnowszych wersjach .NET różnica jest naprawdę bardzo minimalna.

Opisz 3 główne filary programowania obiektowego

1. Enkapsulacja – dzięki modyfikatorom dostępu możemy ukryć detale implementacyjne obiektu, a na zewnątrz udostępnić tylko pożądane przez nas zachowania.

2. Dziedziczenie – dzięki któremu części wspólne zachowań obiektów mogą być zdefiniowane w jednym miejscu.

3. Polimorfizm – który pozwala na inne zachowanie danego obiektu, w zależności od tego kto o niego odpytuje.

Czasami pytanie jest o 4 filary – wtedy dodatkowo opisuje się abstrakcję.

Na czym polega CQS?

Command Query Separation to oddzielenie funkcjolaności na poziomie klasy. Metoda albo coś zwraca - i możemy ją bezpiecznie wywołać 100 razy, wiedząc, że nie zmieni stanu systemu - albo coś zmienia i zwraca void. CQRS jest przeniesieniem tej idei na poziom infrastruktury.

Do czego można wykorzystać prywatny konstruktor?
  1. Przy implementacji fabryki / singletona.

  2. Umożliwiają zrobienie klasy bazowej, z której mogą dziedziczyć tylko subklasy:

    public abstract class BaseClass
    {
        private BaseClass() { }
    
        public class SubClass1 : BaseClass
        {
            public SubClass1() : base() { }
        }
    
        public class SubClass2 : BaseClass
        {
            public SubClass2() : base() { }
        }
    }
  3. Ułatwiają tworzenie bazowego konstruktora, wywoływanego przez inne, np.

    public class MyClass
    {
        private MyClass(object data1, string data2) { }
    
        public MyClass(object data1) : this(data1, null) { }
    
        public MyClass(string data2) : this(null, data2) { }
    
        public MyClass() : this(null, null) { }
    }
Opisz każdą literę z zasady SOLID

S – Single Responsibility Principle – klasa powinna mieć tylko jeden powód do zmiany. Promuje to tworzenie małych, spójnych klas.

O – Open-Closed Principle – klasa powinna być otwarta na rozszerzenia, ale zamknięta na modyfikacje. Promuje ona korzystanie z polimorfizmu przez interfejsy i klasy bazowe, aby dodawać nowe funkcje do systemu.

L – Liskov Substitution Principle – gdy zastąpimy klasę podziedziczoną jej rodzicem to program powinien dalej poprawnie działać. Jest to pilnowanie, czy poprawnie realizujemy polimorfizm.

I – Interface Segregation Principle – klasy nie mogą zależeć od interefejsów, których nie potrzebują. Promuje ona robienie małych i spójnych interfejsów.

D – Dependency Inversion Principle – moduły wysokiego poziomu nie powinny być zależne od modułów niskiego poziomu. Oba typy powinny polegać na abstrakcji. Prowadzi to do kodu, w którym łatwiej wprowadza się zmiany, i który łatwiej testować.

Do czego służy słowo kluczowe params?

Do przekazania dowolnej ilości parametrów poprzez jednowymiarową tablicę do metody. Np params int[] albo params string[]. Jeśli użyjemy params to nie możemy przekazać drugiego argumentu do metody.

Jakie znasz sposoby na odwrócenie zależności (Inversion of Control)?
  1. Wstrzykiwanie zależności (Dependency Injection).

  2. Wzorzec Obserwator / publikowanie zdarzeń (event).

Co to jest lazy loading i eager loading? Gdzie się je stosuje?

Lazy loading polega na odsunięciu w czasie załadowania danych, do momentu w którym będą potrzebne. Jest to domyślne zachowanie Entity Framework. Eager loading polega od razu na załadowaniu większej ilości danych (przez Include w EF). Jest to przydatne, gdy chcemy przeprowadzić dużo operacji na danym zestawie danych i oszczędzić czas, który zejdzie na komunikację z serwerem.

Jakie są ograniczenia konstruktora w struct?
  1. Nie może być bezparametrowy.

  2. Musi przypisywać wartości do każdej property w struct.

Czy można tworzyć metody w struct?

Tak

Jak posortować tablicę w C# malejąco?

Używając metod Array.Sort() a potem Array.Reverse().

Jak usunąć duplikaty z listy w C#?

Używając metody Distinct() z namespace System.Linq

Jaka jest różnica między IEnumerable i IEnumerator?

IEnumerator reprezentuje iterator. Używamy metody MoveNext() żeby szedł dalej, metodą Current() sprawdzamy aktualny stan iteracji. IEnumerable dziedziczy po IEnumerator. Reprezentuje on kolekcję. Możemy wywołać na nim foreach.

Jaka jest różnica między IQueryable i IEnumerable?

IQueryable dziedziczy po IEnumerable. IQueryable zastosuje warunek z Where() dopiero przy odpytywaniu SQLa, a IEnumerable najpierw pobierze wszystkie dane i przefiltruje je w pamięci aplikacji.

Do czego służy yield?

Yield oszczędza nam trochę pamięci, bo nie musimy tworzyć tymczasowych list w metodach. Znam dwa zastosowania yield:

  1. Custom iteration: filtrowanie list.

  2. Stateful iteration: wychodzenie z foreacha i powrót do niego po następny element, gdy metoda przechowuje swój prywatny stan.

Co to są delegaty? Do czego służą?

Dzięki delegatom możemy przekazywać funkcje jako parametry metody.

Jakie są trzy najpopularniejsze implementacje delegat?

- Action - oczekuje funkcji, która może przyjąć do kilkunastu parametrów, nic nie zwraca

- Func - oczekuje funkcji, może przyjąć do kilkunastu parametrów, ostatni parametr jest zwracany.

- Predicate - oczekuje funkcji, która przyjmuje jeden parametr i zwraca bool, To samo można zrobić używając Func<?, bool>.