Język Csharp dla programistów Delphi

Adam Boduch

C# jest obiektowym językiem programowania, rozwijanym przez firmę Microsoft, jako część inicjatywy .NET. Język C# powstał na bazie już istniejących ? Javy oraz C++. Dzięki temu składnia C# jest podobna do składni wspomnianych języków, co powinno ułatwić jego naukę osobom, które programowały wcześniej w Javie lub C/C++.

1 C# w Delphi
2 Biblioteka klas
3 Podstawowa składnia
     3.1 C# jako język obiektowy
     3.2 Metoda Main()
4 Zmienne
     4.3 Deklaracja kilku zmiennych
     4.4 Przydział danych
5 Stałe
6 Tablice
     6.5 Tablice wielowymiarowe
7 Operatory
     7.6 Operatory inkrementacji
     7.7 Operatory przypisania
     7.8 Operator rzutowania
8 Instrukcje warunkowe
     8.9 Instrukcja if
     8.10 Instrukcja switch
     8.11 Operator trójoperandowy
9 Metody w C#
     9.12 Parametry metod
     9.13 Przekazywanie parametrów
10 Pętle
     10.14 Pętla for
     10.15 Pętla foreach
     10.16 Pętla while
     10.17 Pętla do..while
11 Klasy
     11.18 Tworzenie instancji klasy
     11.19 Metody
     11.20 Pola
     11.21 Konstruktor
     11.22 Właściwości
12 Struktury

Niniejszy poradnik opisuje podstawową składnię języka C# , szczególnie przydatny dla programistów Delphi, którzy nie mieli wcześniej styczności z C#, C/C++ lub Java.

C# w Delphi

Jedną z nowości Delphi 2005 jest możliwość pisania aplikacji w języku C#. W tym miejscu należy zaznaczyć, że kompilator języka C# jest udostępniany darmowo w pakiecie .NET Framework SDK.

Kompilator ów nosi nazwę csc.exe i jest uruchamiany z linii poleceń trybu konsoli.

Wraz z pojawieniem się na rynku Delphi 8, Borland rozpoczął sprzedaż nowego narzędzia, służącego do projektowania aplikacji w C# ? Borland C# Builder. Pakiet ów umożliwiał proste projektowanie aplikacji ASP.NET, usług sieciowych czy programów Windows Forms, oczywiście, z użyciem języka C#. Teraz postanowiono jego funkcjonalność włączyć do pakietu Delphi 2005.

Nowe projekty C# można utworzyć wykorzystując okno Repozytorium (File/New/Other), a następnie klikając myszą kategorię C# Projects. Warto zwrócić uwagę, że w Delphi 2005 istnieje możliwość tworzenia usług sieciowych, kontrolek oraz aplikacji ASP.NET z użyciem języka C#.

Biblioteka klas

Należy pamiętać o tym, że platforma .NET Framework udostępnia programistom bibliotekę klas. Klasy .NET można wykorzystać w dowolnym języku programowania, obsługiwanym przez .NET, czyli m.in. Delphi i C#. Wobec tego działanie biblioteki Windows Forms jest identyczne, niezależne od tego, czy korzystamy z Delphi, czy C#.

Przykładowo, aby w aplikacji konsolowej wstrzymać działanie programu do czasu naciśnięcia przez użytkownika klawisza Enter, skorzystamy z metody ReadLine, z klasy Console. Zarówno w C#, jak i w Delphi poniższy kod będzie prawidłowy:

Console.ReadLine();

Tutaj uwidacznia się pierwsza wspólna cecha obydwu języków. Do pól oraz metod obiektu odwołujemy się za pomocą operatora kropki (.).

Podstawowa składnia

Utwórzmy nowy projekt aplikacji konsolowej C#. W tym celu można wybrać z menu File pozycję New, a następnie Other. W repozytorium zaznacza się grupę C# Projects, a następnie Console Application. Przed utworzeniem projektu, Delphi wyświetla zapytanie, gdzie należy zapisać wymagane pliki. Można także nadać nazwę swojemu projektowi ? np. Csharp. W tym momencie Delphi utworzy nowy projekt oraz umieści w pliku źródłowym szkielet kodu (listing 1).

Listing 1. Szkielet programu C#

using System;

namespace Csharp
{
  /// <summary>
  /// Summary description for Class.
  /// </summary>
  class Class
  {
         /// <summary>
         /// The main entry point for the application.
         /// </summary>
         [STAThread]
         static void Main(string[] args)
         {
                //
                // TODO: Add code to start application here
                //
          }
  }
} 

Na pierwszy rzut oka można zauważyć nawiasy klamrowe: { i }, które w C#, podobnie zresztą jak w C++ czy Javie, rozpoczynają i kończą blok. Owe klamry są odpowiednikami słów kluczowych Begin i End w Delphi.

Pierwszy wiersz kodu powoduje włączenie do programu przestrzeni System. Przestrzenie nazw włącza się do programu za pomocą słowa kluczowego Using:

using System;
using System.Security;
using System.Security.Permissions;

Można powiedzieć, że jest to odpowiednik słowa kluczowego Uses z Delphi.

W dalszej części programu znajduje się deklaracja przestrzeni nazw (słowo kluczowe Namespace), ale nie jest to wymagana część aplikacji. Można więc bezproblemowo taką deklarację usunąć ze swojego programu.

C# jako język obiektowy

Zauważmy, że po utworzeniu nowego projektu Delphi wygenerował plik źródłowy C# z jedną przestrzenią nazw, klasą oraz metodą Main(), zawartą w klasie. Program C# musi mieć jedną klasę, a w niej metodę Main(), która jest początkiem oraz końcem programu.

Pisząc o metodach lub funkcjach C#, stosuję zapis z użyciem nawiasów, czyli np. ReadLine(), a nie ReadLine. Pomimo iż metoda ReadLine() nie posiada żadnych parametrów, wywołując ją nie można zapominać o nawiasach. W przeciwnym przypadku kompilator C# zgłosi błąd: [C# Error] Class.cs(8): Only assignment, call, increment, decrement, and new object expressions can be used as a statement.

Nazwa klasy nie ma znaczenia. Jeżeli pominiemy deklarację klasy i pozostawimy jedynie metodę Main(), kompilator wyświetli komunikat o błędzie: [C# Error] Class.cs(5): A namespace does not directly contain members such as fields or methods.

Po uruchomieniu aplikacji na samym początku jest wykonywany kod zawarty właśnie w metodzie Main(). Jest to obowiązkowy wymóg każdego programu. W przypadku braku metody Main() w programie kompilator wygeneruje komunikat o błędzie [C# Error] Program 'Csharp.exe' does not have an entry point defined.

Kolejną zasadą jest fakt, że w programie może istnieć tylko jedna statyczna metoda Main(). W przypadku uwzględnienia w programie dwóch takich metod Main(), nawet jeżeli zostaną umieszczone w różnych klasach (co pokazuje poniższy listing), kompilator nie zezwoli na kompilację takiego programu i zakomunikuje o błędzie: [C# Error] Class.cs(5): 'Csharp.exe' has more than one entry point defined: 'MyClass.Main(string[])'.

using System;

class MyClass
{
  static void Main(string[] args)
  {
         Console.Write("Hello World");
         Console.ReadLine();
  }
}

class OtherClass
{
  static void Main()
  {

  }
}

Zauważmy, że metoda Main() w klasie OtherClass jest zadeklarowana jako statyczna. Jeżeli usuniemy słowo kluczowe Static, program zostanie skompilowany prawidłowo.

Podsumowując:
*każdy program C# musi posiadać co najmniej jedną klasę,
*w każdym programie musi być metoda Main() zadeklarowana jako statyczna,
*w programie może być wiele metod Main() w różnych klasach, jednakże pod warunkiem, że tylko jedna będzie zadeklarowana jako statyczna.

W tym miejscu chciałbym zaznaczyć, że w odróżnieniu od Delphi, w języku C# wielkość znaków jest istotna. Funkcja Main() nie jest więc dla kompilatora tym samym co funkcja main().

Metoda Main()

Zwróćmy uwagę na nagłówek metody Main():

static void Main(string[] args)

Powyższa deklaracja składa się z kilku elementów. Na samym początku znalazło się słowo kluczowe Static. Mówi ono, że metoda jest statyczna. W Delphi obowiązuje identyczne słowo kluczowe określające metodę statyczną.

Kolejnym słowem kluczowym, znajdującym się w deklaracji jest void. Oznacza ono, że funkcja (właściwie to metoda) Main() nie zwraca żadnych danych. Teraz jednak nie musisz się nad tym zastanawiać, ponieważ te zagadnienia zostaną omówione w dalszej części.

Dalej oczywiście jest nazwa metody oraz parametry, podobnie jak w Delphi ? wypisane w nawiasach. W tym przypadku parametrem jest tablica args typu String. W momencie uruchamiania aplikacji konsolowej w trybie konsoli, użytkownik może przekazać aplikacji pewne parametry w wierszu poleceń ? np.:

CSharp Adam

W tym przypadku do programu przekazałem jeden parametr ? Adam. Najlepiej to przeanalizować na przykładzie. Na listingu 2 zaprezentowano przykładowy program, wykorzystujący wartości przekazane z linii poleceń.

Listing 2. Przykładowy program korzystający z argumentów z linii poleceń

using System;

class MyClass
{
  static void Main(string[] args)
  {
         if ( args.Length > 0 )
         {
                Console.WriteLine("Cześć " + args[0] + "! Jestem C#!");
         }
         else
         {
                Console.WriteLine("Podaj swoje imię");
         }
         Console.ReadLine();
  }
}

Skompilujmy teraz ten program i uruchommy terminal konsoli. Przekazując parametry do aplikacji należy oddzielić je spacjami. Do programu można przekazać wiele parametrów, z których każdy zostanie umieszczony w kolejnym elemencie tablicy args (pierwszy element jest umieszczony pod indeksem 0). Rysunek 1 prezentuje działanie takiego programu.

csharp_dla_delphi.jpg
Rysunek 1. Przekazanie parametrów do aplikacji

Deklaracja metody Main() niekoniecznie musi zawierać parametry. Nie jest to obowiązkowy wymóg. Równie dobrze można zmienić deklarację metody na taką:

static void Main()

W takim przypadku program z listingu 2 nie będzie działał poprawnie, gdyż kompilator nie rozpozna wartości args. W tym momencie należy też dopowiedzieć, iż domyślna nazwa tablicy args nie jest żadnym wymogiem. Można zatem metodę zadeklarować w ten sposób:

static void Main(string[] attrbiutes)

Należy jednak pamiętać o zastąpieniu odwołania do tablicy args słowem attributes.

Kolejną ciekawostką jest fakt, że w deklaracji metody Main() słowo kluczowe void może zostać zastąpione słowem Int. O tym jednak będzie mowa nieco dalej. Tak więc do tego, co powiedzieliśmy sobie wcześniej, należy dodać kolejne informacje:
*metoda Main() nie musi mieć parametrów,
*metoda Main() nie musi zwracać wartości pustej (void).

Zmienne

Ucząc się C# musisz mieć pojęcie czym jet CLR, CLS czy CTS. Typ Integer właściwie odpowiada typowi System.Int32 w .NET. W języku C# deklarując liczbę rzeczywistą można użyć typu System.Int32 lub Int ? nie ma to żadnego znaczenia. Wszystkie poniższe deklaracje zmiennych będą więc prawidłowe:

string MyName;
int MyAge;
float MyFoo;

// to też jest prawidłowy zapis
System.Int32 MyInteger;
System.String MyString;

Podobnie jak typ Integer został zachowany w Delphi ze względów kompatybilności, tak np. typ int został zachowany z myślą o programistach używających wcześniej języka C/C++.

Zwróćmy uwagę na to, że w C# deklarowanie zmiennych odbywa się w całkowicie inny sposób. Na samym początku podaje się typ zmiennej, a dopiero później jej nazwę, czyli zupełnie odmiennie niż w Delphi. Nie ma także charakterystycznego dla Delphi słowa kluczowego Var, czy też dwukropka oddzielającego nazwę zmiennej od określenia jej typu.

Tabela 1 prezentuje typy języka C#, ich odpowiedniki w Delphi oraz typy platformy .NET Framework.

Tabela 1. Typy jęczka C#

Typ C#Typ DelphiTyp .NET Framework
stringStringSystem.String
boolBooleanSystem.Boolean
charCharSystem.Char
doubleDoubleSystem.Double
intIntegerSystem.Int32
shortSmallintSystem.Int16
ushortWordSystem.UInt16
ulongInt64System.Int64
byteByteSystem.Byte

Wydaje mi się ze nazwy typów zmiennych są na tyle podobne, że nie będziesz miał problemu z ich opanowaniem.

Zmienne języka C# mogą być deklarowane w dowolnym miejscu programu, a nie jak to jest w Delphi ? jedynie w bloku var. Jest to wygodne, gdyż zadeklarować zmienną można wtedy, gdy jest ona rzeczywiście potrzebna.

Deklaracja kilku zmiennych

Aby w Delphi zadeklarować kilka zmiennych jednego typu, należy po prostu oddzielać je przecinkami w następujący sposób:

var
  MyFoo, MyFoo1 : Single;

W C# wygląda to bardzo podobnie:

int MyAge, MyAge1;
float MyFoo, MyFoo1;

Opisywane przeze mnie duże różnice w sposobach deklaracji zmiennych w Delphi i C# są dość proste do opanowania. Naturalnym jest, że na początku omyłki są możliwe, lecz po dłuższym programowaniu w języku C# lub w języku o podobnej składni (C++, Java, PHP) nabiera się przyzwyczajenia.

Przydział danych

Podobnie jak w Delphi, przypisanie wartości do zmiennej w trakcie jej deklaracji jest możliwe przy użyciu operatora = ? np.:

string MyName = "Adam Boduch";

Należy zwrócić uwagę, że ciągi znaków w C# są ograniczane cudzysłowami, a nie jak w Delphi ? apostrofami.

Kolejną istotną sprawą jest operator przypisania, czyli := w Delphi. W C# taki operator nie obowiązuje, a używa się po prostu znaku równania:

static void Main(string[] attrbiutes)
{
    string MyName = "Adam Boduch";

    MyName = "Adam Kowalski";
    Console.WriteLine(MyName);
}

Stałe

W C# można deklarować stałe, używając do tego słowa kluczowego Const. Takie stałe muszą być jednak członkami klasy. Muszą być więc zadeklarowane jako pole klasy lub zmienne lokalne ? np.:

static void Main(string[] attrbiutes)
{
    const string MyName = "Adam Boduch";

    Console.WriteLine(MyName);
}

W C# używa się również czegoś takiego jak preprocesor, który jest obecny również w C++. Jest to bardziej zaawansowane narzędzie. W trakcie kompilacji, a raczej tuż przed nią, preprocesor dokonuje analizy kodu źródłowego i umożliwia wykonanie dodatkowych instrukcji. Innymi słowy, jest to program dokonujący wstępnej obróbki kodu źródłowego przed kompilacją.

Skoro wspomniano już o preprocesorze, można zaprezentować prosty program z jego użyciem, którego kod znajduje się na listingu 3.

Listing 3. Program działający z użyciem preprocesora

#define DEBUG

using System;

class MyClass
{
   static void Main(string[] attrbiutes)
   {
         Console.WriteLine("Hello World!");

         #if (DEBUG)
            Console.WriteLine("Debug mode on");
          #else
             Console.WriteLine("Debug mode off");
          #endif

          Console.ReadLine();
   }
}

Instrukcje preprocesora deklaruje się poprzedzając je znakiem #. Na samym początku programu znajduje się stała preprocesora o nazwie DEBUG (inaczej nazwyany - symbolem). Następnie w metodzie Main() znajdują się instrukcje warunkowe preprocesora. Dzięki nim, w przypadku zadeklarowania stałej DEBUG, w programie zostanie wyświetlony napis Debug mode on, a w przeciwnym przypadku ? Debug mode off.

Preprocesor to bardzo powszechnie używane narzędzie. Posiada o wiele więcej możliwości. Po szczegóły odsyłam do fachowej literatury, poświęconej programowaniu w C/C++ lub bezpośrednio do dokumentacji języka C#.

Tablice

Tablice są elementem, który może przysporzyć najwięcej problemów osobie, która chce nauczyć się C#. Ich deklaracja bowiem zupełnie odbiega od tego, co istnieje w Delphi. Deklaracja sama w sobie nie jest trudna:

tablica w C# jest deklarowana z użyciem nawiasów kwadratowych [], czyli:

string[] MyAry;

W tym momencie MyAry jest tablicą o nieokreślonej liczbie elementów typu String. Przed przydzieleniem danych do konkretnych elementów, należy taką tablicę zainicjalizować, a służy do tego operator new:

string[] MyAry;
MyAry = new string[5];

W tym momencie tablica MyAry posiada 6 elementów, do których możemy przypisać dane:

  string[] MyAry;
  MyAry = new string[5];

  MyAry[0] = "Zero";
  MyAry[1] = "Jeden";

Zapis deklaracji tablicy można skrócić stosując taki zapis:

string[] MyAry = new string[5];

Tablice w C# (zresztą jak w całej platformie .NET Framework) wywodzą się z typu referencyjnego System.Array.

Podobnie jak w Delphi, w C# istnieje możliwość deklarowania tablicy wraz z elementami startowymi:

string[] MyAry = new string[]
    {"Zero", "Jeden", "Dwa", "Trzy", "Cztery", "Pięć"};

Tablice wielowymiarowe

Tablice wielowymiarowe deklaruje się poprzez podanie liczby elementów wymiarów, podobnie jak to ma miejsce w Delphi. Oto przykład zadeklarowania tablicy 2´2 w C#:

  int[,] MyAry = new int[2, 2];

  MyAry[0,0] = 1;
  MyAry[0,1] = 2;

Oczywiście nic nie stoi na przeszkodzie, aby zadeklarować trójwymiarową tablicę:

  int[,,] MyAry = new int[2, 2, 3];

  MyAry[0,0,0] = 1;
  MyAry[0,1,0] = 2;

W powyższym przykładzie tablica MyAry będzie miała 2´2´3 elementów.

Operatory

Jak nietrudno się domyśleć, operatory w języku C# są podobne do tych z C/C++ czy Javy lub PHP. Większość operatorów jest podobna do tych z Delphi. W zasadzie najpopularniejszą pułapką czyhającą na programistów Delphi jest operator porównania (=), który w C# pełni rolę operatora przypisania. Aby porównać wartości dwóch zmiennych w C#, trzeba użyć operatora ==:

int x, y;

x = 2;
y = 3;

if (x == y)
{
}

Jeżeli zamiast == w powyższym przykładzie użyłbyś operatora =, kompilator wygeneruje komunikat o błędzie: [C# Error] Class.cs(14): Cannot implicitly convert type 'int' to 'bool'.

Porównanie operatorów języka Delphi oraz C# prezentuje tabela 2.

Tabela 2. Porównanie operatorów C# i Delphi

OpisDelphiOdpowiednik w C#
Operator dostępu..
Łączenie ciągów znakowych++
Dodawanie++
Odejmowanie--
Mnożenie**
Dzielenie//
Dzielenie z ucięciem resztydiv/
Modulo (reszta z dzielenia)mod%
Operator inkrementacjibrak++
Operator dekrementacjibrak--
Wartość prawdziwatruetrue
Wartość fałszywafalsefalse
Operator "i"and&&
Operator "lub"or||
Zaprzeczenienot!
Koniunkcja bitowaand&
Zaprzeczenie bitowenot~
Alternatywaor |
Dysjunkcjaxor^
Przesunięcie w lewoshl<<
Przesunięcie w prawoshr>>
Operator większości>>
Operator mniejszości<<
Większe lub równe>=>=
Mniejsze lub równe<=<=
Operator nierówności!=
Operator porównania===
Operator przypisania:==

Operatory inkrementacji

Język C# posiada więcej operatorów niż Delphi. Są one wzorowane na języku C i wbrew pozorom ułatwiają pracę. W Delphi, aby zwiększyć wartość zmiennej o jeden, stosujemy funkcje Inc lub Dec:

X := 10;
Inc(X); // zwiększenie o 1
Dec(X); // zmniejszenie o 1

W C# można stosować operatory ++ oraz --:

int x = 10;

++x; // zwiększ o 1
--x; // zmniejsz o 1

Operatory ++ oraz -- mogą być użyte przed lub po nazwie zmiennej. Np.:

++x;
x++;

Obie formy są prawidłowe. Czym one się różnią od siebie? W pierwszym przypadku operator ++ najpierw zwiększa wartość zmiennej x o 1, a dopiero później zwraca jej wartość. Operator taki jest nazywany operatorem pre-inkrementacji. Drugi przypadek to operator post-inkrementacji, który najpierw zwraca dotychczasową wartość zmiennej x, a dopiero później zwiększa ją o 1. Aby to lepiej zrozumieć, spróbujmy uruchomić program przedstawiony poniżej:

using System;

class MyClass
{
  static void Main(string[] attrbiutes)
  {
         Console.WriteLine("Hello World!");

         int x = 10;

         Console.WriteLine(Convert.ToString(x++));
         Console.WriteLine(Convert.ToString(++x));
         Console.ReadLine();
  }
}

W wyniku jego uruchomienia na ekranie konsoli pojawi się:

Hello World!
10
12

Operatory przypisania

W Delphi istnieje jeden operator przypisania (=). W C# obowiązuje ich o wiele więcej i często są nazywane operatorami ?zrób i przypisz?. Umożliwiają wykonanie działania arytmetycznego lub bitowego, po czym zwracają wartość. Przyjrzyjmy się poniższemu fragmentowi kodu:

int x = 10;

x += 10;

Console.WriteLine(Convert.ToString(x));

Teraz warto zastanowić się, jaka liczba zostanie wyświetlona na ekranie? Odpowiedź brzmi: 20. Jak to się stało? Otóż operator += spowodował inkrementację wartości zmiennej x o 10. Operator = może być łączony z innymi ? np. operatorem mnożenia (*), dzielenia (/) czy działań bitowych. Oto kolejny przykład:

int x = 10;
int y = 0;

y = 5 + (x *= 10) / 2;

W tym przypadku najpierw wartość zmiennej x została pomnożona przez 10, a następnie podzielona przez dwa. Na końcu do liczby 50 została dodana cyfra 5.

Operator rzutowania

W Delphi rzutowanie odbywa się poprzez ?boksowanie? typu rzutowanego ? np.:

var
  C : Char;
  B : Byte;
begin
  B := 65;
  C := Char(B);
end;

W C# do tego celu wykorzystujemy nawiasy ( ) i wygląda to tak:

double x = 55.5;
int y;

y = (int)x; 

W tym przypadku wartość zmiennoprzecinkowa (55.5) zostanie rzutowana na typ Int. W rezultacie takiej operacji do zmiennej y zostanie przypisana liczba 55.

Instrukcje warunkowe

Język C# oferuje, podobnie jak Delphi ? dwie podstawowe instrukcje warunkowe: If oraz Switch (odpowiednik Case z Delphi). Dodatkowo, C# oferuje tzw. operator trójoperandowy. Przykłady zastosowania tego operatora zaprezentuję w dalszej części.

Instrukcja if

Programując w C# trzeba pamiętać o kilku zasadach stosowania instrukcji warunkowej If. Przede wszystkim brak słowa kluczowego Then. Instrukcja if w języku C# ma prostszą postać:

if ( warunek ) 
{
  operacje;
}

Kolejnym wymogiem jest stosowanie nawiasów ( i ). W przypadku zastosowania takiej konstrukcji:

if x > 50
{
}

Kompilator wyświetli komunikat o błędzie: [C# Error] Class.cs(14): Syntax error, '(' expected. Prawidłowa konstrukcja wygląda tak:

if (x > 50)
{
}

Poza tymi dwoma warunkami, zastosowanie instrukcji if jest takie samo jak w Delphi. Trzeba jeszcze pamiętać tylko o operatorach logicznych stosowanych w C#, które nieco różnią się od tych z Delphi ? np.:

if ( x > 50 && x < 100 )
{
      Console.Write("Super!");
}

Z kolei instrukcje Else oraz else if stosuje się w identyczny sposób jak w przypadku języka Delphi.

Instrukcja switch

Instrukcja Switch w C# jest odpowiednikiem instrukcji Case z Delphi. Jednak słowo case również jest słowem kluczowym w C#. Spójrzmy na poniższy przykład:

string S;
S = "rybki";

switch ( S ) 
{
    case "pieski":
    Console.WriteLine("Kocham pieski!");
    break;

    case "rybki":
    Console.WriteLine("Uwielbiam rybki!");
    break;

    case "kotki":
    Console.WriteLine("Kotki są fajne!");
    break;    
}

W odróżnieniu od Delphi, w C# możemy stosować ciągi znakowe w instrukcji switch. I tak, w zależności od wartości zmiennej S, na konsoli jest wyświetlany odpowiedni tekst. Zwróćmy jeszcze uwagę na słowo kluczowe Break, które oznacza wyjście z instrukcji.

Operator trójoperandowy

W języku C# obowiązuje dodatkowy operator (dostępny także w językach C/++ czy PHP), który idealnie może zastąpić prostą instrukcję if. Składnia operatora trójoperandowego jest przy tym bardzo przejrzysta. Spójrzmy na poniższy przykład:

int Integer;

Integer = -10;

Integer = Integer < 0 ? Integer * 2 : Integer;

Deklarujemy zmienną Integer typu Int i przypisujemy jej wartość -10. W kolejnym wierszu sprawdzamy, czy wartość zmiennej jest mniejsza od 0. Jeżeli tak ? mnożymy wartość zmiennej Integer.

Składnia operatora trójoperandowego jest następująca:

(warunek) ? (warunek spełniony) : (warunek niespełniony)

Jeżeli warunek zostanie spełniony, są wykonywane instrukcje znajdujące się po znaku ?. W przeciwnym przypadku są realizowane instrukcje po znaku :.

Operator trójoperandowy może być łączony z innymi operatorami języka C#. Przykładowo, poniższy warunek zostanie spełniony, jeżeli wartość zmiennej Integer jest mniejsza od zera a większa od ?50:

Integer = (Integer < 0 && Integer > -50) ? Integer * 2 : Integer;

Metody w C#

W języku C# nie obowiązuje pojęcie procedury. Program może posiadać jedynie funkcje, które mogą zwracać wartość pustą. Ściślej mówiąc, C# jest ściśle obiektowy, więc program może zawierać jedynie metody, które ewentualnie nie zwracają żadnej wartości. Oto najprostsza deklaracja takiej metody:

void Foo()
{
}

Powyższy zapis oznacza, że metoda Foo nie zwraca żadnej wartości, jest więc odpowiednikiem procedury z Delphi. W metodach C# odpowiednikiem słowa kluczowego Result (z Delphi) jest Return. Aby funkcja zwróciła jakąś wartość, musimy zastosować taki zapis:

void Foo()
{
    return 10;
}

Jednak przy próbie kompilacji kompilator wyświetli komunikat o błędzie Since 'Project.Class.Foo()' returns void, a return keyword must not be followed by an object expression, który mówi o tym, że funkcja Foo nie może zwrócić żadnych wartości, gdyż zostało użyte słowo kluczowe void.

Wystarczy jednak taki kod zmienić na następujący, aby program został skompilowany prawidłowo:

int Foo()
{
    return 10;
}

Parametry metod

Składnia parametrów metod w C# jest podobna do tej z Delphi. Tutaj również parametry należy oddzielić przecinkami:

int Foo(int Value, string S)
{
    return Value * Value;
}

Należy jednak pamiętać o charakterystycznej deklaracji zmiennych w C# (w formacie [typ danych] [nazwa zmiennej]).

Funkcje (metody) języka C# nie mogą posiadać parametrów domyślnych tak jak to ma miejsce w Delphi.
Powyższa deklaracja metody Foo jest prawidłowa, jednak w razie próby jej wykorzystania w innej metodzie klasy, program nie zostanie prawidłowo skompilowany. Trzeba użyć jeszcze jednego słowa kluczowego ? Static, które informuje kompilator, że dana metoda jest statyczna. Spójrzmy na poniższy program:

using System;

namespace Project
{
  class Class
  {
         static int Foo(int Value)
         {
                return Value * Value;
         }

         [STAThread]
         static void Main(string[] args)
         {
            int Integer;

            Integer = 10;

            Console.WriteLine(Foo(Integer));
            Console.ReadLine();
         }
  }
}

Jeżeli usuniemy z deklaracji funkcji Foo słowo kluczowe static, kompilator w trakcie kompilacji zgłosi błąd An object reference is required for the nonstatic field, method, or property 'Project.Class.Foo(int)'.

Przekazywanie parametrów

W C# przekazywanie parametrów przez referencję wiąże się z poprzedzeniem nazwy parametru słowem kluczowym ref. Oto przykładowy program:

using System;

namespace Project
{
  class Class
  {
         static void Foo(ref int Value)
         {
                Value = Value * Value;
         }

         [STAThread]
         static void Main(string[] args)
         {
            int Integer;

            Integer = 10;

            Foo(ref Integer);

            Console.WriteLine(Integer);
            Console.ReadLine();
          }
  }
}

Należy zwrócić uwagę na to, iż aby przekazać parametr przez referencję, nie wystarczy użyć słowa kluczowego w deklaracji metody, lecz także w przypadku jej wywołania.

Słowo kluczowe out działa podobnie jak ref. Różnica pomiędzy nimi jest taka, że przekazywana zmienna musi być uprzednio zainicjowana.

Pętle

Pętle są bardzo ważnym, użytecznym i często stosowanym elementem języka programowania. Służą do cyklicznego wykonywania tych samych czynności. W C# mamy do dyspozycji cztery rodzaje pętli: For, Foreach, While oraz do..while.

Pętla for

Pętla for w języku C# działa podobnie jak pętla for z języka Delphi. Można jej użyć w przypadku, gdy jest znana liczba iteracji danej pętli. W C# (podobnie jak C++) pętla for jest bardziej rozbudowana. Spójrzmy na poniższy fragment kodu:

  for (int i = 1; i <= 10; i++)
  {
      Console.WriteLine("Iteracja nr " + i);
  }

W C# pętla for posiada trzy człony, oddzielone od siebie znakiem średnika. W pierwszym członie inicjalizujemy zmienną licznikową, która będzie przechowywała numer iteracji. W tym przykładzie zmienna i została zadeklarowana w nagłówku pętli, lecz można oczywiście zadeklarować ją wcześniej:

int i;

for (i = 1; i <= 10; i++)
{
    Console.WriteLine("Iteracja nr " + i);
}

Drugi człon pętli for określa warunek zakończenia. W naszym przypadku pętla będzie kontynuowana dopóty, dopóki zmienna i będzie mniejsza lub równa liczbie 10.

Trzeci człon pętli określa zachowanie zmiennej i (w tym przypadku wartość zmiennej będzie zwiększana o 1 w trakcie każdej iteracji). W C# istnieje możliwość zwiększania licznika o dowolną ustaloną wartość ? np.:

for (int i = 1; i <= 10; i += 2)
{
    Console.WriteLine("Iteracja nr " + i);
}

W tym przypadku licznik zwiększa się o 2, co spowoduje wykonanie jedynie 5 iteracji pętli.

W Delphi nie istnieje sposób na zwiększanie zmiennej licznikowej o wartość większą od 1. Można oszukać kompilator stosując jedynie wskaźniki.

Aby dokonać iteracji ?od góry do dołu?, tak jak w Delphi ? przy użyciu słowa kluczowego DownTo, należy zapisać prawidłowe warunki pętli:

for (int i = 100; i > 0; i -= 2)
{
     Console.WriteLine("Iteracja nr " + i);
}

Tutaj ustawiono początkową wartość na 100. Warunkiem zakończenia pętli jest osiągnięcie przez zmienną i wartości mniejszej lub równej zeru.

Istnieje możliwość pominięcia członów pętli for. Uzyskuje się w ten sposób tzw. pętlę forever, która nie posiada warunku zakończenia:

for (;;)
{
  // instrukcje
}

Teoretycznie działanie takiej pętli nie zostanie nigdy zakończone. Trzeba więc gdzieś w ciele pętli umieścić instrukcję break, jeżeli chcemy zakończyć jej działanie.

Pętla foreach

Pętla Foreach w C# jest odpowiednikiem nowo dodanej w Delphi 2005 pętli for..in. Doskonale nadaje się do operowania na zbiorach (np. tablicach). Oto przykład użycia pętli foreach:

string[] ary;
ary = new string[5];

ary[0] = "Bogdan";
ary[1] = "Stefan";
ary[2] = "Rysiek";
ary[3] = "Damian";
ary[4] = "Zbigniew";

foreach (string element in ary)
{
      Console.WriteLine(element);
}

W programie zadeklarowałem 5-elementową tablicę typu String. Budowa pętli foreach jest prosta. Pierwszy człon jest nazwą zmiennej, do której zostanie przypisana wartość elementu tablicy, a drugi (po słowie kluczowym In) to nazwa tablicy, na której zostanie przeprowadzona operacja.

Pętla while

Składnia pętli while w C# jest prawie identyczna jak ta stosowana w Delphi. Jedyną różnią jest brak słowa kluczowego Do, obecnego w Delphi:

string[] ary;
ary = new string[5];

ary[0] = "Bogdan";
ary[1] = "Stefan";
ary[2] = "Rysiek";
ary[3] = "Damian";
ary[4] = "Zbigniew";

int i = 0;

while ( i < ary.Length )
{
    Console.WriteLine(ary[i]);
     i++;
}

Użycie tego kodu da taki sam efekt, jak w poprzednim przykładzie (wyświetlenie po kolei elementów tablicy). Pętla będzie wykonywana dopóty, dopóki zmienna i będzie mniejsza niż rozmiar tablicy ary.

Pętla do..while

Pętla do..while jest odpowiednikiem pętli repeat..until z języka Delphi. Różnica w porównaniu do pętli while polega na tym, że ta druga sprawdza warunek zakończenia przed wykonaniem iteracji. W praktyce oznacza to, że w pewnych warunkach może nie zostać wykonana żadna iteracja.
Oto przykład wyświetlania wszystkich elementów tablicy w pętli do..while:

int i = 0;

do
{
     Console.WriteLine(ary[i]);
     i++;
}
while ( i < ary.Length );

Jak widać, stosowanie tej pętli jest bardzo podobne do wykorzystywania pętli repeat..until. Jedyna różnica polega na tym, że zamiast słowa kluczowego repeat stosujemy do, a zamiast until ? while.

Klasy

Klasy są bardzo ważnym elementem w programowaniu w C#. Podobnie jak w Delphi, klasy deklaruje się z użyciem słowa kluczowego Class. Deklaracja klasy w najprostszym przypadku wiąże się z napisaniem jednej linii kodu:

class BaseClass { }

Oznacza to zadeklarowanie nowej klasy o nazwie BaseClass. Jeżeli chcemy utworzyć klasę, która ma dziedziczyć po klasie już istniejącej, wystarczy podać nazwę klasy bazowej po znaku dwukropka:

class MyClass : BaseClass { }

W tym przypadku klasa MyClass dziedziczy po klasie BaseClass. Jeżeli dana klasa ma dziedziczyć klasie oraz implementować interfejs, wystarczy użyć znaku przecinka:

class MyClass : BaseClass, IFoo { } 

Tworzenie instancji klasy

Utworzenie nowej instancji klasy wiąże się z użyciem słowa kluczowego new. Podobnie jak w Delphi, należy utworzyć zmienną wskazującą na daną klasę. Można to zrealizować na dwa sposoby:

MyClass Foo;

Foo = new MyClass();

// lub:
MyClass Foo = new MyClass();

Wydaje mi się, że drugi sposób jest lepszy: w jednej linii deklarujemy zmienną oraz tworzymy instancję klasy. Do elementów klasy (właściwości, pól, metod) odwołujemy się tak samo jak w Delphi:

MyClass Foo = new MyClass();

MessageBox.Show(Foo.ToString());

Należy pamiętać o konieczności stosowania nawiasów w przypadku wywołania konstruktora klasy lub metod.

Metody

Użycie metod i ogólnie ? klas w C# różni się od tego znanego z Delphi. Nie ma czegoś takiego jak definicja funkcji. Po prostu wpisujemy od razu ciało metody:

class BaseClass { }
class MyClass : BaseClass
{
     public string FooMethod()
     {
          return "Hello!";
    }
} 

W tym przykładzie utworzyłem funkcję FooMethod(), która zwraca wartość typu String (w tym przypadku ? napis Hello!). Należy zwrócić uwagę, iż metoda FooMethod() została zadeklarowana jako publiczna (słowo kluczowe Public), dzięki czemu jest możliwe jej użycie poza klasą. W przeciwnym razie (po zadeklarowaniu metody jako private), próba wykorzystania spowoduje zgłoszenie błędu: Project13.MyClass.FooMethod() is inaccessible due to its protection level.

Pola

Pola klasy można deklarować w każdym miejscu klasy ? np.:

class MyClass : BaseClass
{
    public string FooMethod()
    {
        return "Hello!";

        X = Y = 10;
    }

    private int X, Y;
}

Jak widać, tutaj pola X i Y zostały zadeklarowane poniżej metody FooMethod(). Pomimo to w ciele metody przypisujemy wartość do tych metod.

W C# i Delphi obowiązują takie same słowa kluczowe, określające dostępność pól bądź metod, czyli: Private (prywatne), Protected (chronione) oraz Public (publiczne). Nie ma natomiast w C# klauzul strict private oraz strict protected. Trzeba także pamiętać, że odpowiednikiem poziomu strict private w C# jest private. Oznacza to, że żadne metody czy pola zadeklarowane jako private nie są dostępne na zewnątrz klasy, nawet dla modułu, w którym się znajdują.

Konstruktor

W celu utworzenia konstruktora klasy nie trzeba stosować żadnych specjalnych dodatkowych słów kluczowych (jak np. w Delphi ? słowo kluczowe constructor). Wystarczy utworzyć w klasie metodę, która będzie miała taką samą nazwę, co klasa:

public class Point
{
     private string FFoo;

     public Point(string Value)
     {
          FFoo = Value;
    }
}

W tym przypadku podczas tworzenia instancji klasy należy podać parametr typu string:

Point myPoint = new Point("Hello World!");

Konstruktory mogą ulegać przeciążaniu (podobnie jak inne metody klas C#) i w celu wykorzystania tego mechanizmu nie trzeba używać żadnych słów kluczowych (typu Overload):

public Point(string Value)
{
    FFoo = Value;
}

public Point(int Value)
{
    // kod
}

W tym momencie konstruktor został przeciążony. Podczas tworzenia instancji możemy wybrać, czy parametrem konstruktora ma być wartość typu String lub Int.

Właściwości

Deklaracja właściwości klas w języku C# w gruncie rzeczy różni się od deklaracji tychże w Delphi. Tutaj zamiast słów kluczowych Read, Write stosujemy słowa get oraz set. Spójrzmy na poniższy przykład, gdzie zadeklarowałem właściwość Foo:

public class Point
{
    private string FFoo;

    public string Foo
    {
      get
      {
         return FFoo;
      }
    }

    public Point(string Value)
    {
         FFoo = Value;
    }
}

Właściwość Foo będzie pobierała wartość z pola prywatnego FFoo. Użycie takiej klasy może wyglądać np. tak:

Point myPoint = new Point("Hello World!");
Console.WriteLine(myPoint.Foo);

Wykonanie takiego kodu spowoduje wyświetlenie na konsoli napisu Hello World!. Warto zwrócić uwagę, że właściwość Foo jest tylko do odczytu, więc próba przypisania wartości:

myPoint.Foo = "Foo";

zakończy się zgłoszeniem błędu: Property or indexer 'Project14.Point.Foo' cannot be assigned to -- it is read only.

Jeżeli chcemy zadeklarować właściwość zarówno do odczytu, jak i zapisu, wystarczy doprowadzić kod do podobnej postaci:

private string FFoo;

public string Foo
{
  get
  {
     return FFoo;
  }
  set
  {
     FFoo = value;
  }
}

Słowo value oznacza wartość przekazaną do właściwości. Jeżeli więc teraz skorzystamy z poniższego kodu, na konsoli zostanie wyświetlony napis Foo:

Point myPoint = new Point("Hello World!");

myPoint.Foo = "Foo";
Console.WriteLine(myPoint.Foo);

Struktury

Struktury w języku C# są odpowiednikiem rekordów w Delphi. Ze struktur w języku C# korzysta się podobnie jak z klas, z tą różnicą, iż zamiast słowa kluczowego Class używamy Struct:

public struct Point
{

}

Struktury w C# mogą zawierać pola, właściwości, metody oraz konstruktory, jak również i klasy zagnieżdżone. Nie mogą jednak dziedziczyć po innych klasach.

W niektórych przypadkach użycie struktur powoduje bardziej wydajne działanie niż korzystanie z klas. W języku C# możemy wykorzystać tablicę struktur, lecz deklaracja tablicy klas nie będzie już możliwa. Przykładowo, deklaracja tablicy struktur.

4 komentarzy

"Funkcje (metody) języka C# nie mogą posiadać parametrów domyślnych tak jak to ma miejsce w Delphi."
od .NET 4.0. chyba już są parametry domyślne

Zachecam do rozbudowy tego tekstu jak i poprawiania ew. bledow.

Przeczytałem ok. połowy.. wygląda nieźle.
Jednak przyznaję się bez bicia.. nie mam pojęcia o czym mowa w zdaniu:
"Ucząc się C# musisz mieć pojęcie czym jet CLR, CLS czy CTS."
co to za akronimy?