C# składnie, o których zapominamy

Pracując długo jako programista C# trzeba uważać na stan, w którym ciągle i ciągle używamy tych samych metod na osiągnięcie podobnych celów. Człowiek jest istotą, która bardzo szybko się przyzwyczaja przez co przestaje korzystać ze wszystkich udogodnień jakie oferuje środowisko. Poniżej kilka przykładów składni w C#, o których nie zawsze pamiętamy.

In i out

Aby wyjaśnić dokładnie,  gdzie można stosować słowa kluczowe in i out najpierw trochę teorii. W C# 4.0 zostały wprowadzone dla typów generycznych pojęcia kontra- i kowariancji (z cyklu trudne słowa łatwa sprawa).

Kowariancja

czyli inaczej zależność –  to nic innego jak konwersja z typu bardziej precyzyjnego na typ bardziej ogólny. Typem dla zwracanego obiektu może być sam obiekt lub też obiekt bazowy.

public class Figury { };

public class Czworokaty : Figury
{
    Figury figura = new Czworokaty();
    Czworokaty czworokat = new Czworokaty();
};

public class Kwadrat : Czworokaty { };

Czyli do obiektu typu Figura można przypisać obiekt typu Czworokat.

  Kwadrat kwadrat = new Czworokaty(); // błąd

Jednak nie można już przypisać do obiektu typu Kwadrat, obiektu typu Czworokat,  ponieważ nie zawsze Czworokąty są kwadratami.

Kowariancja jest wartością zwracaną przez metody.

Kontrawariancja

czyli inaczej konwersja z typu bardziej ogólnego na typ bardziej precyzyjny.

Kontrawariancja to nic innego jak parametry funkcji.

 public class Figury { };
 public class Czworokaty : Figury
 {
       public Czworokaty()
       {
           this.Test(new Kwadrat());
           this.Test(new Czworokaty());
       }

       public Czworokaty Test(Czworokaty czworokat)
       {
           return new Czworokaty();
       }
 };

public class Kwadrat : Czworokaty { };

Do metody z parametrem typu Czworokaty można przesłać obiekty bardziej precyzyjne czyli Kwadrat.

   this.Test(new Figury()); //błąd

Jednak już nie możemy przesłać typu bardziej ogólnego, ponieważ nie każda figura to czworokąt.
W C# 3.0 typy generyczne nie używały kowariancji ani kontrawariancji a więc mogły zwracać i przyjmować parametry tylko tego samego typu dla jakiego zostały stworzone.

   IEnumerable<Figury> listaFigur = new List<Czworokaty>(); // Kowariancja błąd w C# 3.0
   IEnumerable<Czworokaty> listaCzworokatow = new List<Czworokaty>(); // prawidłowo  w C# 3.0
   IEnumerable<Kwadrat> listaKwadratow = new List<Czworokaty>(); // Kontrawariancja. błąd w C# 3.0

W C# 4.0 dodano słowa kluczowe out(nie mylić z out i ref) i in.

Słowo out służy do zapewnienia kowariancji. Używamy go w deklaracji interfejsu:

public interface IEnumerable<out T>;

dzięki temu możemy wykonać następujące przypisanie:

IEnumerable<Figury> listaFigur = new Czworokaty();

Jeśli chcemy zapewnić kontrawariancję należy użyć słowa kluczowego in

public interface ITest<in T>;

dzięki temu możemy skompilować poniższy kod:

public ITest<Czworokat> Test()
{
  return null;
}

ITest<Czworokat> listaCzworokatow = Test();
ITest<Kwadrat> listaKwadratow = Test();

 

Drzewa wyrażeń

Czyli Expression Trees reprezentują kod w strukturze drzewiastej, w której każdy węzeł odpowiada pewnemu wyrażeniu. Czyli proste wyrażenie x > y powoduje, że w zależności od wyniku przechodzimy do prawej lub do lewej części wyrażenia.

Wykorzystuje się to najczęściej w LINQu to SQL i w wyrażeniach lambda.

 public void Study()
   {
            Expression<Func<int, bool>> wyrazenie = x => x > 4;

            var metoda = wyrazenie.Compile()(5);

            Assert.AreEqual(true, metoda);

        }

Osobiście jeszcze tego nie używałem ale na pewno warto to wiedzieć. Trochę informacji ze źródła: https://msdn.microsoft.com/en-us/library/bb882637.aspx

Caller Info

Caller Info to atrybuty wprowadzone w C# 5.0.  Dzięki nim można uzyskać informację o wywołaniu metody, ścieżce do pliku źródłowego i informacje o numerze wiersza, w której metoda została wywołana.

Mamy 3 atrybuty do wykorzystania w metodzie:

  • CallerFilePathAttribute (ścieżka do pliku źródłowego z ciałem metody podczas kompilacji)
  • CallerLineNumberAttribute (numer wiersza w, którym metoda jest wywoływana)
  • CallerMemberNameAttribute (nazwa metody wywołującej)

 

 class Things : IStudyTest
    {
        public int Odejmij(int arg1, int arg2,
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int lineNumber = 0,
            [CallerMemberName] string methodName = "")
        {
            Console.WriteLine("Ścieżka do pliku {0}",filePath);
            Console.WriteLine("Number wiersza {0}", lineNumber);
            Console.WriteLine("Nazwa metody {0}", methodName);
            return arg1 - arg2;
        }
}

Należy pamiętać, że atrybuty zawierają informację na temat metody wywołującej metodę, w której deklarujemy atrybuty.

Partial

Słowo kluczowe partial wprowadzone w C# 3.0 można stosować i do klas i do metod. Słowo to rozdziela na klika części klasy, struktury, interfejsy lub metody. Wszystkie części muszą znajdować się w obrębie jednej dll-ki.

Kolejne części muszą być takie same, czyli jeśli jedna część dziedziczy jakiś obiekt to druga części też będzie dziedziczyć, muszą mieć ten sam dostęp (public, private), jeśli jedna cześć jest abstrakcyjna to druga również będzie abstrakcyjna.

Poniższy kod skompiluje się bez błędów:


class Przyklad
    {
        partial class Klasa
        {
            public void Metoda2() { }
        }

        public void Study()
        {
            var klasa1 = new Klasa();

            klasa1.Metoda2();
            klasa1.Metoda1();
        }

        partial class Klasa
        {
            public void Metoda1(){}
        }

    }

Widać wyraźnie, że obiekt Klasa jest rozdzielony na dwie deklaracje. Mimo tego, możemy korzystać z tego obiektu tak jak by był to jeden obiekt.

Swoją drogą gdzieś czytałem, że słowo kluczowe Partial zostało celowo wprowadzone po to by w Web Forms-ach ładniej wyglądało code-behind (i w ogóle było możliwe). W części książek piszą wyraźnie aby z tego nie korzystać.

Istnieje też możliwość rozdzielenie metody:

partial class Przyklad
{
partial void Metoda();

public Przyklad()
{
this.Metoda();
}

partial void Metoda()
{
//Ciało metody.
}

Jest jednak sporo „gwiazdek”. Metody partial mogą być zadeklarowane tylko w klasach i strukturach, które też są partial. Mogą tylko zwracać typ void i zawsze są private.

 

Checked i Unchecked

Instrukcje te służą do kontroli przepełnień arytmetycznych. Instrukcja Chcecked gdy zajdzie przepełnienie rzuci wyjątkiem. Dla instrukcji Uchecked przepełnienie nie ma znaczenia i błąd zostanie zignorowany.

Przykładowe przepełnienie. Wielkość typu byte jest  255.

skladnia

Instrukcja Checked rzuciła wyjątkiem.

Jeśli jednak zastosujemy instrukcję Uchecked

skladnia1

kod wykona się bez problemów i nastąpi „kontrolowane” przepełnienie.

Typy anonimowe

Typy anonomowe zostały wprowadzone w C# 3.0. Dzięki nim możemy tworzyć klasy, których deklaracja nie istnieje.

Poniższy zapis:

 var Test = new { napis = "Przemek", liczba = 12, liczba1 = 13.1 };

jest równy takiej klasie:

class Test
    {
        private string _napis = "Przemek";
        private int _liczbna = 12;
        private double _liczba1 = 13.1;

        public string napis
        {
            get { return _napis; }
            set { _napis = value; }
        }

        public int liczbna
        {
            get { return _liczbna; }
            set { _liczbna = value; }
        }

        public double liczba1
        {
            get { return _liczba1; }
            set { _liczba1 = value; }
        }
    }

Argumenty nazwane

Wprowadzone w C# 4.0. Dzięki nim  możemy podawać argumenty w dowolnej kolejności. Dla metody poniżej:


public int Odejmij(int arg1, int arg2)
{
  return arg1 - arg2;
}

…te dwa wyrażenia zwrócą to samo:

var wynik = Odejmij(1, 2);

var wynik2 = Odejmij(arg2: 2, arg1: 1);

 

 

3 przemyślenia nt. „C# składnie, o których zapominamy

  1. W żadnym wypadku nie da się tak zrobić:
    IEnumerable listaFigur = new Czworokaty();
    domyślam się jednak że chodziło o:
    IEnumerable listaFigur = new List();

    1. Co się tu stało ? czemu w moim poprzednim komentarzu nie ma nawiasów trójkątnych, spróbuję użyć „|”, oczywiście powinno:

      IEnumerable|Figury| listaFigur = new Czworokaty();
      domyślam się jednak że chodziło o:
      IEnumerable|Figury| listaFigur = new List|Czworokaty|();

  2. Pingback: dotnetomaniak.pl

Możliwość komentowania jest wyłączona.