Znalazłem ostatnio trochę czasu aby dokładniej przyjrzeć się .NET i na własnej skórze przekonać się czy jest warty więcej niż Java. Dotąd napisałem tylko dwa proste programik wykorzystują tę platformę – aplikację MDI obsługująca skromną bazę danych przechowującą informację o osobach opartą na plikach binarnych, oraz obsługę portu rs232. Pamiętam że sposób programowania nie powalił mnie wtedy na kolana, przypominało to raczej tylko trochę uproszczone C++. Oczywiście skorzystałem z całkiem fajnego mechanizmu właściwości(properties) i upraszczającej życie pętli foreach, stworzyłem nawet własną kontrolkę, ale wszystko to było robione w pośpiechu i nie pozwoliło wniknąć głębiej. Teraz postanowiłem nadrobić zaległości i jestem mile zaskoczony.
To co tak naprawdę skłoniło mnie do wypożyczenia książki i studiowania jej od samego początku to możliwość łączenia kodu napisanego w C++(/CLI) z kodem zarządzanym napisanym w .NET. Miło jest móc połączyć wysokopoziomowe programowanie logiki aplikacji z niskopoziomowym dostępem do funkcji systemu operacyjnego Windows takich jak dostęp do kamery czy przechwytywanie komunikatów.
Ponieważ książkę czytam od deski, zostałem zmuszony do przypomnienia podstawowych konstrukcji programistycznych takich jak metody czy proste klasy, ale poznałem przy tym kilka ciekawych słów kluczowych umożliwiających kontrolowanie modyfikacji danych.
Modyfikator out
Modyfikatorem out można oznaczyć dowolną liczbę parametrów metody. Nie ma przy tym znaczenia kolejność, parametry zmodyfikowane przez out mogą wystąpić zarówno na początku listy w środku jak i na końcu. To słowo kluczowe definiuje tzw. parametr wyjściowy. Wykonanie funkcji wymaga zapisania wartości do parametru wyjściowego.
public static void Dodawanie(out int sum, int x, int y, out int ssum)
{
sum = x + y;
ssum = sum * sum;
}
Jeśli kompilator nie znajdzie prawidłowego(co można wymusić komentując linię ssum = sum*sum) przypisania wygeneruje błąd i poinformuje nas o tym komunikatem:
„The out parameter ‘ssum’ must be assigned to before control leaves the current method”
Modyfikator out, może trochę przypominać mechanizm znany z C polegający na przekazywaniu wskaźnika jako parametru funkcji. Jednak w przypadku C# mamy pewność że po wykonaniu podprogramu, w parametrze wyjściowym znajdzie się jakąś wartość, co chroni nas przed nieprzyjemnym komunikatem „Segmentation fault”. Istnieje jeszcze jedna różnica wpływająca na korzyść platformy .NET, mianowicie sposób wywołania. Nie ma tu magicznych zaklęć nie trzeba się domyślać który parametr to wskaźnik i jak został przekazany, ponieważ trzeba to jawnie określić. Kompilator umożliwi uruchomienie programu tylko wtedy gdy wszystkie wywołania funkcji zostaną opisane zgodnie z ich deklaracjami/definicjami, znakomicie wpływa to na czytelność kodu:
static void Main(string[] args)
{
int sum, ssum;
int a = 5, b = 6;
Dodawanie(out sum, a, b, ssum); // FAIL !
Dodawanie(out sum, a, b, out ssum); // OK !
Console.WriteLine("Suma {0} + {1} = {2}, ({0} + {1})^2 = {3}", a, b, sum, ssum);
Console.ReadLine();
}
Modyfikator ref
Działanie modyfikatora ref również jest związane z wywołaniem metod. Jednakże jego zastosowanie ma sens tylko w odniesieniu do obiektów które w łańcuchu dziedziczenia nie mają klasy System.ValueType, a więc takich które tworzone są na stercie i nie są przekazywane przez wartość ale przez referencję. Podobnie jak w przypadku Javy, modyfikacje wykonane na takim obiekcie pozostają aktualne po zakończeniu metody, o czym napisałem we wpisie Pass-by-value w javie. Dla przypomnienia: tak naprawdę przekazywana jest nie referencja a jej kopia, z tego powodu dozwolona jest zmiana stanu obiektu ale przypisanie referencji do innego obiektu już nie. Aby lepiej zrozumieć zagadnienie przedstawiam prosty przykład prezentujący wspomnianą funkcjonalność:
class Punkt
{
public int x;
public int y;
}
class Program
{
public static void CopyReference(Punkt p)
{
p.x = 0;
p.y = 0;
p = new Punkt();
p.x = 200;
p.y = 200;
}
static void Main(string[] args)
{
Punkt p1 = new Punkt();
p1.y = 100;
p1.x = 100;
Console.WriteLine("Przed wykonaniem CopyReference: p1.x = {0}, p1.y = {1}", p1.x, p1.y);
CopyReference(p1);
Console.WriteLine("Po wykonaniem CopyReference: p1.x = {0}, p1.y = {1}";, p1.x, p1.y);
Console.ReadLine();
}
}
W wyniku wykonania programu powinniśmy zobaczyć na ekranie następujący wynik:
Przed wykonaniem CopyReference: p1.x = 100, p1.y = 100
Po wykonaniem CopyReference: p1.x = 0, p1.y = 0
Aby osiągnąć zamierzone działanie wystarczy dodać modyfikator ref, zarówno do definicji metody, jak i do jej wywołania pdobnie jak ma to miejsce w przypadku „out”:
public static void OrigReference(ref Punkt p)
{
p.x = 0;
p.y = 0;
p = new Punkt();
p.x = 200;
p.y = 200;
}
Po wykonaniu tak zmodyfikowanej metody, pola x oraz y obiektu p1 będą przechowywały wartość (200, 200)
AFAIK w Javie takich możliwości nie ma, może konstrukcje takie nie są szczególnie przydatne, ale w .NET w razie czego są i można je wykorzystać co pozytywnie wpływa na moja dotychczasową ocenę tej platformy.
Witam, nazywam się Piotr Doniec, w internecie występuję pod nickami 'pejotr' oraz 'doniczek'. Obecnie jestem studentem 3 roku informatyki na Politechnice Warszawskiej na wydziale Elektroniki i Technik Informacyjnych.