Juan Alberto España Garcia
Juan Alberto España GarciaFounder and CEO @ ByteHide

Jak być lepszym C# Developerem – 5 wskazówek

Co jeśli powiedziałbym Ci, że 5 prostych wskazówek od starszego programisty C# może uczynić Cię lepszym koderem? Uwierzyłbyś mi?
8.12.20227 min
Jak być lepszym C# Developerem – 5 wskazówek

Większość osób nie uwierzy, ponieważ jako ludzie mamy tendencję do zakładania, że umiejętności i doświadczenie są cechami wrodzonymi, z którymi rodzą się tylko najbardziej utalentowani programiści.

Choć częściowo może być to prawdą, to jeśli chodzi o umiejętność tworzenia oprogramowania, większość tego, co czyni wyjątkowego kodera wyjątkowym, nie sprowadza się do naturalnego talentu, ale do celowej praktyki i umiejętności krytycznego myślenia.

Wskazówki w tym artykule zaproponował nam Stefan Djokic, Senior Software Engineer pracujący w EXLRT, cyfrowej agencji customer experience specjalizującej się w handlu detalicznym, podróżach i hotelarstwie pracującej z dużymi firmami, takimi jak Adidas, IBM czy Disneyland Paris.

Bardzo polecam obserwowanie go na Linkedinie, ponieważ zawsze dzieli się wartościowymi treściami na temat C#, .NET i nie tylko!

Używaj yield, gdzie tylko możesz

W pierwszej wskazówce przekazanej przez Stefana, mówi o tym, że programiści zwykle używają zmiennych tymczasowych do przechowywania elementów kolekcji, podczas gdy używają pętli do tworzenia nowej kolekcji elementów.

Sprawdźmy jego przykładowy kod:

List<int> tempResult = new();
foreach(var item in collection)
{
    tempResult.Add(item);
}
return tempResult;


W tym przypadku Stefan zaleca użycie yield return, ponieważ zapewni to stanową i niestandardową iterację:

Kontroler jest zwracany do metody wywołującej za każdym razem, gdy napotkana i wykonana jest instrukcja yield return.

Dodał jeszcze:

Co najważniejsze, przy każdym takim wywołaniu informacje o stanie kontrolera są zachowane, aby wykonanie mogło być kontynuowane natychmiast po instrukcji yield, gdy kontroler powróci.

A wynik jest następujący:

foreach(var item in collection)
{
    yield return item;
}


Na koniec skomentował kilka swoich uwag, o których należy zawsze pamiętać:

  • Nie możemy używać instrukcji yield return lub yield break wewnątrz anonimowych i niebezpiecznych metod. Typem zwracanym metody, w której używamy yield, powinien być IEnumerable lub IEnumerator. Nie możemy używać instrukcji yield return w bloku try-catch, ale możemy ją mieć wewnątrz bloku try-finally.

Pamiętajcie o tym drodzy deweloperzy. Tutaj znajdziecie oryginalny link do tych wskazówek: Używaj yield gdzie tylko możesz.

Uzyskaj indeks iteracji w pętli foreach

Przy drugiej wskazówce, Stefan mówi o najlepszych praktykach dla indeksów i stawia pytanie:

Jak uzyskać indeks bieżącej iteracji pętli foreach?

W ślad za tym sam odpowiedział na to pytanie, aby rozjaśnić nasze wątpliwości:

Najprostszym sposobem śledzenia indeksów jest ustawienie zmiennej indeksowej przed pętlą i zwiększanie jej w każdej iteracji – tak jak w przypadku pracy w pętli while

A proponowany przez niego scenariusz jest następujący:

foreach (var (value, i) in users.Select((value, u) => (value, i)))
{
    User value = user.value;
    int index = user.i;
}


Wyjaśniwszy to, Stefan przechodzi do przedstawienia scenariusza, w którym można to osiągnąć bez użycia zmiennej.

Jest to możliwe, łącząc metodę Select() LINQ i ValueTuple, ale Stefan komentuje, że nie jest to jego ulubiony sposób i szczegółowo opisuje przyczyny:

  1. Zmniejszona czytelność kodu;
  2. Gorsza wydajność (miejsce i czas);
  3. Trudny do utrzymania;
  4. Ale dlaczego?;

No właśnie. Dlaczego? Jestem pewien, że niektórzy z was zadali już sobie to pytanie.

Dlatego Stefan pokazuje nam najlepszy sposób, aby uzyskać indeks iteracji pętli foreach:

Użyj pętli for (jeśli możesz). Użyj właściwości MoveNext()& Current.

Kolejna dobra rada i poprawiamy się o 1%. Pamiętaj, że sukces tkwi w wytrwałości. Tutaj znajdziecie oryginalny link do tych wskazówek: Uzyskaj indeks iteracji w pętli foreach.

Nie używaj konkatenacji stringa + w pętlach

Pętle w C# są głównym punktem trzeciej wskazówki i Stefan ostrzega, że nie jest dobrą praktyką używanie konkatenacji stringa +.

Dzieje się tak dlatego, ponieważ string jest niezmienny, i jak komentuje sam Stefan:

Gdy stworzymy string, nie można go zmienić.

Aby lepiej to zrozumieć, ten świetny senior deweloper podał praktyczny przykład:

Mamy nowy string „Test” – Ten string będzie zajmował miejsce w pamięci na stercie. Zmieniamy oryginalny string na „Test na string”. Utworzymy nowy obiekt string na stercie. Zamiast modyfikować oryginalny string pod tym samym adresem pamięci.

Fragment kodu wygląda tak:

string finalStr = "";
foreach (var str in stringArray)
{
    finalStr = finalStr + str;
}


Jeśli jesteś ciekawy, co się stanie, gdy zrobisz to w ten sposób, to główną rzeczą jest możliwa utrata wydajności.

Dlaczego? Ponieważ wielokrotne alokacje pamięci byłyby używane podczas wielokrotnego modyfikowania stringa.

Ulubionym rozwiązaniem tego problemu przez Stefana jest użycie StringBuilder. Jak stwierdza:

StringBuilder nie tworzy nowego obiektu w pamięci, ale dynamicznie rozszerza pamięć, aby pomieścić zmodyfikowany string.”

Mając na uwadze takie rozwiązanie, powyższy przykład kodu wyglądałby bezbłędnie w następujący sposób:

StringBuilder builder = new StringBuilder();
foreach( var str in stringArray)
{
    builder.Append(str);
}

string finalStr= builder.ToString();


Na koniec inżynier oprogramowania Exlrta stawia pytanie wszystkim deweloperom:

Czy gdzieś w kodzie masz konkatenację + w pętli? Czy spotkałeś się z tym problemem w realnej sytuacji?

Zarówno Stefan, jak i ja chcielibyśmy wiedzieć, czy spotkało Was to kiedykolwiek i czy sami zauważyliście jakieś problemy.

Tutaj znajdziecie oryginalny link do tej wskazówki: Nie używaj konkatenacji stringa '+' w pętlach.

Typy zapieczętowane

Żaba z 6 nogami to nazwa, jaką Stefan nadał opowiadaniu o swojej czwartej wskazówce.

Pewnie zastanawiacie się teraz, od kiedy to żaba ma sześć nóg, prawda🐸? Cóż, pozwólcie, że Stefan opowie wam tę mini historię:

A: Dlaczego masz klasę MyFrog, skoro istnieje standardowa klasa Frog? Do tego Twoja żaba ma sześć nóg?? 😮

B: Haha, pozwoliłeś mi na to 😅

A: Jak?

B: Pozwoliłeś mi przedłużyć standardową żabę o dodatkowe nogi. Pozwoliłeś mi odziedziczyć twoją klasę.

A: A mogłem użyć słowa kluczowego sealed 🤦♂️

Gdyby Stefan napisał książkę o C# wykorzystując tego typu mini historyjki przykładowe i swój sposób wyjaśniania rzeczy, to jestem pewien, że byłby to najlepszy nabytek mojego życia, a książka odniosłaby ogólny sukces (znasz Stefana 😉).

Wracając do wskazówki, głównym powodem (wśród kilku innych, które zobaczymy później), dlaczego używanie typów zapieczętowanych jest dużo lepsze, jest ograniczenie funkcji dziedziczenia.

Obok tego stwierdza:

Kiedy zdefiniujemy klasę jako zapieczętowaną, nie możemy już jej dziedziczyć.

Kolejną wskazówką, którą tutaj podsuwa to, że na metodzie lub właściwości, która nadpisuje wirtualną metodę lub właściwość w klasie bazowej, możemy również użyć słowa kluczowego sealed.

Zapobiega to nadpisywaniu przez inne klasy pewnych wirtualnych metod lub atrybutów, jednocześnie umożliwiając im dziedziczenie po klasie bazowej.

A jeśli zastanawiasz się, jakie są zalety używania zapieczętowanych typów, to Stefan wymienia tutaj 3 punkty:

  1. Zabiera użytkownikom klasy funkcję dziedziczenia, aby nie mogli wyprowadzić z niej klasy.
  2. Najlepiej, jeśli mamy klasę z elementami statycznymi.
  3. Może prowadzić do poprawy wyników.

Spójrzmy na przykład Stefana!

sealed class Users
{
    public string name = "Stefan Djokic";
    public void GetName()
    {
        Console.WriteLine("Name: {0}", name);
    }
}
//Derived Class
public class Details : users //Error
{
    public int age = 27;
    public void GetAge()
    {
        Console.WriteLine("Age: {0}", age);
    }
}


Tutaj znajdziecie oryginalny link do tej wskazówki: Typy zapieczętowane.

Select() vs SelectMany()

W piątej wskazówce, Stefan postanowił podzielić się z nami studium przypadku klienta.

Poznajmy historię:

Klient zażądał wszystkich technologii, które znają twoi pracownicy. ...mamy klasę Pracownik z nazwą pracownika i listą technologii, w których umie pracować

Tak prezentuje się ten scenariusz:

List<Employee> employees = new();
Employee emp1 = new Employee { Name = "Stefan", Skills = new List<string> {"C", "C++", "Java"}};
Employee emp2 = new Employee { Name = "Karan", Skills = new List<string> {"SQL Server", "C#", "ASP.NET"}};
Employee emp3 = new Employee { Name = "Lalit", Skills = new List<string> {"C#", "ASP.NET MVC", "Windows Azure", "SQL Server"}};

employees.Add(emp1);
employees.Add(emp2);
employees.Add(emp3);


Teraz, aby podołać wyzwaniu zaproponowanemu przez swojego klienta, Stefan wymyślił dwa rozwiązania:

  • .Select()
  • .SelectMany()

Przede wszystkim Stefan zaproponował przypuszczalny scenariusz z .Select():

IEnumerable<List<String>> resultSelect = employees.Select(e=> e.Skills);

foreach (List<String> skillList in resultSelect)
{
    foreach (string skill in skillList)
    {
        Console.WriteLine(skill);
    }
}


Sprawdźmy szybko, co Stefan mówi nam o takim sposobie postępowania:

  1. Używamy 2 pętli.
  2. Pierwsza pętla przechodzi przez wszystkich pracowników.
  3. Druga pętla przechodzi przez wszystkie umiejętności każdego pracownika.

Mając to na uwadze, zobaczmy, jak wyglądałby zakładany scenariusz, gdybyśmy użyli .SelectMany():

IEnumerable<string> resultSelectMany = employees.SelectMany(emp => emp.Skills);

foreach (string skill in resultSelectMany)
{
    Console.WriteLine(skill);
}


Już na pierwszy rzut oka widać, że kod wynikowy jest krótszy przy użyciu .SelectMany() w porównaniu do .Select().

Jeśli zrobimy to z .SelectMany(), jak twierdzi Stefan, proces wygląda inaczej:

  1. Od razu dostajemy listę umiejętności.
  2. Pierwsza i jedyna pętla przechodzi przez umiejętności każdego pracownika.

W ten sposób możemy zaoszczędzić 1 pętlę, co zrekompensuje nam wydajność.

Na koniec proponuje nam jeszcze jedną małą, ale dobrą opcję do rozważenia:

Możemy użyć SelectMany do „spłaszczenia” kolekcji zagnieżdżonej lub warstwowej do prostej kolekcji jednopoziomowej.

Tutaj znajdziecie oryginalny link do tej wskazówki: Select() vs SelectMany().

Jeszcze raz dziękuję Stefanowi Djokicowi za udostępnienie tych wskazówek i wniesienie wartości do wielkiej i wspaniałej społeczności programistów .NET. Jeśli podobały Ci się te wskazówki, to polecam Ci obserwować go na Linkedinie ponieważ zawsze jest aktywny i wrzuca dużo wartościowych treści .NET.

<p>Loading...</p>