SOLID.Blog

Witamy na naszym blogu.

„Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” – Martin Fowler


Blog post image

Człowiek się tak szybko starzeje, że nie zauważa kolejnych wersji frameworków przyzwyczajając się do znanych konceptów. Co prawda od daty wypuszczenia wersji 8 .NETu i C# 12 minęło już trochę czasu spróbujmy spojrzeć z tej perspektywy na nowe koncepty i możliwości oraz ich wpływ na codzienną pracę w formie case-study: analiza i komentarz w czasie.

 

Przypadek #1: primary constructor

 

To jest coś do czego nie byłem przekonany od samego początku w dużych projektach, do których jestem przyzwyczajony. Zasada jest prosta: dodajmy cukier składniowy tak, aby móc pisać mniej tekstu a osiągnąć to samo. To jest ostatnio domena nowych wersji.

 Przykład z dokumentacji wydaje się całkiem przyjemny:


 public class SavingsAccount(string accountID, string owner, decimal interestRate)

: BankAccount(accountID, owner)

{

public SavingsAccount() : this("default", "default", 0.01m) { }

//...

}


Ot przenosimy zapis konstruktora do definicji klasy. Minusem jest konieczność wykorzystywania tego konstruktora przez inne (zastępuje wywołanie `: base(`). Jeden sposób kreacji obiektu to raczej oczekiwana cecha niż wyjątek.

Dokumentacja (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/primary-constructors)

 

Przypadek #2: Getter vs expression body definition

 

Ten przypadek akurat dla mnie wydaje się oczywisty, ale dla nieco starszych programistów wydawał się dziwny, a mianowicie możliwość zapisu gettera dla pól readonly przez expression body definition.


public class Test {

private string _testFieldValue;

 

public string TestField1 {

get { return _testFieldValue; }

}

public string TestField2 { get => _testFieldValue; }

public string TestField3 => _testFieldValue;

}

 

Wszystkie te pola mają takie samo zachowanie dla użytkownika klasy Test. Osobiście lubię wykorzystywać ostatni przypadek dla pól, które mają konwertować wartość innego pola w obiektach domenowych, np. konwersja GUIDa do stringa w wybranym formacie.

 

Przypadek #3: Collection expressions

 

Czasem mam wrażenie, że C# i Python się do siebie zbliżają. Jakże inaczej byłoby wytłumaczyć taki sam sposób inicjalizacji kolekcji (przynajmniej dla zmiennych tablicowych) w obu językach?


List<int> numbers = [7, 8, 9];


Co ciekawe nie można używać collection expression w celu dostarczania danych, których wartość jest oczekiwana w momencie kompilacji, np. inicjalizacja stałych lub domyślna wartość dla metody, ale tak samo nie można w tym miejscu zainicjalizować obiektów implementujących IEnumerable np. List więc nie dziwi to bardzo.

 

Dodatkowo można wykorzystać ten sposób do weryfikacji sekwencji elementów w liście lub obiekcie tablicowym również z wykorzystaniem znaków typu wildcard oraz do nadawania wartości zgodnie z pozycją w kolekcji.


List<int> numbers = [7, 8, 9];

Assert.That(numbers is [7, 8, 9]);

 

List<int> numbers = [7, 8, 9];

Assert.That(numbers is [7, _, 9]);

 

List<string> fruits = ["apple", "banana", "strawberry"];

if (fruits is [var first, var second]) {

Assert.That(first, Is.EqualTo("apple");

Assert.That(second, Is.EqualTo("banana");

}

 

W kwestii oceny to znowu jest kwestia przyzwyczajenia. Dla mnie nowy sposób jest powrotem do korzeni i dobrych wspomnień z językiem Python, który swą łatwością i prostotą wtedy przyciągał.

Dokumentacja (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions)

 

Przypadek #4: Globalizacja definicji

 

Pozwolę sobie cofnąć się do wersji 10tej C# i spojrzeć na zmiany, które przeszły niezauważone, m. in. możliwość definiowania namespace dla pliku w formie deklaracji zamiast bloku. Ta zmiana spowodowała głównie zmniejszenie ilości znaków w pliku i zwiększenie czytelności kodu.

 

namespace MyNamespace;


Dokumentacja (https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#file-scoped-namespace-declaration)

 

Drugą sprawą, której również przyświecały te same cele jest "global usings". Przyznam szczerze, że wykorzystuję ją masowo w testach zapominając kompletnie o konieczności importowania biblioteki do testów.


global using NUnit.Framework;


Dokumentacja (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive#global-modifier)

 

Kolejną wprowadzoną niedawno zmianą jest centralne zarządzanie wersjami paczek, które znacząco przyspieszyło w dużych projektach proces weryfikacji poprawności tzw. pull requestów. Aktualizacja zależności, która jest wykorzystywana w wielu projektach teraz wymaga zmiany jednej linijki w pliku z wersjami paczek.

 

Dokumentacja (https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management)

 

Gdzie nas to zaprowadzi?

 

Skracanie pojęć i słów jest widoczne w codzienności od lat. Czy to oznacza, że kiedyś będziemy mówić kodami kreskowymi?

 

Pewnie nie, ponieważ ludzki mózg potrzebuje porównań i obrazów, które może połączyć w całość. Mała część społeczeństwa ma dobrą pamięć do numerów. Możemy też postępować jak luddyści, technologiczni hipsterzy XIX wieku – zawsze przed trendem, ale w odwrotną stronę. Gdyby żyli dziś, zamiast rzucać butami w maszyny parowe, pewnie wypowiadaliby wojnę automatom do kawy i smartfonom, wykrzykując: "Nie będziesz mojej pracy zabierać, robocie!".

 

Tak czy owak zmiany są dobre, pod warunkiem, że je dobrze wykorzystamy. Celem życia jest adaptacja i przetrwanie. 


Udostępnij ten wpis:

FacebookX (Twitter)LinkedIn
Firma

Solid Company
Solid Company

Small software house specialized in web applications development using Microsoft .NET platform and Azure cloud technologies.