Język C# jest językiem z mocnym nadawaniem typów, oznacza to, że wszystkie używane w nim zmienne i obiekty muszą mieć jawnie zadeklarowany typ danych. C# rozróżnia kilka rodzajów typów danych, między innymi typ wartościowy, który został opisany.
using System;
namespace HelloWorld
{
class Program
{
static double pi = 3.14;
private static void Main(string[] args)
{
int l0;
float liczba1, liczba2 = 4.3f;
Console.WriteLine("pi = " + pi + " w Main");
double wynik = dodaj(l0 = 3, liczba2);
Console.WriteLine("wynik = " + wynik);
Console.WriteLine("l0 = " + l0 + " w Main");
Console.WriteLine("pi = " + pi + " w Main");
Console.ReadLine();
}
public static double dodaj(int l0, float b)
{
l0 += 8;
pi--;
Console.WriteLine("l0 = " + l0 + " w dodaj");
return l0 + b * pi;
}
}
}
Powyższy przykład jest osią tej notatki. Będziemy do niego wracać omawiając poszczególne typy wartości: int
(liczba całkowita), float
(liczba zmiennoprzecinkowa 32-bitowa) oraz double
(liczba zmiennoprzecinkowa 64-bitowa).
Zacznijmy od pierwszej zadeklarowanej zmiennej pi
, która została poprzedzona słowem kluczowym static
. Wcześniejszy wpis Pierwszy program HelloWorld C# prezentował połączenie static
z metodą Main, dzięki czemu dostęp do metody możliwy był bez tworzenia egzemplarza klasy. Połączenie słowa static
z typem wartości lub referencji zapewnia identyczną wartość, lub referencję do obiektu, we wszystkich egzemplarzach klasy.
using System;
namespace HelloWorld
{
class Program
{
private static void Main(string[] args)
{
MyCounter t0 = new MyCounter();
Console.WriteLine("t0 i = " + t0.I);
MyCounter t1 = new MyCounter();
t1.I = 10;
Console.WriteLine("SET t1.I = 10");
Console.WriteLine("t0 i = " + t0.I);
Console.WriteLine("t1 i = " + t1.I);
Console.ReadLine();
}
}
class MyCounter
{
public static int i = 5;
public int I
{
get { return i; }
set { i = value; }
}
}
}
Powyższy przykład obrazuje wykorzystanie statycznej zmiennej, w którym dla egzemplarza t0 klasy MyCounter
wartość zmiennej i domyślnie wynosi 5. Przy tworzeniu obiektu t1 zmieniono wartość zmiennej i na 10. Modyfikacja wartości w obiekcie t1 wpływa również na wartość w obiekcie t0. Warto wspomnieć, że ustawienie wartości odbywa się przy użyciu właściwości obiektu (tutaj znajdziesz więcej informacji).
Wróćmy do naszego głównego przykładu i zmiennej l0 typu int
. Należy pamiętać, aby przed użyciem typów wartości, między innymi typu int
, wykonać przypisanie wartości przed użyciem zmiennej, inaczej kompilator zgłosi błąd. Wartość zmiennej l0 została przypisana wraz z wywołaniem metody dodaj
.
double wynik = dodaj(l0 = 3, liczba2);
Język C# pozwala na deklarowanie kilku zmiennych w jednej instrukcji. Przykładem są zmienne liczba1 oraz liczba2 typu float
. W przykładzie wartość zmiennej liczba2 ustawiono na 4.3, natomiast zmienna liczba1 została wyłącznie zadeklarowana. Jeżeli chcemy, aby wartość obu zmiennych wynosiła 4.3, zmienne należy przypisać do siebie (pokazuje to przykład poniżej). C# traktuje wszystkie liczby niecałkowite jako wartości typu double
. Jeżeli chcemy przypisać wartość do zmiennej typu float
, musimy uzupełnić zapis tej liczby literą F
lub f
.
float liczba1 = liczba2 = 4.3f;
Podobnie jest w przypadku typu decimal
, dla którego przyrostek to M
lub m
. Poprzedzając informację prezentowaną w tabeli typów wartościowych, stosując przyrostek U
lub u
, możemy określić użycie typu wyłącznie dodatniego, przez co zakres wartości typu zostanie przesunięty o wartość ujemną. Na koniec informacja o możliwości zapisu w formie szesnastkowej, którą wystarczy poprzedzić sekwencją znaków 0x
, np. int a = 0xFFF
, czyli 4095.
Warto pamiętać, że kompilator nie pozwala na mieszanie typów. Poniższy przykład nie pozwoli wykonać kompilacji kodu.
float a, double b;
Wielkość liter w nazewnictwie ma znaczenie. Możemy zadeklarować zmienną int alamakota = 123;
oraz int alaMaKota = 123;
. Dla kompilatora są to dwie różne zmienne. Zastosowanie dwóch niemal identycznych nazw zmiennych może doprowadzić do problemów w późniejszej analizie kodu. Nazwa zmiennej powinna zaczynać się od litery bądź podkreślenia. Kompilator nie dopuszcza, aby nazwa rozpoczynała się od cyfry lub znaku specjalnego (cyfry mogą współtworzyć nazwę zmiennej). Warto unikać używania polskich znaków w nazewnictwie zmiennych. W przypadku, gdy nazwa zmiennej brzmi identycznie jak słowo kluczowe, należy poprzedzić ją znakiem @
. Nazwanie zmiennej identycznie jak słowo kluczowe języka nie jest najlepszym pomysłem.
Pamiętaj o tworzeniu "poprawnego kodu", czyli:
alaMaKota
powinny zostać wyróżnione wielką literą dla każdego słowa, z pominięciem pierwszego.Słowa kluczowe języka C#
abstract as base bool
break byte case catch
char checked class const
continue decimal default delegate
do double else enum
event explicit extern false
finally fixed float for
foreach goto if implicit
in int interface internal
is lock long namespace
new null object operator
out override params private
protected public readonly ref
return sbyte sealed short
sizeof stackalloc static string
struct switch this throw
true try typeof uint
ulong unchecked unsafe ushort
using virtual void volatile
while
Kontekstowe słowa języka C#
add alias ascending async
await descending dynamic from
get global group into
join let orderby partial
remove select set value
var where yield
Znający C# czytelnicy zapewne zgłoszą uwagi przy użyciu zmiennej pi. Nazwa zmiennej pi wskazuje na stałą wartość matematyczną. W rzeczywistości deklaracja stałej powinna wyglądać tak:
const double Pi = 3.14;
Słowo kluczowe const
zapewnia niezmienialność wartości w czasie działania programu. Wyłącznie typy wartości mogą zostać oznaczone jako stałe. Jednocześnie należy pamiętać, że wartość stałej ustalana jest w momencie deklarowania zmiennej, kompilator zgłosi błąd przy próbie zmiany wartości stałej. Warto wspomnieć o konwencji nazewnictwa nakazującego zapisywać nazwy stałych od wielkiej litery.
Analizując przedstawiony kod, należy omówić również zasięg poszczególnych zmiennych. Zmienna pi została zadeklarowana wewnątrz klasy (zmienna globalna), przez co widoczna jest we wszystkich metodach klasy. W przypadku gdy zadeklarujemy zmienną pi wewnątrz metody dodaj
, zmienna globalna zostanie praktycznie przysłonięta przez zmienną lokalną metody. Jeżeli chcemy pomimo to dostać się do zmiennej globalnej, musimy podać pełną nazwę zmiennej HelloWorld.Program.Pi
. W ramach jednego zasięgu nie mogą istnieć dwie zmienne o takich samych nazwach. Innym aspektem zasięgu zmiennych jest możliwość wpływania na ich wartość. Zmienna l0 w momencie przekazywania wartości metodzie dodaj ustawiana jest na 3. Natomiast wewnątrz metody, wartość zwiększona jest o 8. Zmiana nie wpływa na wartość zmiennej poza metodą. Do metody trafia wartość, a nie referencja, przez co wszystkie operacje wewnątrz metody nie wpływają na zmienną poza nią. Jeżeli chcemy przekazać referencję musimy użyć słowa kluczowego ref
lub out
. Inaczej jest w przypadku zmiennych globalnych np. pi, modyfikacja wewnątrz metody dodaj wpływa na wartość zmiennej poza nią.
Tabela typów wartości
Typ języka C# Typ środowiska .NET Liczba bitów
---------------------------------------------------------------
bool System.Boolean 1
byte System.Byte 8
sbyte System.SByte 8
short System.Int16 16
ushort System.UInt16 16
int System.Int32 32
uint System.UInt32 32
long System.Int64 64
ulong System.UInt64 64
float System.Single 32
double System.Double 64
decimal System.Decimal 128
char System.Char 16
Deklarując zmienną można naprzemiennie posługiwać się typem języka C# jak i typem środowiska .NET. Metoda GetType zwraca typ środowiska .NET, natomiast sizeof
zwraca rozmiar typu danych.
int liczba0 = 5;
System.Int32 liczba1 = 5;
Console.WriteLine(liczba0.GetType());
Console.WriteLine(sizeof(long) + " bajty");
Innym przykładem typów wartości są struktury. Struktura Point
z przestrzeni nazw System.Drawing
reprezentuje parę współrzędnych "x" i "y", definiując punkt dwuwymiarowy. Zmiana wartości w zmiennej pB nie powoduje zmian wartości zmiennej pA. Oznacza to, że obie zmienne zawierają niezależne kopie swoich wartości. Struktury dokładniej opisane zostały tutaj.
Point pA, pB;
pA = new Point(3, 4);
pB = pA;
Console.WriteLine("pA = " + pA);
Console.WriteLine("pB = " + pB);
pB.X = 10;
pB.Y = 8;
Console.WriteLine("pA = " + pA);
Console.WriteLine("pB = " + pB);
Wszystkie typy wartości języka C# otrzymują domyślną wartość podczas deklaracji. Należy jednak pamiętać, że kompilator nie pozwoli użyć zmiennej przed przypisaniem jej wartości początkowej.
Słowo kluczowe default
umożliwia nadanie wartości domyślnej dla podanego typu.
object x;
x = default(int);
Console.WriteLine("x = " + x);
x = default(bool);
Console.WriteLine("x = " + x);
C# umożliwia użycie wartości "pustych", wymaga to jednak jawnej deklaracji. Do wykorzystania są dwa równoważne warianty: bool? b = null;
oraz Nullable<bool> b = null;
. Pierwszy zapis jest skróconym zapisem drugiego. Wykorzystując wartości puste, warto korzystać z właściwości HasValue, która sprawdza, czy zmienna przechowuje wartość. Oczywiście można użyć operatora porównania if (a == null) { ... }
. Należy pamiętać, aby zabezpieczyć się przed wykorzystaniem takiej zmiennej. Próba odczytania wartości poprzez właściwość Value dla zmiennej bez ustawionej wartości spowoduje powstanie wyjątku. Kompilator zgłosi również błąd przy próbie przypisania wartości zmiennej zerowej do innej zmiennej tego samego typu, która nie jest zmienną zerową. Problem można rozwiązać, wykorzystując operator łączący ??
. Poniższy kod demonstruje działanie. Jeżeli zmienna a
będzie miała wartość null
, zostanie przypisana do niej wartość false
.
bool? a = null;
if (a.HasValue)
{
Console.WriteLine("a = " + a);
}
else
{
Console.WriteLine("zmienna a jest pusta");
a = a ?? false;
Console.WriteLine("a = " + a.Value);
}
Notatka zawiera jeszcze jeden nieomówiony typ string
, czyli łańcuch znaków. Za każdym razem, gdy korzystamy z metody WriteLine
Post jest wprowadzeniem do pierwszych działań z językiem C#. W gruncie rzeczy nie należy się przejmować, jeżeli nie wszystko jest zrozumiałe. To tylko teoria, praktyka jeszcze przed nami w kolejnych notatkach.
Troska Robert