Ref Returns, Ref Locals, Ref Arg

Powracamy do podstaw, tak – dla sportu.

Przekazywanie zmiennej typu wartościowego, takiej jak int czy czy struct do metody przez referencję oraz zwracanie wartości z metody przez referencję to coś, co rzadko wykorzystuje się na co dzień, dlatego od czasu do czasu warto odświeżyć sobie tę wiedzę.

W załączonym kodzie możesz prześledzić różnice między przekazywaniem argumentów przez wartość oraz przez referencję. Przedstawiam przykłady przekazywania typów wartościowych oraz referencyjnych jako argumenty do metod i modyfikowania ich wartości wewnątrz metod.

Przed przejściem do kodu, należy pamiętać, że przekazywanie zmiennych przez referencję może prowadzić do trudniejszego zrozumienia kodu i jego debugowania. Dlatego należy zachować ostrożność i rozważyć odpowiednio użycie tej techniki.

Program wyświetli wartości zmiennych przed i po przekazaniu ich jako argument do metod. Ilustruje różnice między przekazywaniem przez wartość i przez referencję, a także wpływ modyfikowania wartości argumentów wewnątrz metod.

GitHub: Ref Arg

// Przejdź do GitHub, aby przeanalizować całość kodu
// wraz z wydrukowanymi wynikami i komentarzami
 
var globalInt = 100;
ArgIntMethod(globalInt);
ArgRefIntMethod(ref globalInt);

var globalStruct = new FirstStruct { /***/};
ArgStructMethod(globalStruct);
ArgRefStructMethod(ref globalStruct);

var globalClass = new Class { /***/};
EditPropsOfArgClassMethod(globalClass);
EditPropsOfArgRefClassMethod(ref globalClass);

Przedstawiłem również przykłady zwracania z metody typów wartościowych oraz referencyjnych przez referencję oraz ich przypisanie do lokalnych referencyjnych typów wartościowych.

GitHub: Ref Locals, Ref Returns

// Przejdź do GitHub, aby przeanalizować całość kodu
// wraz z wydrukowanymi wynikami i komentarzami

var globalClassObject = new Class();
var value = globalClassObject.GetInt();
value += 100;

ref var localRef = ref globalClassObject.RefGetInt();
localRef += 100;

var localValueType = globalClassObject.RefGetInt();
localValueType += 1000;

var complexObject = new Object();
var dto = complexObject.GetDto();
dto.IntProp += 111;
dto = new Dto { IntProp = 1_000 };

ref var refDto = ref complexObject.RefGetDto();
refDto = new Dto { IntProp = 5_000_000 };

internal class Class
{
    private int _valueTypeVariable = 100;

    public int GetInt() => _valueTypeVariable;
    public ref int RefGetInt() => ref _valueTypeVariable;
}

internal class Object
{
    private Dto _dto = new ();

    public Dto GetDto() =>_dto;
    public ref Dto RefGetDto() => ref _dto;
}

internal class Dto
{
    public int IntProp { get; set; } = 111;
}

Przekazując do metody zmienną typu wartościowego, jej wartość nie jest trwale zmodyfikowana poza tą metodą. Natomiast, gdy przekazujemy je przez referencję, jej wartość może zostać zmodyfikowana wewnątrz metody, co będzie miało wpływ na jej wartość poza metodą.

Podsumowując, gdy potrzebujemy zoptymalizować wydajność swojej aplikacji, czasami lepiej jest używać typów wartościowych, zamiast typów referencyjnych. Powodem jest to, że typy wartościowe są przechowywane na stosie, a nie na starcie, co oznacza, że GC ma mniej pracy, ponieważ nie musi śledzić i czyścić nieużywanych obiektów.

Kolejnym sposobem na poprawienie wydajności programu jest przekazywanie typów wartościowych przez referencję. Unikamy w ten sposób nadmiaru kopiowania wartości do zasięgu lokalnego metody, zwłaszcza gdy pracujemy z dużymi wartościowymi lub często wywołujemy metodę.

Pamiętajcie, że przekazywanie argumentów przez referencję może być użyteczne w celu poprawienia wydajności, ale należy zachować ostrożność i rozważyć, czy jest to odpowiednie dla danego przypadku.

P.S. Większą część tekstu na blogu i w GitHub oraz komentarze w kodzie wygenerował za mnie ChatGPT (nie wiem, czy chciałoby mi się to wszystko pisać samemu xD).

Trzej DotNetos

Trzej dotNETowi amigos przemierzą nasz kraj.

Konrad Kokosa
Szymon Kulec
Łukasz Pyrzyk

Cytat:

„Pakujemy się we trójkę w samochód i wyruszamy w Polskę. 5 miast, dzień po dniu. Trzech .NETomaniaków. Trzej Dot Netos w trasie. Codziennie wieczorem inne miasto, inni ludzie ale te same tematy – wydajność .NET, wnętrza .NET, zaawansowane tematy .NET. Tematycznie – ostra jazda bez trzymanki. Litości nie będzie. Jeśli nudzisz się na sesjach o .NET na innych konferencjach, teraz powinieneś być zadowolony! Oczywiście nie ma to być sztuka dla sztuki. Wszystkie poruszane tematy będą praktyczne i życiowe. Ale nie będziemy nudzić, że „typy referencyjne są na stercie, a wartościowe na stosie itd. itp.”. Oj nie nie!”

Przeczytaj całość na Blog Kokosa

Continue reading →

Kalkulator napisany z pomocą behavior-driven development (BDD)

Postawiłem sobie za cel napisanie kalkulatora przy użyciu metody BDD a wynikami mojej pracy chcę się z wami podzielić.

Kalkulator ten będzie zachowywać się jak ten z lat dziewięćdziesiątych, pokazany poniżej na obrazku. Mam jeszcze taki sam w domu — ma już chyba 20 lat. Ten, który tutaj zaimplementuje, będzie posiadał, tak samo, jak ten oryginalny, kilka dziwnych zachowań. Na przykład, przy każdym ponownym wciśnięciu „=”, liczba na wyświetlaczu będzie się zwiększać o jeden, jeśli wcześniej wykonaliśmy działanie „1+1”.

Continue reading →

Pex For Fun

Jest ciekawa gierka dla programistów. Coś jak sudoku, czyli nie ma sensownego wytłumaczenia, dlaczego się w to gra, ale wciąga.

Pex For Fun polega na tym, aby napisać taki kod, który zwróci wyniki zgodne z oczekiwaniami. Przypomina to trochę TDD, ale nie do końca, ponieważ tutaj już mamy gotowe testy jednostkowe. Celem gry jest odgadnięcie i napisanie implementacji, która zapali wszystkie testy jednostkowe na zielono.

Kodu testów jednostkowych nie znamy. W zamian mamy tabelkę, w której są oczekiwane wartości wyjściowe dla zadanych wartości wejściowych. Sztuczka polega na tym, aby kod, który napiszemy, zwracał takie same wartości jak te oczekiwane w tabelce.

Continue reading →

Zakleszczenie wątków: lock (lockA) { lock (lockB) { } }

Zakleszczenie, to taka sytuacja gdzie jeden wątek czeka na zakończenie drugiego wątku, aby otrzymać dostęp do sekcji krytycznej, a ten drugi czeka na zakończenie tego pierwszego.

Przeanalizujmy poniższy kod:

Continue reading →