BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Nowości w C# 3.0

26-01-2008 16:36 | _Neo_
Artykuł przedstawia nowości jakie niesie ze sobą specyfikacja języka C# 3.0. Przedstawione są nowe elementy języka, ich zastosowanie, jak i wsparcie ze strony środowiska.

Słowo o wersjach .NET

 

Zmiana na wersje .NET 3.5 wprowadza:

·         Service Pack dla wersji .NET Framework 2.0

·         Service Pack dla wersji .NET Framework 3.0

·         Nowe składniki biblioteki .NET Framework 3.5

·         Wsparcie dla języka C# specyfikacji 3.0

 

Zmiany jakie się dokonują w poprzednich wersjach przewidują zazwyczaj niewielkie poprawki. Microsoft zapewnia, że poprawki mają poprawić przede wszystkim wydajność i są maksymalnie kompatybilne z istniejącym kodem.

 

C# 3.0

Słowo kluczowe var.

 

W nowym języku zostało zaproponowane nowe słowo kluczowe var. Stosuje się go do zadeklarowania zmiennej niewiadomego typu. Typ będzie określony na podstawie statycznej analizy kodu. Środowisko programistyczne takie jak Visual Studio w pełni wspiera użycie tego słowa i rozpoznaje typ w czasie pisania kodu. Przykład:

 

[Kod C#]

var hColl = new HashSet<string>();

hColl.Add("one");

 

Stosowanie słowa w programowaniu zwalnia programistę z potrzeby pamiętania jakiego typu argumenty są zwracane przez metody. W niektórych przypadkach użycie tego słowa będzie niezbędne. Takim przypadkiem będzie instancjonowanie obiektu anonimowej klasy, które będzie szczegółowo omówione nieco dalej.

Microsoft jednak uprzedza, zbyt częste korzystanie z takiego mechanizmu zdecydowanie zmniejsza czytelność kodu. Zaleca się zatem stosowanie go z ograniczeniem lub w sytuacjach niezbędnych.

 

 

Konstruktory kolekcji.

 

Nowa specyfikacja języka przewiduje instancjonowanie nie tylko obiektów tablicowych konkretnymi danymi. Prawie każdą kolekcję można instancjonować zbiorem kolejnych wartości lub obiektów. Za przykład może posłużyć nowa kolekcja generyczna w .NET 3.5 typu HashSet<T>:

 

[Kod C#]

var hColl = new HashSet<string>() { "one", "two" };

 

Jest to bardzo wygodny mechanizm, ułatwiający pisanie kodu. Bardzo często programista korzysta z bogatszych kolekcji niż tablic zatem takie instancjonowanie unifikuje je razem pod względem inicjacji wartościami początkowymi.

 

Dla rozwiania ewentualnych wątpliwości dodam, że klasę generyczną można porównać z szablonem z języka C++ o pewnych ograniczeniach. Jej użycie w przypadku kolekcji gwarantuje to że wszystkie jej elementy są określonego typu (lub po nim dziedziczą).

 

 

Konstruktory z listą właściwości.

 

Proces tworzenia nowej klasy zazwyczaj wygląda następująco:

 

·         Tworzona jest nazwa klasy.

·         Uzupełniane są pola klasy i właściwości.

·         Uzupełniane są metody i… pojawia się problem.

 

Problem o którym mowa to stworzenie nowego obiektu tej klasy. Potrzebny jest konstruktor. Programista zazwyczaj zadaje sobie pytanie: Jakie parametry będzie on miał i czy od razu nie napisać ich kilku żeby zaoszczędzić czas na odrywanie się od właściwego kodu. I właśnie w tym momencie przychodzi na pomoc mechanizm samego języka.

Microsoft zauważył że parametry które są przekazywane w konstruktorze są zazwyczaj potem dostępne publicznie. Mechanizm języka pozwala zatem na tworzenie nowego obiektu, podając listę par:

 

< nazwa publicznej właściwości lub pola> <wartość>

 

Posłuży ona do wygenerowania odpowiedniego konstruktora. Przykład:

 

[Kod C#]

Person p = new Person()

{

Id = Guid.NewGuid(),

Name = "Kowalski",

Address = "ul. Kwiatkowskiego 9"

};

 

 

Konstruktory anonimowe.

 

Podpunkt ten wymaga pewnego wprowadzenia. Poprzednie wersje C# przynosły pojęcie Refleksji. Jest to mechanizm dostarczający informacje o budowie klas w czasie wykonania, lub nawet pozwalający na dynamiczne tworzeniu kodu klas. Zyskał on dużą popularność gdyż umożliwia przykładowo tworzenie formularzy, które mogą wyświetlać zawartość obiektów różnego typu, po inspekcji ich budowy. Jest to zapewne świetny mechanizm, niemniej jednak jego używanie łączy się z dość niewielką możliwością kontrolowania, które właściwości, które powinny być wyświetlane, są interesujące z punktu widzenia biznesowego. Istnieje rozwiązanie tego problemu w postaci stosowania własnych atrybutów, niemniej jednak czasem okazuje się to niewystarczające.

Potrzebny jest zatem mechanizm przesłania lub filtracji danych a analogii do widoków znanych z SQL’a. Twórcy języka posunęli się nieco dalej. Umożliwili na dynamiczne tworzenie lekkich klas, anonimowego typu, o określonych właściwościach. Przykład:

 

[Kod C#]

var p = new ()

{

Id = Guid.NewGuid(),

Name = "Kowalski"

};

 

 

W przykładzie zostanie stworzony obiekt anonimowy o podanych właściwościach. Proszę zwrócić uwagę na użycie konstruktora z listą parametrów. Konwencja jest identyczna. Gdy będziemy chcieli się dowiedzieć typu takiego obiektu, okaże się że nazwa jego będzie wyglądała następująca:

 

<>f__AnonymousType0`6

[System.Guid,

System.String]

 

Można zatem z nazwy wprost wyczytać jakie właściwości obiekt przechowuje. Oczywiście zamiast z nazwy zdecydowanie wygodniej jest użyć dalej reflekcji.

 

 

Rozszerzenia klas.

 

W językach programowania takich jak C# czy Java, istnieją ograniczenia co do dziedziczenia. Można dziedziczyć tylko po jednej klasie, ale implementować wiele interfejsów. Co więcej w C# istnieje słówko kluczowe sealed co oznacza że daną klasę nie można już rozszerzać. Co więcej nie da się rozszerzyć klasy tak, żeby zmiany były widoczne w klasach potomnych bez ingerencji w samą klasę. Jest to problem dotyczący klas z samej biblioteki.

 

Konstruktorzy C# 3.0 zaproponowali rozwiązanie. Postulują, można rozszerzyć każdą klasę o metody. Metoda musi korzystać tylko ze zmiennych publicznych klasy. Co więcej metoda będzie rozszerzała klasę w całej hierarchii, czyli będzie widoczna w klasach potomnych. Błyskotliwy programista od razu zauważy, że może to wprowadzić pewien chaos. Środowisko programistyczne wspiera ten mechanizm zatem podpowiedzi będą się ukazywały wszędzie. Jak rozszerzymy klasę Object, która jest korzeniem w hierarchii dla wszystkich klas, podpowiedzi środowiska będą uwzględniać dodatkowe metody, a może ich być zbyt dużo. Na ratunek przychodzą przestrzenie nazw. Metody rozszerzające definiuje się w konkretnej przestrzeni nazw i tylko po zadeklarowaniu użycia takiej przestrzeni, metoda rozszerzająca będzie widoczna. Innymi słowy widoczność metody jest kontrolowana przez przestrzeń nazw. Wadą całego mechanizmu jest tworzenie metod rozszerzających dla klas genetycznych. Jest to możliwe, niemniej jednak metoda rozszerzająca musi być też generyczna. Implikuje to jawne precyzowanie parametrów generycznych podczas jej wywoływania, co może być uciążliwe dla programisty. Ciekawe jednak jest, że możemy w ten sposób rozszerzać skonkretyzowane klasy generyczne. Przykłady:

 

[Kod C#]

public static class Extensions

{

   public static string ToStrProp (this object i)

   {

      if (i == null) return "";
      Type t = i.GetType();
      PropertyInfo[] ps = t.GetProperties();
      StringBuilder sb = new StringBuilder(t.Name);
      for (int j = 0; j < ps.Length; j++)
          sb.AppendFormat("\n{0} - {1}", ps[j].Name, ps[j].GetValue(i, null).ToString());
      

      return sb.ToString();

   }

   public static string ToStrProp <T>(this List<T> l) {…}

   public static string ToStrProp (this List<int> li) {…}

}

 

Jak widać powyżej zostały zdefiniowane 3 metody rozszerzające odpowiednie klasy. W przypadku korzystania z którejkolwiek z nich zostanie wywołana ta która jest najlepiej dopasowana (parametry leżą bliżej w hierarchii dziedziczenia).

 

 

 

Partial methods

 

Zmieniające się wciąż wymagania funkcjonalne stawiane przed oprogramowaniem spowodowały, że wiele zespołów projektowych zaczęło generować cześć kodu aplikacji z istniejącego jakiegoś modelu. Za model możemy rozumieć na przykład model fizyczny bazy danych. W czasie generowania kodu jednak powstawało sporo problemów związanych z brakiem możliwości rozszerzania wygenerowanego kodu. Sytuacją idealną byłoby napisanie tak generatora kodu, żeby generował maksymalny szkielet klasy, natomiast linker łączył tylko te części które są zdefiniowane. Wchodząc w szczegóły wygląda to następująco: Generator generuje plik z częściową definicją klasy. Deklaruje także użycie pewnych metod, z których korzysta. Te metody to właśnie „partial methods”. Niemniej jednak po skompilowaniu okaże się, że ponieważ nie ma ich definicji nie zostaną nigdzie wywołane. Ale jeśli użytkownik stworzy drugi plik, żeby do-definiować klasę i zdefiniuje właśnie metody zadeklarowane jako „partial”, wtedy już będą one wszędzie wywoływane. Przykład:

 

Plik Person.Generated.cs:

 

[Kod C#]

public partial class Person

{

  partial void CityChanging(string v);

  public string City

  {

    get { return city; }

    set

    {

      CityChanging(value);                           // (1)

      city = value;

    }

  }

}

 

 

Plik Person.cs:

 

[Kod C#]

public partial class Person

{

  partial void CityChanging(string v)                // (2)

  {

    Console.WriteLine("City.. ");

  }

}

 

Zatem metoda CityChanging (1) zostanie wywołana tylko wtedy gdy zostanie ona zdefiniowana (2).

 

 

 

 Lambda expressions

 

Mechanizm ten to rozszerzenie mechanizmu anonimowych delegatów, znanych z .NET 2.0. Jest to możliwość tworzenia nowej funkcji w już istniejącej. Poniżej są dwa przykłady, jeden anonimowego delegatu, natomiast drugi jest równoważny pierwszemu, jednakże wykorzystuje mechanizm lambda-expression. Oba wyszukują w kolekcji list2 elementu reprezentującego osobę o imieniu „Asia”.

 

[Kod C#]

var PAsia = list2.Find(delegate(Person p)

      {

            return p.Name == "Asia";

      });

var PAsia = list2.Find(p => p.Name == "Asia");

 

 

 

 

Linq

 

Linq – Language Integrated Query. Pod tym pojęciem kryje się wykonywanie zapytań do różnego rodzaju kolekcji. Mogą być różne źródła danych dla ów kolekcji. Platforma dostarcza gotowych rozwiązań dla źródeł danych w postaci bazy danych SQL, plików XML, oraz zwykłych kolekcji. Należy zwrócić uwagę na fakt, iż możliwa jest wstępna optymalizacja zapytania po stronie klienta. W stadium testowym jest PLinq czyli zrównoleglenie wykonywania takich zapytań.

 

Oto prosty przykład wykorzystania linq do wykonania zapytania dla zdefiniowanej kolekcji:

 

[Kod C#]

string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" };       // (3)

 

IEnumerable<string> query = from s in names           // (4)                         

                         where s.Length == 5          // (5)

                         orderby s                    // (6)

                         select s.ToUpper();          // (7)

 

foreach (var item in query)

            Console.WriteLine(item);                  // (8)

 

 

Najpierw inicjowana jest kolekcja(3), następnie jest wykonywane zapytanie, które określa niejawnie przy pomocy lambda-expression (5) filtr. Potem kolekcja jest sortowana według podobnie zdefiniowanego kryterium (6), a na końcu zwracany jest element po zamianie małych liter na wielkie (7). Ostatnia pętla wypisuje (8) zawartość wykonania zapytania.

 

Bardzo użyteczny okazuje się tutaj mechanizm instancjonowanie obiektów anonimowych. W miejscu, w którym określamy, co ma być z kolekcji zwracane (7) możemy stworzyć nową instancję anonimową. Implikuje to zastosowanie słowa var jako zmiennej przechowującej rezultaty zapytania(4). Analogia do widoków znanych z sql jest oczywista.

 

Platforma .NET 3.5 wspiera gotowe rozwiązania bazujące na LINQ. Jednym z nich jest Linq2Sql. Jest to mechanizm mapowania relacyjno-obiektowego:

 

Tabela

-->

Klasa

Rekord

-->

Obiekt

Kolumna

-->

Właściwość klasy

Procedura składowana

-->

Metoda

 

Linq2Sql jest elastyczny i prosty w obsłudze. Klasy generuje się metodą Drag & Drop w środowisku programistycznym. Edytor umożliwia pewne modyfikacje modelu. Można zmieniać nazwy, połączenia pomiędzy klasami, a także można wprowadzać dziedziczenia przy zachowaniu odpowiednich warunków. Klasy są generowane jako dzielone („partial”) jak również w pełni jest wykorzystany mechanizm „partial methods”. Umożliwia to dodefiniowanie zachowania klas. Możliwe jest także jawne zdefiniowanie komend języka SQL służących do wstawiania, modyfikacji bądź usuwania rekordów.

Najprostszą metodą generowania mapowania jest dodanie do istniejącego projektu pliku *.dbml:

 

AddItem

 

Następnie metodą Drag & Drop  z zakładki Server Explorer możemy przeciągnąć interesujące nas tabele, widoki i procedury składowane do Designera i model jest wygenerowany :)

 

Podsumowanie

Platforma .NET 3.5 wprowadza liczne udogodnienia w procesie tworzenia oprogramowania. Z jednej strony są to nowe mechanizmy językowe, w szczególności na uwagę zasługuje LINQ, z drugiej cała biblioteka, która je wykorzystuje.

W artykule zostały omówione:

  • Słowo kluczowe var.
  • Konstruktory kolekcji
  • Konstruktory z listą parametrów.
  • Konstruktory obiektów anonimowych.
  • Partial Methods.
  • Rozszerzenia klas
  • Lambda expressions.
  • Linq & Linq2 Sql

 Na zakończenie jedynie dodam, że dwa bardzo interesujące zagadnienia, jakimi są Lambda expressions oraz Linq, zostały w artykule jedynie 'wspomniane'. Ich dokładniejsza analiza to temat na dwa nowe artykuły.

 

Bibliografia:

http://msdn2.microsoft.com/en-us/default.aspx                                       

Microsoft Vista SDK  

http://weblogs.asp.net/scottgu/archive/2007/04/08/new-orcas-language-feature-lambda-expressions.aspx 

http://www.danielmoth.com/Blog/2007/06/net-framework-35.html 

http://www.codeproject.com/

http://sunnytalkstech.blogspot.com/2007/09/whats-new-in-net-framework-35.html

 

tagi: C#

Komentarze 0

pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.