Sytuacja kobiet w IT w 2024 roku
17.12.20193 min
Matt Eland

Matt ElandSoftware Development ManagerMoveHQ

Jak wyeliminować null w C#

Sprawdź, jak dzięki Option dodać elementy programowania funkcyjnego w C# i zapobiec wyjątkom pustej referencji.

Jak wyeliminować null w C#

Przygotowałem krótki artykuł, który pokazuje, w jaki sposób klasa Option może wprowadzić funkcyjne koncepcje programowania do kodu w C# i zapobiec wyjątkom pustej referencji.


W tym artykule użyję biblioteki language-ext, która jest najpopularniejszą biblioteką .NET, wspierającą programowanie funkcyjne w językach obiektowych (oprócz F#).

Instalacja biblioteki language-ext

Aby pracować z biblioteką language-ext, musisz dodać odwołanie do pakietu NuGet. Można to zrobić za pomocą Menedżera pakietów NuGet w Visual Studio lub uruchamiając Install-Package LanguageExt.Core z konsoli menedżera pakietów.

Czym jest Option?

Options są typami referencyjnymi, które zawierają pewną wartość lub jej brak. Jako przykład weźmy następującą metodę, która przeszukuje słownik i zwraca z niego wartość:

public Option<ResumeKeyword> GetKeyword(
   IDictionary<string, ResumeKeyword> keywords, 
   string word) { 

  // Ensure we're searching for a lower-cased non-null word
  string key = word ?? word.ToLowerInvariant() : string.Empty;

  // If the keyword is present, return it
  if (keywords.ContainsKey(key)) {
    return keywords[key]; // Implicitly converted to Some<ResumeKeyword>
  }

  // No keyword found
  return Option<ResumeKeyword>.None;
}


W tej metodzie, ponieważ typem zwracanym jest Option<ResumeKeyword>, typem zwracanym będzie też Some<ResumeKeyword> lub None. Wynikiem nigdy nie będzie zwrócony bezpośrednio null, więc wywołujący nie musi się martwić o nulle.

Kompilator jest w stanie przekonwertować wartość standardową na wartość Some, ale składnia używająca typu None nie jest idealna. Pracując z Option zobaczymy dodatkową złożoność tego rozwiązania.

Na szczęście typ zwracany można uprościć, jeśli dodamy następującą linijkę w górnej części pliku: using static LanguageExt.Option <ResumeKeyword>;.

To pozwala nam po prostu zwrócić None, zamiast zwracać Option<ResumeKeyword>.None, co znacznie łatwiej odczytać.

Teraz, gdy masz już Option, chcesz zacząć z nim pracować. Ponieważ pracujemy z Option, kompilator nie pozwala nam tworzyć potencjalnie błędnego kodu, takiego jak:

// Give a bump for various words in the title
foreach (var word in job.Title.Split())
{
    var keyword = FindKeyword(keywordBonuses, key);
    jobScore += keyword.Value; // Does not compile
}


Ten kod nie kompiluje się, ponieważ słowem kluczowym jest Option<ResumeKeyword> zamiast ResumeKeyword, przez co nie można uzyskać bezpośredniego dostępu do składowych ResumeKeyword. To utrudnienie jest w rzeczywistości pozytywem, ponieważ wiemy, że FindKeyword czasami może nie znaleźć żadnego dopasowania słowa kluczowego, co w przypadku standardowej aplikacji skutkowałoby NullReferenceException, gdybyśmy zapomnieli dodać sprawdzenia obecności wartości null.

Zamiast tego napiszemy:

// Give a bump for various words in the title
foreach (var word in job.Title.Split())
{
    var keyword = FindKeyword(keywordBonuses, key);
    jobScore += keyword.Some(k => k.Value)
                       .None(0);
}


Mówimy tutaj, że chcemy zwiększyć wynik o Value z ResumeKeyword, jeśli słowo kluczowe Some jest obecne, a jeśli jest to None, do wyniku należy dodać 0.

Sprawdzanie typów przez kompilator wymusi te reguły i upewni nas, że mamy właściwe typy, zmuszając nas do zastanowienia się, czy wartość jest obecna i zachować się prawidłowo, biorąc to pod uwagę.

W przypadku scenariuszy, w których chcesz coś zrobić tylko wtedy, gdy wartość jest obecna lub nie i nie potrzebujesz rzeczywistej wartości, możesz sprawdzić właściwości IsSome lub IsNone. Np.:

if (keyword.IsNone) {
   Console.WriteLine($"No value defined for keyword '{word}');
}

Co z typami dopuszczającymi wartości null?

W .NET przez jakiś czas istniało pojęcie nullable types, wywoływanych przez Nullable<T> (często kodowane jako T? myVar).

Typ dopuszczający wartość null to sposób reprezentowania typu wartościowego jako obiektu, który może mieć wartość null. Różni się to od Option, ponieważ:

  1. Option działa zarówno z typami referencyjnymi, jak i wartościowymi
  2. Chociaż Nullable<T> oferuje HasValue i Value, Option<T> daje wygodny sposób reagowania w określony sposób na Some i None, co widzieliśmy wyżej

Podsumowanie

Klasa Option z Language-Ext zmusza do podejmowania inteligentnych decyzji i bezpiecznej pracy przy działaniu z potencjalnie brakującymi wartościami.

Wadą tego podejścia jest to, że utrudnia to zarówno czytanie, jak i pisanie kodu,, ale w krytycznych aspektach jakości bazy kodu lub obszarach, w których często występują nulle, sensowne może być włączenie Option<T> do kodu.

Pamiętaj też, że Option<T> to tylko fragment możliwości oferowanych przez Language-Ext. 

<p>Loading...</p>