Przydatne przykłady C# 3.0

7

Oto trzy przykładowe scenariusze, które wykorzystują moc daną nam przez Andersa Hejlsberga i spółkę:

1. Sprawdzenie, czy wszystkie textboxy są wypełnione

Przykładzik banalny i w wielu sytuacjach niepraktyczny, ale ładnie prezentujący wygodę pisania kodu “the 3.0 way”.

Stary kod:

 1:   private bool AreAllTextboxesFilledOldWay()
2: {
3: foreach (Control c in this.Controls)
4: {
5: TextBox tb = c as TextBox;
6: if (tb == null)
7: continue;
8:
9: if (tb.Text == string.Empty)
10: return false;
11: }
12:
13: return true;
14: }

Nowy kod:

 1:   private bool AreAllTextboxesFilledNewWay()
2: {
3: return this.Controls.OfType<TextBox>().Any(tb => tb.Text == string.Empty) == false;
4: }

Cool.

2. Wysłanie wyłącznie niezbędnych danych z serwera do przeglądarki w postaci JSON

Założenie: na stronie wyświetlamy DropDown z użytkownikami w określonym wieku. Wiek można podać jakkolwiek np przez wpisanie go do textboxa. Po wpisaniu wieku wysyłamy na serwer AJAXową strzałkę po dane przesłane w formacie JSON potrzebne do wyrenderowania listy. Co robiliśmy na serwerze przed erą .NET 3.5? Trzeba było mieć klasę reprezentującą tylko potrzebne do wysłania dane, trzeba było mieć klasę odpowiedzialną za serializację JSON, trzeba wreszcie było odfiltrować zbędnych użytkowników. Wraz z udogodnieniami takimi jak LINQ (filtrowanie danych), typy anonimowe (dbanie o serializowanie wyłącznie potrzebnych w przeglądarce danych), metody rozszerzające (żeby wywołanie “ToJSON()” wyglądało ładnie) i wyrażenia lambda cała operacja sprowadza się do jednej czytelnej (po zapoznaniu się z w/w mechanizmami) linijki kodu.:

 1:   _usersSrv.GetUsers().Where(u => u.Age == age).Select(u => new { Text = u.Name, Value = u.Id }).ToList().ToJSON();

3. SqlDataReader.HasColumn()

No i ostatni jak na razie przykład. Oto śliczny sposób na dowiedzenie się, czy DataReader posiada kolumnę o nazwie podanej w parametrze:

 1:   public static bool HasColumn(this SqlDataReader reader, string columnName)
2: {
3: return reader.GetSchemaTable().Rows.Cast<DataRow>().Any(dr => dr[0].ToString() == columnName);
4: }

A wy jakie macie godne pokazania praktyki związane z językowymi nowościami (o ile można w ogóle mówić o “nowościach” tyle miesięcy po premierze C# 3.0)?

Nie przegap kolejnych postów!

Dołącz do ponad 9000 programistów w devstyle newsletter!

Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit
Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET i współorganizator konferencji Programistok. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook, YouTube.

7 Comments

  1. Wydaje mi się, że fakt istnienia LINQ i metod rozszerzających nie powinien służyć za usprawiedliwienie stylu kodowania “byleby zmieścić wszystko w jednym wyrażeniu”, za który przez lata programistów straszono, że zacytuję pewnego znanego wszystkim Zine’owego blogera, ucinaniem rączek. Niestety bardzo często do tego się to sprowadza (do usprawiedliwania, nie rozczłonkowania).
    Przyjrzyjmy się, co właściwie robi ta “_czytelna_” linia:
    _usersSrv.GetUsers().Where(u => u.Age == age).Select(u => new { Text = u.Name, Value = u.Id }).ToList().ToJSON();
    1) przygotowuje zbiór danych:
    _usersSrv.GetUsers()
    2) deklaruje przekształcenia do wykonania na zbiorze – selekcję i projekcję:
    .Where(u => u.Age == age).Select(u => new { Text = u.Name, Value = u.Id })
    3) przekształca zbiór zgodnie z wcześniejszymi deklaracjami i zwraca wynik w postaci listy:
    .ToList()
    4) konwertuje listę par etykieta-wartość do łańcucha w notacji JSON:
    .ToJSON()
    Podumowując, w jednej linii dostajemy wyrażenie składające się z 5 wywołań metod i realizujące 4 zadania. Czy to oby na pewno dobry pomysł? Według mnie – taki sobie.
    Osobiście rozbiłbym to wyrażenie na kilka mniejszych, mając na uwadze właśnie odpowiedzialności poszczególnych fragmentów:
    1) zdefiniuj zbiór danych:
    UserCollection users = _usersSrv.GetUsers();
    2) zdefiniuj przekształcenia dla zbioru*:
    var query =
      from u in users
      where u.Age == age
      select new { Text = u.Name, Value = u.Id };
    3) wygeneruj dane w formacie JSON uwzlędniając zadeklarowane przekształcenia na zbiorze danych (konwersję do listy pominę, gdyż ja bym metodę ToJson zaimplementował jako rozszerzenie IEnumerable):
    string formattedData = query.ToJson();
    W wyniku dostajemy taki fragment kodu:
    UserCollection users = _usersSrv.GetUsers();
    var query =
      from u in users
      where u.Age == age
      select new { Text = u.Name, Value = u.Id };
    string formattedData = query.ToJson();
    który jest może nieco dłuższy, ale według mnie czytelniejszy i łatwiejszy w utrzymaniu. Po jednym zerknięciu na kod możemy powiedzieć, że w pierwszym wyrażeniu uzyskujemy jakiś zbiór, w drugim jakieś jego przekształcenia, a w trzecim jakiś wynik na ich podstawie, czyli możemy na bardzo ogólnym poziomie odtworzyć logikę. Gdy wszystko zapiszemy w jednej linii, po jednym zerknięciu możemy co najwyżej zakrzyknąć “D’oh!”.
    P.S. Tak przy okazji, o tym, że zbyt intensywnie wykorzystując składnię LINQ można się zapędzić w kozi róg pisał dziś Ayende Rahien w poście o dużo mówiącym tytule “Unreadable LINQ”:
    http://ayende.com/Blog/archive/2008/08/02/Unreadable-Linq.aspx
    ______
    * Tak naprawdę nie byłoby większym złem połączenie punktów 1) i 2) w jeden, pod warunkiem, że wyrażenie pobierające zbiór danych jest trywialne (proste wywołanie metody lub właściwości).

  2. @Gutek:
    W tym poście można znaleźć kilka dodatkowych “skróceń” osiągniętych dzięki LINQ: http://igoro.com/archive/7-tricks-to-simplify-your-programs-with-linq/ .
    @apl:
    Oczywiście, że pakowanie WSZYSTKIEGO co się da w jedną linię  jest bez sensu. Gdybym uważał że ma to sens to ten post zawierałby 100 przykładów a nie 3 :).
    Efekt taki jak w punkcie 2 można było i tak osiągnąć wcześniej, jednak jak by to wyglądało… Moim zdaniem w tym konkretnym przypadku robijanie owej linii na 4 fragmenty można spokojnie zastąpić umieszczeniem jej w metodzie o wszystkomówiącej nazwie, np GetUsersByAgeToJSON(int age).
    A na koniec: implementacja ToJSON jako rozszerzenie IEnumerable to zaiste dobry pomysł, w powyższym przypadku została ona dopięta do object przy wykorzystaniu biblioteki Newtonsoft, stąd ToList().

  3. MichalCz on

    Przykład 1 jest wg mnie zapisany nieelegancko. Jeżeli już bawimy się w takie skróty napisałbym
    private bool AreAllTextboxesFilledNewWay()
    {
    return this.Controls.OfType().All(tb => tb.Text != string.Empty);
    }

  4. Zgodzę się z Olkiem, że takie upychanie w jednolinijkowce nie sprzyja czytelności. Przypominają mi się wszelkie wariacje na temat “:?”.
    Jeśli zaś chodzi o ciekawe nowe cechy C# 3.0 to extension methods i lambdy są absolutnie genialne.
    Podam mój ulubiony przykład. Typowym idiomem w aplikacjach desktopowych jest znane
    delegate void FunHandler();
    void Fun()
    {
       if (control.InvokeRequired)
       {
           control.Invoke(Fun);
       }
       else
       {
           control.Text = “blah blah”;
       }
    }
    Aktualnie metodę Fun można uprościć do postaci:
    void Fun()
    {
       control.Invoke(() => control.Text = “blah blah”);
    }
    oczywiście nasz nowy Invoke może też zwracać wartość. Wszystko dzięki dwóm metodom rozszerzającym(?) o postaci:
    static class ControlEx
       {
           public static TResult Invoke(this T controlToInvokeOn, Func code) where T : Control
           {
               if (controlToInvokeOn.InvokeRequired)
               {
                   return (TResult)controlToInvokeOn.Invoke(code);
               }
               else
               {
                   return (TResult)code();
               }
           }
           public delegate void Func();
           public static void Invoke(this Control controlToInvokeOn, Func code)
           {
               if (controlToInvokeOn.InvokeRequired)
               {
                   controlToInvokeOn.Invoke(code);
               }
               else
               {
                   code();
               }
           }
       }
    Btw: Czy silnik zine.net.pl posiada obsługę tagów w komentarzach? Mam na myśli [code] ;).
    Filip