Zmienne w C# są podzielone na następujące rodzaje:
typy wartościowe (value types)
typy referencyjne (reference types)
typy wskaźnikowe (poiner types)
Typy wartościowe
Zmienne typu wartościowego mogą mieć bezpośrednio przypisaną wartość. Dziedziczą one z klasy System.ValueType.
Typy wartościowe zawierają dane. Niektóre z tych typów to: int, char, float, które pozwalają na składowanie numerów, liter czy liczb zmienno-przecinkowych.
Kiedy deklarujemy wartościowy typ danych, system od razu przydziela pamięć potrzebną na przechowywanie tej zmiennej.
Poniższa tabela zawiera listę dostępnych typów wartościowych:
Typ
Reprezentacja
Zakres
Domyślna wartość
bool
typ logiczny
true lub false
false
byte
8-bitowa liczba całkowita
0 do 255
0
char
16-bitowy znak z tablicy Unicode
U +0000 do U +ffff
'\0'
decimal
128-bitowa wartość dziesiętna z dokładnością 28-29 cyfr znaczących
(-7.9 x 1028 do 7.9 x 1028)
/ 100 do 28
0.0M
double
64-bitowa wartość o podwójnej precyzji używana do obliczeń zmiennoprzecinkowych
(+/-)5.0 x 10-324 do (+/-)1.7 x 10308
0.0D
float
32-bitowa wartość o pojedynczej prezycji używana do obliczeń zmiennoprzecinkowych
-3.4 x 1038 do + 3.4 x 1038
0.0F
int
32-bitowa liczba całkowita
-2,147,483,648 do 2,147,483,647
0
long
64-bitowa liczba całkowita
-9,223,372,036,854,775,808 do
9,223,372,036,854,775,807
0L
sbyte
8-bitowa liczba całkowita
-128 do 127
0
short
16-bitowa liczba całkowita
-32,768 do 32,767
0
uint
32-bitowa liczba całkowita (bez możliwości przechowywania liczb ujemnych)
0 do 4,294,967,295
0
ulong
64-bitowa liczba całkowita (bez możliwości przechowywania liczb ujemnych)
0 do 18,446,744,073,709,551,615
0
ushort
16-bitowa liczba całkowita (bez możliwości przechowywania liczb ujemnych)
0 do 65,535
0
W języku C# mamy możliwość sprawdzenia ile pamięci zajmuje dana zmienna. Należy w tym wypadku użyć metody sizeof(type).
Zwrócona zostanie nam wartość zajętej pamięci przez określony typ danych. Wartość ta będzie podana w bajtach.
Oto przykład sprawdzenia rozmiaru w pamięci 32-bitowej liczby całkowitej:
using System;
namespace TypyDanych_WielkoscPamieci
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Rozmiar zmiennej typu int: {0}", sizeof(int));
Console.ReadLine();
// Wynik działania programu: Rozmiar zmiennej typu int: 4
// Powyższa wartość wyrażona w bajtach -> 1 bajt to 8 bitów
// Wynika zatem, że int zajmuje w pamięci 32 bity (zgodność z zaprezentowaną
// wyżej tabelą.
}
}
}
Typy referencyjne
Typy referencyjne nie przechowują rzeczywistych wartości w zmiennej, przechowują referencje do zmiennych.
Innymi słowy, odnoszą się do konkretnej lokalizacji w pamięci. Przykładami wbudowanych typów referencyjnych są: obiekty (objects),
typy dynamiczne (dynamics) oraz string (typ tekstowy).
Typ obiektowy (object type)
Typ obiektowy Object Type jest klasą bazową dla wszystkich typów danych w CTS (Common Type System)
- jeden z bloków składowych platform .NET, który jest odpowiedzialny za opis wszystkich danych udostępnionych przez środowisko uruchomieniowe.
Do typów obiektowych możemy przypisać dowolną wartość, tj. typ wartościowy, typ referencyjny oraz typ zdefiniowany przez użytkownika. Jednakże,
przed przypisaniem wartości należy dokonać odpowiedniej konwersji.
W momencie gdy typ wartościowy jest konwertowany na typ obiektowy mamy doczynienia z operacją boxing.
Jeżeli obiekt jest konwertowany na typ wartościowy mamy doczyniania z operacją unboxing.
// Najpierw boxing - konwertowanie typu wartosciowego na obiekt
int i = 10;
object o = i;
// A teraz zrobimy unboxing
int liczba = (int)o;
Typ dynamiczny (dynamic type)
W zmiennej dynamicznej możemy przechowywać dowolny typ danych. Sprawdzanie typów zmiennych odbywa się w momencie wykonywania programu.
Składnia typów dynamicznych jest następująca: dynamic nazwa_zmiennej = value;
Typy dynamiczne są podobne do typów obiektowych z zaznaczeniem, że sprawdzanie typów dla typów obiektowych odbywa się w trakcie kompilacji, podczas
gdy sprawadzenie typów dla typów dynamicznych odbywa się w trakcie wykonywania programu.
Typ łańcuchowy (String type)
Typ łańcuchowy String Type pozwala przypisać dowolną wartość tekstową do naszej zmiennej.
Wartość może zostać przypisana na jeden z dwóch sposobów: cytowany (quoted) lub @cytowany (
@quoted
). Warto również nadmienić, że string to alias do klasy System.String.
Dziedziczy on z typu obiektowego Object Type.
Przykład:
String tekst = "Witaj drogi Użytkowniku!";
Lub
String tekst = @"Witaj drogi Użytkowniku!";
A jaka w takim razie jest różnica w powyższych zapisach? W drugim przypadku użyliśmy znaku @, którego
zadaniem jest wyłączenie sekwencji ucieczki. Co to dla nas oznacznia? Taki łańcuch znaków traktowany jest jako tzw. dosłowny string i
uwzględnia wszystkie znaki takimi jakimi są. Użycie tego znaku znajduje zastosowanie np. podczas uzyskiwania dostępu do konkretnego
folderu w systemie Windows.
Prosty przykład:
String lokalizacja_1 = "C:\Windows\System32"; // błąd o nierozpoznanej sekwnecji ucieczki
String lokalizacja_2 = @"C:\Windows\System32"; // dysponujemy aliasem do wskazanego folderu
Z czasem pojawi się u Was pytanie, czym się różni Sting vs string?
string, tak jak wyżej zostało wspomniane to alias do System.String.
Teoretycznie więc nie ma różnicy. Jako dobry przewodnik dla programistów i czytelności kodu warto zastosować się do poniższych wskazówek.
string zaleca się używać jeżeli odnosimy do zmiennej:
string tekst = "Witaj";
Jeżeli jednak chcemy odwołać się do klasy, a ściślej metod w niej zaimplementowanych warto skorzystać z poniższego zapisu:
Typy referencyjne zdefiniowane przez użytkownika to: klasy, interfejsy oraz delagaty. Zostaną one omówione w późniejszych rozdziałach.
Typy wskaźnikowe
Zmienne typu wskaźnikowego przechowują w pamięci adres do danego typu. Wskaźniki w C# to po prostu zmienna, która przechowuje ten adres.
W języku C# wskaźnik może trzymać adres jedynie dla typów wartościowych i tablic.
Przykład użycia:
int* ptr;
Deklarujemy wskaźnik zmiennej ptr która składuje adres dla zmiennej typu int.
Operator odniesienia(reference operator) – ‘&’ może być użyty do pobrania adresu w pamięci.
int x = 100;
Wyrażenie &x zwraca nam adres pamięci zmiennej x, którą możemy przypisać
do zmiennej wskaźnikowej.
int *ptr = &x;
Podsumowanie:
Console.WriteLine((int)ptr); // Wyświetla adres pamięci
Console.WriteLine(*ptr); // Wyświetla wartość przechowywaną w pamięci
Należy również zaznaczyć, że używanie wskaźników w języku C# wymaga użycia tzn. unsafe context.
Polecenia takie wykonywane są poza kontrolą Garbage Collector.