C# 3.0.pdf
(
525 KB
)
Pobierz
45272955 UNPDF
Programowanie
C#
Marcin Kawalerowicz
C# 3.0
ma nic wspólnego z C# 3.0. Trzecia wersja
platformy .NET posiada standardowy (zgod-
ny z normą ECMA-334) kompilator C# 2.0 i właściwie
nie jest niczym innym jak .NET Framework upiększo-
ny zestawem „fundacyjnym” (WWF –
Windows Work-
low Foundation
, WCF –
Windows Communication
Foundation
i WPF –
Windows Presentation Founda-
tion
). Nadciągająca wersja .NET Framework związa-
na z Visual Studio 9 Codename „Orcas” będzie no-
sić dumny numer 3.5 i zawierać wersję 3.0 kompila-
tora C#.
Nowości będzie sporo i praktycznie wszystkie
idą w tym samym kierunku. Budują podstawy po-
zwalające na sprawne wykorzystanie
Language In-
tegrated Query
czyli LINQ. To rozszerzenie .NET
Framework pozwoli na tworzenie zapytań do róż-
nych struktur danych wprost z poziomu języka pro-
gramowania (na początku oprócz C# 3.0 także VB
9.0). Zacznijmy jednak od początku. Przyjrzyjmy się
po kolei wszystkim nowościom kompilatora C# 3.0.
Zaczniemy od nowych sposobów na inicjalizację ty-
pów zmiennych i tablic. Przyjrzymy się typom anoni-
mowym, wyrażeniom lambda i poznamy metody roz-
szerzające, by w końcu dotrzeć do translacji zapytań
oraz drzew wyrażeń. Zatrzymamy się więc w miej-
scu gdzie C# 3.0 bezpośrednio łączy się z LINQ. Po-
staramy się jednak nie wchodzić zbyt głęboko na je-
go tereny, gdyż jest to temat na tyle szeroki, że za-
sługuje na osobny artykuł.
Mono i C# 3.0
W tej chwili trwają pracę nad integracją opensourcowej,
wieloplatformowej implementacji .NET Framework Mo-
no z językiem C# 3.0. Kompilator Mono C# zawiera w tej
chwili pełną implementację C# 1.0 i 2.0 oraz pewne ele-
menty 3.0. Pełna integracja planowana jest w wersji 2.2
zapowiadanej na ostatni kwartał 2007.
lekcje wraz z ich zawartością. Dodatkowo progra-
mista dostaje do ręki rozszerzenie znanych z C#
2.0 anonimowych metod w postaci równie anoni-
mowych typów. Najnowszy kompilator C# jest na
tyle inteligentny, że rozpoznaje dynamicznie typ
lokalnej zmiennej na podstawie wyrażenia je ini-
cjującego. Korzystanie z niejawnej inicjalizacji ty-
pów jest bardzo proste. Tam gdzie normalnie de-
klarowany był typ zmiennej należy wstawić słowo
var
i od razu zainicjować wartość
var s = "test";
var i = 1;
Kompilator sam zorientuje się, że
s
to
string
, a
i
to
int
. Podobnie można postępować z tablicami
var doubles = new double[] { 1.0, 2.0, 3.0 };
I bardziej zaawansowanymi typami
Kierunek dynamika
Wydaje się, że Anders Hejlsberg, główny twórca
C#, stara się polerować najnowszą wersję swoje-
go dziecka tak, by nadać mu połysk zbliżony do
rubinowego. Dzięki temu w C# widać coraz więcej
cech języków dynamicznych takich jak na przy-
kład Ruby. Nie mamy tu jednak w żadnym wypad-
ku rewolucji. C# pozostaje językiem o sztywnej
kontroli typów. Zmiany mogą być jednak postrze-
gane symptomatycznie. W najnowszej wersji C#
można niejawnie inicjować typy zmiennych i tablic,
dynamicznie tworzyć instancje obiektów oraz ko-
var list = new List<int>();
Nic nie stoi również na przeszkodzie, by i typ tablicy
deiniować w sposób niejawny. W takim przypadku
pozwalamy kompilatorowi na samodzielną decyzję w
tej sprawie na podstawie inicjalizujących zmiennych.
Na przykład tak:
var ints = new[] { 1, 2, 3, 4 };
var strings = new[] { "1", "2", "3", "4" };
Trzeba jednak uważać. Następujące operacje są nie-
dozwolone.
Autor pracuje jako główny programista .NET w mona-
chijskiej irmie Trygon Softwareberatung, gdzie to z
upodobaniem majstruje przy procesach wytwarzania
oprogramowania i z determinacją aspiruje do miana
„pragmatycznego programisty”. Prywatnie miłośnik do-
brego kina i zapalony wiosenny biegacz.
Kontakt z autorem:
mkawalerowicz@poczta.onet.pl
var v1;
var v2 = null;
var v3 = { 1, 2, 3 };
i = "i"; //podczas gdy var i = 1;
Równie łatwo tworzy się instancje obiektów. W tym
przypadku można jednak pójść o krok dalej i inicjując
24
www.sdjournal.org Software Developer’s Journal 10/2007
U
stalmy szczegóły: .NET Framework 3.0 nie
C# 3.0
Listing 1.
Liczba zespolona
klasy
Extensions
pokazanej na Listingu 1 można zastosować
następujące operacje:
public
class
ComplexNumber
{
private
double
re
;
public
double
Re
{
get
{
return
re
;
}
set
{
re
=
value
;
}
}
private
double
im
;
public
double
Im
{
get
{
return
im
;
}
set
{
im
=
value
;
}
}
}
double d1 = d.Squere();
double d2 = d.Power(d1);
Tak klasa deiniująca metody rozszerzające jak i same metody
muszą być statyczne. Pierwszy parametr metody zaopatrzony
jest w słowo
this
, po którym deiniowana jest klasę rozszerza-
na (w naszym przypadku
System.Double
). Aby użyć metod roz-
szerzających wystarczy zaimportować przestrzeń nazw, w
której zostały one zdeiniowane (poprzez
using
). Nic nie stoi
na przeszkodzie by jako parametrów użyć zmiennych typowa-
nych (z ang.
generics
). Następującą metodę rozszerzającą
public static void DoNothing<P>(this P t) { }
obiekt wypełnić jego pola. W ten sposób klasę przedstawioną
na Listingu 1 można zainicjować tak:
będzie można użyć do dowolnego typu danych. Będzie trzeba
uważać by nie pogubić się w piekiełku przypominającym ma-
krodeinicje języka C.
var z = new ComplexNumber { Re = 1.1, Im = 2.2 };
Podobnie inicjować można bardziej złożone klasy. I tak prosty
zbiór dwóch liczb zespolonych przedstawiony na Listingu 2
można zainicjować tak:
Wyrażenia lambda
Najpierw była zwykła metoda. Posiadała ona nazwę, mogła
zwracać jakąś wartość, miała parametry o określonym typie, no
i zawierała ciąg instrukcji. Później przyszła metoda anonimowa i,
jak sama nazwa wskazuje, nie musiała już mieć nazwy. Deinio-
wało się ją poprzez słowo
delegate
. Od teraz będziemy mieli wy-
rażenia lambda, które wymagają jeszcze mniej. Żadnego
dele-
gate
, żadnych typów parametrów, właściwie nawet nie potrze-
ba
return
. Wiele funkcjonalnych języków programowania takich
jak Lisp, OCaml czy F# używa wyrażeń lambda do deiniowa-
nia funkcji. W C# wyrażenia te są funkcjonalnym rozszerzeniem
anonimowych metod. Oznacza to, że nie wnosząc większych in-
nowacji co do zasady działania anonimowych metod starają się
wprowadzić ułatwienia w ich zastosowaniu i zapisie. I tak zamiast
var p = new SimpleComplexNumberSet {
Z1 = { Im = 1.1, Re = 2.2 },
Z2 = { Im = 3.3, Re = 4.4 }
};
Nowe dynamiczne cechy języka C# pozwalają na zastoso-
wanie anonimowych typów. Mechanizm ten jest podobny do
znanych z wersji 2.0 anonimowych metod i pozwala na de-
iniowanie typów w locie (ang.
in-line
). I tak następujące de-
klaracje
var person1 = new { LastName = "Kawalerowicz", FirstName =
"Marcin" };
var person2 = new { LastName = "Kawalerowicz", FirstName =
"Sylwia" };
delegate(int x)
{
return x + x;
}
utworzą dwa obiekty o tej samej strukturze. Obiekty te nie będą
miały nazwy, będą dziedziczyć bezpośrednio z
System.Object
i posiadać serię właściwości zapewniających dostęp do pól
klasy (
get/set
). Stosując nowe cechy języka C# można podczas
inicjalizacji kolekcji wypełnić ją zawartością. Na przykład tak:
można po prostu zapisać
x => x + x
Listing 2.
Zbiór dwóch liczb zespolonych
List<int> ib = new List<int> { 0, 1, 1, 2, 3, 5, 8, 13};
public
class
SimpleComplexNumberSet
{
private
ComplexNumber
z1
;
public
ComplexNumber
Z1
{
get
{
return
z1
;
}
set
{
z1
=
value
;
}
}
private
ComplexNumber
z2
;
public
ComplexNumber
Z2
{
get
{
return
z2
;
}
set
{
z2
=
value
;
}
}
}
lub
var cn = new List<ComplexNumber> {
new ComplexNumber { Re = 1.1, Im = 2.2 },
new ComplexNumber { Re = 3.3, Im = 4.4 }
};
Rozszerzamy
Extension methods
(metody rozszerzające) deklarowane
są tak jak metody statyczne ale mogą być wywoływane tak
jakby były „zwykłymi” metodami instancji. I tak po deklaracji
Software Developer’s Journal 10/2007
www.sdjournal.org
25
Programowanie
C#
Jak widać zapis w postaci wyrażenia lambda to nic in-
nego jak lista parametrów po której następują znaki
=>
oraz deinicja działania. Działaniem może być wyrażenie
lub ciąg instrukcji. Parametry mogą być deklarowane nie-
jawnie i jeśli jest ich więcej niż jeden to muszą być za-
mknięte w nawiasach. Oto przykłady zastosowania wyra-
żeń lambda:
Listing 4.
Deinicja dwóch wyrażeń lambda
Func
<
ComplexNumber
,
double
>
abs
=
z
=>
Math
.
Sqrt
(
z
.
Re
*
z
.
Re
+
z
.
Im
*
z
.
Im
);
Func
<
ComplexNumber
,
double
>
arg
=
z
=>
{
if
(
abs
(
z
)
==
0.0
)
throw
new
ArgumentOutOfRangeException
(
"Argument is
undeined"
);
if
(
z
.
Im
>
0.0
)
return
Math
.
Atan2
(
z
.
Re
,
z
.
Im
);
else
return
-
1
*
Math
.
Atan2
(
z
.
Re
,
z
.
Im
);
}
;
(int x) => x + 1
(int x, int y) => { return x + y; }
W praktyce wyrażenia lambda można używać anonimowo lub
przypisując je delegatowi. Obie formy mogą być używane jako
parametry innych funkcji i wyrażeń. Nasz delegat
Func
będzie
mógł reprezentować każdą funkcję jednoparametrową zwra-
cającą jakąś wartość
tak zapisane wyrażenie lambda można w prosty sposób prze-
chowywać i przetwarzać. Równie proste jest przekształcenie
tak przechowywanych danych na postać wykonywalną
delegate R Func<P, R>(P param);
Można mu przypisać implementacje przedstawione na
Listingu 4, których użyć można w następujący sposób:
var f = data.Compile();
Console.WriteLine(f(1));
abs(new ComplexNumber { Re = 1.1, Im = 2.2 });
arg(new ComplexNumber { Re = 1.1, Im = 2.2 });
Wprowadzenie elementów programowania funkcjonalne-
go do najnowszej wersji C# przyjęte zostało przez nie-
których programistów z rezerwą. Dało się słyszeć głosy,
że mieszanie programowania obiektowego i funkcjonalne-
go jest niezdrowe. Tak czy siak, to co działo się od dawna
w kręgach akademickich przenika do „języków praktyczne-
go zastosowania” (przepraszam wszystkich programistów
OCaml piszących aplikacje biznesowe i mistrzów F# two-
rzących programy użytkowe). Całe zamieszanie ma jed-
Wyrażenia lambda mają jeszcze jedną bardzo przydatną
cechę. Mogą być one mianowicie przekształcane w struk-
tury danych zamiast w standardowy kod pośredni platfor-
my .NET (MSIL)
ExpressionFunc<ComplexNumber, double> abs =
z => Math.Sqrt(z.Re * z.Re + z.Im * z.Im);
Listing 5.
Przykładowe zastosowanie zintegrowanego z
C# języka zapytań
Co to oznacza? Nie mniej nie więcej tyle, że zamiast wy-
konywalnego kodu możemy otrzymać tak zwane drzewo
wyrażeń (ang.
expression tree
), które można sobie wy-
obrazić jak opis działania danego wyrażenia lambda. By
zapisać wyrażenie lambda w postaci drzewa wyrażeń
należy przypisać go do zmiennej typu
Expression<T>
w
ten sposób:
using
System
;
using
System
.
Collections
.
Generic
;
using
System
.
Linq
;
using
System
.
Diagnostics
;
namespace
LinqTest
{
class
Program
{
static
void
Main
(
string
[]
args
)
{
Process
[]
processes
=
Process
.
GetProcesses
();
Expression<Func<int, int>> data = i => i + 1;
Listing 3.
Klasa dodająca dwie metody rozszerzające
public
static
class
Extensions
{
public
static
double
Squere
(
this
double
d
)
{
return
d
*
d
;
}
var
pNames
=
from
p
in
processes
where
p
.
ProcessName
.
StartsWith
(
"S"
)
select
p
.
ProcessName
;
public
static
double
Power
(
this
double
d
,
double
y
)
{
return
Math
.
Pow
(
d
,
y
);
}
foreach
(
string
s
in
pNames
)
{
Console
.
WriteLine
(
s
);
}
}
}
}
}
26
www.sdjournal.org Software Developer’s Journal 10/2007
C# 3.0
W Sieci
stosując go do dowolnego źródła danych. I tak na przykład
nasze zapytanie przetłumaczone zostanie na:
•
http://msdn2.microsoft.com/en-us/vcsharp/aa336745.aspx
•
http://www.mono-project.com/CSharp_Compiler
– kompilator
Mono C#
•
http://msdn2.microsoft.com/en-us/vstudio/aa700830.aspx
–
Visual Studio 2008
var pNames2 = processes
.Where(p => p.ProcessName.StartsWith("S"))
.Select(p => p.ProcessName);
Wygląda znajomo? Tak, to metody rozszerzające z parametrami
w postaci wyrażeń lambda. Naturalnie nic nie stoi na przeszko-
dzie by próbować zaimplementować wzorzec
query expression
pattern
za pomocą delegatów zamiast wyrażeń lambda oraz me-
tod instancji, jako że są one wywoływane w identyczny sposób
jak metody rozszerzające.
nak drugie dno. Przecież z przytoczonych przykładów ja-
sno wynika, że „to wszystko dało się zrobić już wcześniej”.
Wyrażenia lambda i drzewa wyrażeń mają jednak kolosal-
ne znaczenie dla integracji z LINQ. Pozwala ona bowiem
na zapis, tłumaczenie i niezależne przetwarzanie zintegro-
wanych zapytań.
Podsumowanie
C# w wersji 3.0 ma być w założeniu językiem o wiele bardziej dy-
namicznym i ekspresywnym. Nie będzie już kłopotów z żonglo-
waniem kawałkami stringów w celu utworzenia zapytania SQL,
które i tak na końcu okazywało się błędne. Co więcej, sztywną
kontrolę typów i autouzupełnianie będzie można używać w zapy-
taniach do dowolnych struktur danych, a drzewa zapytań umożli-
wią niezależne tworzenie narzędzi obsługujących LINQ. Pewną
trudność może nastręczać dość zawiła składnia, ale nic nie stoi
na przeszkodzie by już teraz zacząć się z nią zaznajamiać. Mi-
crosoft na swoich stronach internetowych udostępnia wersję beta
najnowszego .NET Framework 3.0 zawierającą kompilator C# 3.0
oraz środowisko Visual Studio 2008 „Orcas”. Cóż więcej potrzeba
młodym i odważnym w kolejnej podróży „ku przygodzie”.
n
Sedno sprawy
Tak, krok po kroku, doszliśmy do sedna innowacji w C#
3.0. Teraz pozostając w domenie C# i nie zagłębiając się
zbytnio w „technologię” LINQ, która jest tematem samym
w sobie, odkryjemy tajniki rządzące współpracą LINQ i
C#. Nie jest bowiem tajemnicą, że praktycznie wszelkie
przedstawione powyżej nowości wprowadzone zostały
po to, by język był w stanie wspierać LINQ. Dzięki zinte-
growanemu językowi zapytań (czyli z ang.
Language IN-
tegrated Query
) możliwe jest pisanie podobnych do SQL
zapytań wprost w kodzie C#. Co za tym idzie możliwe jest
bezpośrednie korzystanie z
IntelliSense
oraz automatycz-
nego sprawdzania składni wewnątrz zapytań. Wykorzy-
stanie nowych koncepcji języka C# pozwoliło na unieza-
leżnienie LINQ od konkretnych źródeł danych. Praktycz-
nie rzecz biorąc, możliwe jest pisanie zapytań do dowol-
nych struktur. Mogą to być relacyjne bazy danych (DLI-
NQ), struktury hierarchiczne takie jak XML (XLINQ) czy
nawet kolekcji obiektów umieszczone w pamięci. Niech
za przykład posłuży nam Listing 5. Przedstawione w nim
zapytanie przeiltruje wszystkie działające procesy w sys-
temie i znajdzie te, których nazwy rozpoczynają się od
„S”. Czytelnicy znający SQL z pewnością zwrócą uwagę
na „dziwną” kolejność komend w zapytaniu. Na początku
from
później
where
, a dopiero na końcu
select
. Co tu się
dzieje? Słowem kluczem jest
IntelliSense
, czyli system
uzupełniania kodu w VisualStudio (w innych systemach
zwany z ang.
code completion
). Jeśli
select
byłby, tak jak
się to należy, na początku zapytania, system podpowia-
dania VisualStudio zostałby mocno upośledzony, gdyż
nie byłby w stanie „zgadnąć” co takiego moglibyśmy wy-
selekcjonować.
Zajrzyjmy pod maskę najnowszej wersji C#. Przedsta-
wione na Listingu 5 zapytanie zostanie przetłumaczone
(za pomocą nowej cechy języka C# zwanej translacją za-
pytań –
query expression translation
) na „zwykły” kod C#,
na przykład na zbiór metod implementujących wzorzec
qu-
ery expression pattern
. Wzorzec ten proponuje zbiór me-
tod, na które mogą być tłumaczone zintegrowane zapyta-
nia. Są to między innymi
Where
,
Select
,
Join
,
OrderBy
czy
GroupBy
. Metody te, dla zachowania elastyczności, zaopa-
trzone mogą być w typowane parametry. Nic nie stoi na
przeszkodzie by samemu zaimplementować ten wzorzec
R E K L A M A
Software Developer’s Journal 10/2007
Plik z chomika:
Emkacf
Inne pliki z tego folderu:
WPF in action with Visual Studio 2008.pdf
(12990 KB)
Sekrety języka C#.pdf
(3937 KB)
Język C#- szybki kurs.pdf
(388 KB)
Jezyk_C_Szkola_programowania_jcshpr.pdf
(786 KB)
C#. Programowanie.pdf
(324 KB)
Inne foldery tego chomika:
Heartland hasło - Heartland
Paranormalne
Photoshop
Plotkara [Gossip Girl]
Porady
Zgłoś jeśli
naruszono regulamin