W C#, tak jak w innych językach obiektowych, mamy typy proste i referencyjne.
Do typów prostych należą dane liczbowe takie jak: „int”, „float”, itd., a także typy wyliczeniowe oraz struktury („string” to struktura, a więc jest typem prostym). Przypisując do jednej zmiennej prostej jakąś wartość lub wartość z innej zmiennej prostej, tworzy się kopia. W przypadku referencji nie powstaje kopia danych, lecz kopiuje się tzw. wskaźnik na dane, czyli wskazanie na jakie dane zmienna wskazuje. Spróbuję to zobrazować przykładami.
Najpierw przyjrzyjmy się operacjom na typach prostych.
class
KlasaGlowna
{
static
void Main()
{
int x = 2;
int y = x;
x = x + 8;
System.Console.WriteLine("Wartość
x: " + x);
System.Console.WriteLine("Wartość
y: " + y);
System.Console.ReadLine();
}
}
Na początku przypisujemy wartość 2 do zmiennej “x”, następnie kopię wartości “x” (w zasadzie wartości 2 znajdującej się w zmiennej “x”) zmiennej “y”. Na koniec zwiększamy wartość zmiennej „x” o 8 i wypisujemy zawartości obu zmiennych na ekranie.
Widać, że zmiana wartości
zmiennej „x” nie miała wpływu na wartość zmiennej „y”.
Zupełnie inaczej jest w przypadku zmiennych referencyjnych.
class
KlasaPomocnicza
{
public int x;
}
class KlasaGlowna
{
static
void Main()
{
KlasaPomocnicza
obiekt1 = new KlasaPomocnicza();
KlasaPomocnicza
obiekt2 = new KlasaPomocnicza();
obiekt1.x = 2;
obiekt2 = obiekt1;
obiekt1.x = obiekt1.x + 8;
System.Console.WriteLine("Wartość
x w obiekcie pierwszym: " + obiekt1.x);
System.Console.WriteLine("Wartość
x w obiekcie drugim: " + obiekt2.x);
System.Console.ReadLine();
}
}
Wynik wykonania powyższego programu może być zaskakujący.
Zwiększaliśmy wartość „x” tylko w obiekcie pierwszym, jednak zwiększyła się też wartość „x” w obiekcie drugim. Stało się tak dlatego, ponieważ obiekty to typy referencyjne. W linijce
obiekt2
= obiekt1;
określiliśmy, że obydwa obiekty wskazują na to samo miejsce w pamięci, a więc każda zmiana danych wskazywanych przez jeden z obiektów, powoduje zmianę danych wskazywanych przez drugi (jest to oczywistem, ponieważ są to te same dane).
@STRONA@Rzutowanie
Typy „z tej samej rodziny” możemy rzutować jeden na drugi. Np. typ „long” na „int”
class
KlasaGlowna
{
static
void Main()
{
long x = 2;
int y = (int)x;
}
}
lub „int” na „long”
class
KlasaGlowna
{
static
void Main()
{
int x = 2;
long y = (long)x;
}
}
Jednak pierwszy przypadek jest niepoprawny, ponieważ typ „long” jest dokładniejszy od „int” i podczas rzutowania możemy utracić poprawność danych.
Konwersja między typami
Konwersja służy do konwertowania typów z jednego na drugi. W części kursu poświęconej łańcuchom znakowym konwertowaliśmy wartości liczbowe na typ „string” podczas łączenia łańcuchów za pomocą operatora „+”. Była to konwersja niejawna, czyli taka, której sami nie wywołaliśmy. Kompilator sam rozpoznał, że musi skonwertować typ liczbowy na „string”. Napiszmy krótki program konwertujący łańcuch znakowy na typ liczbowy.
class
KlasaGlowna
{
static
void Main()
{
System.Console.WriteLine("Podaj liczbę.");
string napis = System.Console.ReadLine();
int x = int.Parse(napis);
int wynik =
x * 2;
System.Console.WriteLine("Liczba " + x + "
pomnożona przez 2 " + "równa się
" + wynik);
System.Console.ReadLine();
}
}
„Int” posiada statyczną metodę „Parse(string parametr)”, która jako parametr przyjmuje łańcuch znakowy, który następnie konwertuje na typ całkowitoliczbowy.
Jeśli łańcuch znakowy nie będzie liczbą, (np. podamy zamiast
liczby literę „s”) otrzymamy błąd podczas wykonania programu.
W .NET mamy bardzo przydatna klasę „Convert”, która posiada
wiele metod statycznych do konwersji typów. Działanie tych metod możemy
zobrazować poniższym przykładem.
class
KlasaGlowna
{
static
void Main()
{
System.Console.WriteLine("Podaj liczbę.");
string napis = System.Console.ReadLine();
int x = System.Convert.ToInt32(napis);
int wynik = x * 2;
System.Console.WriteLine("Liczba " + x + "
pomnożona przez 2 " + "równa się
" + wynik);
System.Console.ReadLine();
}
}
Wynik jaki otrzymamy powinien być taki sam, jak w poprzednim programie.
@STRONA@Proste typy danych w C#
Warto poznać kilka z wbudowanych prostych typów danych w C#.
bool – typ logiczny, oznaczający prawdę („true”) lub
fałsz („false”), używany do operacji logicznych,
short – 16-bitowa liczba całkowita,
int – 32-bitowa liczba całkowita,
long – 64-bitowa liczba całkowita,
float – 32-bitowa liczba zmiennoprzecinkowa,
double – 64-bitowa liczba zmiennoprzecinkowa,
char – pojedynczy znak zapisany w formacie Unicode,
string – łańcuch znakowy, zbiór znaków w formacie
Unicode.
Stałe
Stałe to dane, których wartości nie możemy zmieniać. W pewnym uproszczeniu możemy sobie wyobrazić, że są to zmienne, których wartości są zawsze takie same. Ze stałymi związane jest słówko „const”, które definiuje nam stałą.
class
KlasaGlowna
{
public
const double PI
= 3.14;
public
const double E
= 2.74;
static
void Main()
{
System.Console.WriteLine("Liczba PI ma warość: " + PI);
System.Console.WriteLine("Liczba e ma wartość: " + E);
System.Console.ReadLine();
}
}
Wygodniej jest stworzyć klasę ze stałymi, którą możemy wykorzystać w wielu innych klasach (a nawet programach).
class
Liczby
{
public
const double PI
= 3.14;
public
const double E
= 2.74;
}
class KlasaGlowna
{
static
void Main()
{
System.Console.WriteLine("Liczba
PI ma warość: " + Liczby.PI);
System.Console.WriteLine("Liczba
e ma wartość: " + Liczby.E);
System.Console.ReadLine();
}
}
Ponieważ „PI” oraz „E” są stałymi, nie musimy tworzyć
obiektów klasy „Liczby” – wartości tych stałych będą zawsze dostępne i
niezmienne.
A oto wynik działania obydwu powyższych programów.