Klasa Gemini w C#

Przed przeczytaniem tego postu zachęcam do zapoznania się następującymi wpisami:

1. Typ dynamic w C# 4.0
2. Zastosowanie dynamic: ExpandoObject
3. Zastosowanie dynamic: DynamicObject

ExpandoObject to przydatna czasami klasa, ale problem z nią taki, że nie można jej rozszerzać i nawet została oznaczona jako sealed.

Aby skorzystać z klasy Gemini, najpierw należy zainstalować odpowiedni pakiet z NuGet:

image

Zacznijmy od przykładu pokazującego, że faktycznie można po Gemini dziedziczyć:

internal class Program
{
   private static void Main(string[] args)
   {
       dynamic person=new Person();
       person.FirstName = "Piotr";

       person.DisplayMessage();
   }
}

internal class Person : Gemini
{
   public void  DisplayMessage()
   {
       Console.WriteLine(_.FirstName);
   }
}

Proszę zwrócić uwagę na wskaźnik _, który jest dynamic i dlatego może odnosić się do jakichkolwiek pól. Gdybyśmy chcieli użyć this, program nie skompilowałby się, ponieważ właściwość FirstName nie jest jawnie zadeklarowana. Wewnętrznie, Gemini implementuje po prostu DynamicObject.

Na razie jeszcze nie pokazałem żadnych nadzwyczajnych możliwości Gemini. Ciekawą jednak możliwością jest definiowanie metod globalnie, w jednym miejscu, np. w bootstrapperze aplikacji:

internal class Program
{
   private static void Main(string[] args)
   {
       Gemini.Extend<Person>(p =>
       {
           p.DisplayMessage = new DynamicMethodWithParam(text => Console.WriteLine(text + p.FirstName));
       });


       dynamic person = new Person();
       person.FirstName = "Piotr";

       person.DisplayMessage(); // wyswietla "Piotr"
       person.DisplayMessage("Witaj "); // wyswietla "Witaj Piotr"
   }
}

internal class Person : Gemini
{
   public void  DisplayMessage()
   {
       Console.WriteLine(_.FirstName);
   }
}

Analogicznie można zastąpić już istniejące metody. W powyższym przypadku rozszerzamy funkcjonalność o dodatkową metodę, przyjmującą parametr wejściowy. Możliwe jest również rozszerzenie konkretnej specyfikacji:

 p.DisplayMessage = new DynamicMethodWithParam(text => Console.WriteLine(text + p.FirstName));

To chyba akurat nie jest szczególnie interesujące bo podobną funkcjonalność dostarczał ExpandoObject. Gdy próbujemy wywołać metodę, która nie istnieje wtedy zostanie wywołana MethodMissing:

internal class Person : Gemini
{
   public void DisplayMessage()
   {
       Console.WriteLine(_.FirstName);
   }

   private dynamic MethodMissing(dynamic callInfo)
   {
       Console.WriteLine(callInfo.Name);
       throw new NotImplementedException();
   }
}

Obiekt callInfo zawiera informacje o metodzie, którą użytkownik chciał wywołać. Możemy wyrzucić wyjątek (jak w przypadku powyżej) lub stworzyć implementacje i ją wstrzyknąć tzn.:

dynamic MethodMissing(dynamic callInfo)
{
  var member = callInfo.Name.Replace("Html", "");
  SetMember(callInfo.Name,
      new DynamicFunction(() =>
      {
          return "Wstrzyknieta metoda zwracajaca string.";
      });

  return GetMember(callInfo.Name)();
}

Powyższa konstrukcja to tak naprawdę lazy loading – definiujemy metodę, dopiero gdy faktycznie jest ona potrzebna.

W następnym poście postaram się pokazać jakiś konkretny, praktyczny przykład.

Leave a Reply

Your email address will not be published.