5 rad od seniora C#, które zmienią Twój styl kodowania
Nauka kodowania może być sporym wyzwaniem, dlatego słuchanie się rad bardziej doświadczonych programistów może pomóc Ci opanować podstawy kodowania w szybki i łatwy sposób. Te pięć wskazówek C#, które zaproponował nam w tym artykule Senior Developer, zmieni sposób, w jaki piszesz swój kod. W tym artykule dowiesz się, jak kodować jak profesjonalista! Możesz zacząć już teraz!
Wskazówki te zostały opracowane przez Milana Jovanovića, Senior Software Engineer w HTEC Group, ogromnej firmie technologicznej, która sama zebrała aż 140 milionów dolarów w swojej ostatniej rundzie inwestycyjnej!
Definiowanie zmiennych tymczasowych w składni zapytań LINQ
W swojej pierwszej wskazówce Milan mówi, że istnieje możliwość definiowania zmiennych tymczasowych w składni zapytań LINQ.
Jest to o tyle interesujące, że rozmawiał on z programistami i wielu z nich zupełnie nie wiedziało o tej funkcji. I dlatego postanowił to wyjaśnić:
„Używając słowa kluczowego let, możesz zdefiniować wartość tymczasową w swoich zapytaniach LINQ. Możesz wykorzystać to do dalszych obliczeń lub zwrócić tę wartość jako wynik.”
from user in _dbContext.Set<User>().AsNoTracking()
where user.Id == userId
let name = $"{user.FirstName} {user.LastName}"
let hasFirstName = !string.IsNullOrEmpty(user.FirstName)
let hasLastName = !string.IsNullOrEmpty(user.LastName)
select new
{
user.Id,
Name = name,
ProfileComplete = hasFirstName && hasLastName
}
Kolejną kwestią, którą porusza, jest to, że podczas pisania zapytań EF Core (Entity), użyta klauzula (let) jest również przekładana na poprawny SQL.
Dodatkowo zaleca przetestowanie go i sprawdzenie wygenerowanego SQL:
„Nie wszystko jest obsługiwane jak w przypadku in-memory LINQ”.
Instrukcja switch w celu obliczenia wartości
Milan postanowił również podzielić się z nami najlepszą praktyką podczas pisania czystego kodu w C#. Osobiście widziałem sporo takich przypadków i bardzo polecam samemu sprawdzić.
Milan mówi nam, że od wersji 8 języka C# można używać wyrażeń switch (switch expressions), aby zastąpić instrukcję switch (switch statement).
Zły sposób:
switch (DateTime.Now.DayOfWeek)
{
case DayOfWeek.Monday:
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
return "Not Weekend";
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
return "Weekend";
default:
throw new ArgumentOutOfRangeException();
}
Dobry sposób:
DateTime.Now.DayOfWeek switch
{
not (DayOfWeek.Saturday or DayOfWeek.Sunday) => "Not Weekend",
DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
_ => throw new ArgumentOutOfRangeException()
}
Oprócz tego, jak twierdzi, jest miejsce na ulepszenia:
„Oprócz tego, począwszy od wersji 9 języka C#, możemy dodać do składni dopasowane do wzorca operatory logiczne dla jeszcze większej elastyczności.”
Utwórz leniwą bezpieczną dla wątków implementację Singleton
I tutaj pojawiają się pytania, jak utworzyć leniwą, bezpieczną dla wątków implementację Singleton.
Według Milana istnieje kilka sposobów, aby to zrobić, ale powinieneś zawsze polegać na blokowaniu, aby uniemożliwić jednoczesny dostęp, dzięki czemu implementacja jest bezpieczna dla wątków.
Milan ostrzega jednak, że taka praktyka wymagałaby dość zaawansowanej wiedzy na temat mechanizmów blokujących.
„Możemy jednak wykorzystać klasę Lazy
, aby „leniwie” instancjonować instancję klasy.”
public sealed class Singleton
{
private static readonly Lazy<Singleton> LazyInstance =
new Lazy<Singleton>(() => new Singleton());
private Singleton()
{
}
public static Singleton Instance => LazyInstance.Value;
}
Na koniec twierdzi również, że współbieżność nie jest ważna:
„Lazy jest również domyślnie bezpieczny dla wątków, więc nie musisz „myśleć” o współbieżności”
Tworzenie i używanie funkcji lokalnych
Dla tych, którzy nie wiedzą, czym jest funkcja lokalna. Funkcje lokalne pozwalają zadeklarować metodę wewnątrz wcześniej zdefiniowanej metody. Funkcja ta została dodana w wersji 7 języka C# i Milan postanowił wyjaśnić to w zrozumiały sposób:
„Funkcje lokalne są widoczne tylko w obrębie zakresu ich elementu zawierającego. Zazwyczaj definiowałbyś i używałbyś ich wewnątrz innej funkcji.”
public IEnumerable<string> CapitalizeFirstLetter(IEnumerable<string> enumerable)
{
if (!enumerable.Any())
{
throw new ArgumentException("The sequence is empty.");
}
return enumerable.Select(CapitalizeFirstLetterLocal);
static string CapitalizeFirstLetterLocal(string input) =>
input switch
{
null or "" => throw new ArgumentNullException(nameof(input)),
_ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1))
};
}
Inną kwestią, o której mówi Milan, jest fakt, że istnieje również możliwość, aby funkcje lokalne były statyczne, o ile nie mają dostępu do elementów instancji.
„Interesujące jest połączenie funkcji lokalnych z iteratorami. Iteratory w zamyśle wykorzystują wartościowanie leniwe. Ale być może chcesz wykonać sprawdzanie argumentów, a tu właśnie lokalne funkcje mogą być pomocne.”
Połączenie funkcji lokalnych z blokami iteratorów
Jeśli już jesteśmy przy funkcjach lokalnych to pociągnijmy ten temat! Tym razem Milan chciał podzielić się tym, że istnieje możliwość osiągnięcia leniwej ewaluacji z blokami iteratorów przy jednoczesnym sprawdzaniu argumentów.
„Zauważ, że funkcja lokalna używa instrukcji „yield return”, która wykonuje się, gdy następuje enumeracja. Jeśli blok iteratora znajduje się bezpośrednio wewnątrz „ReadFileLineByLine
”, otrzymasz wyjątek tylko podczas wyliczania wyników.”
public IEnumerable<string> ReadFileLineByLine(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentNullException(nameof(fileName));
}
return ReadFileLineByLineImpl();
IEnumerable<string> ReadFileLineByLineImpl()
{
foreach (var line in File.ReadAllLines(fileName))
{
yield return line;
}
}
}
Ta korzystna praktyka pozwala na wykrycie wyjątków zanim się pojawią:
„Wyjątek zostanie wyrzucony, gdy tylko „ReadFileLineByLine
” zostanie wywołany.”
Jeszcze raz dziękuję Milanowi Jovanovićowi za udostępnienie tych wskazówek i wniesienie wartości do tak dużej i wspaniałej społeczności programistów C#.