среда, 23 ноября 2011 г.

О том как в C# применить dynamic

Когда добавляли в C# слово dynamic видимо основное назначение было в интеграции с COM. Но оказалось, что его можно весьма неплохо применить и в других задачах.
Я столкнулся с необходимостью его использования при загрузке и показе нескольких таблиц из базы данных при помощи RIA+EF.Если на пальцах, то есть метод, который принимает Generic тип и Action, в которые передается параметр того же типа. Мне надо вызвать этот метод для 10+ разных типов.
Entity Framework для меня сгенерил типы, RIA сервисы методы для получения их из базы данных. Я, в целях эмуляции заменил вот на такой аналог:
  class First
  {
   public int Value { getset; }
  }
 
  private List<First> ReciveFirst()
  {
   List<First> result = new List<First>();
   result.Add(new First() { Value = 3 });
   result.Add(new First() { Value = 1 });
   result.Add(new First() { Value = 7 });
   return result;
  }
 
  class Second
  {
   public string Item { getset; }
  }
 
  private List<Second> ReciveSecond()
  {
   List<Second> result = new List<Second>();
   result.Add(new Second() { Item = "g" });
   result.Add(new Second() { Item = "b" });
   result.Add(new Second() { Item = "e" });
   return result;
  }

Ну и соответственно у RIA сервисов есть метод, который асинхронно загружает данные. Его аналог будет иметь вид:
  private void LoadData(Func<List> p_dataReciver, Action<List> p_callBack)
  {
   Action act =
    () =>
    {
     Thread.Sleep(1000); // Долгая операция по сети
     p_callBack(p_dataReciver()); // Возврат загруженных данных
    };
   act.BeginInvoke(nullnull);
  }

Как видим он является Generic методом. Так вот, мне надо было возвращенные коллекции отображать в DataGrid-е, в зависимости от того, какой метод загрузки выберет пользователь. Вариант с 10+ кнопками и кодом вида:
  private void button1_Click(object sender, RoutedEventArgs e)
  {
   LoadData<First>(ReciveFirst, ShowFirst);
  }
 
  private void ShowFirst(List<First> p_items)
  {
   Dispatcher.BeginInvoke((Action)(() => dgItems.ItemsSource = p_items));
  }

для каждого справочника мне писать было лень. И я сделал следующий финт ушами:
1. Объявил словарь названий справочников-методов для их загрузки:
                        Dictionary<stringdynamic> _methods = null;
2. Загрузить в него названия справочников и методы их возвращающие (там звездочка, о ней ниже):
   _methods = new Dictionary<stringdynamic>();
   _methods.Add("Первый", (Func<List<First>>)ReciveFirst);
   _methods.Add("Второй", (Func<List<Second>>)ReciveSecond);
   //*
3. Загрузить ключи в список:

   lbNames.ItemsSource = _methods.Keys;

4. На событие изменения выбранного элемента дописать вот такой обработчик:

  private void lbNames_SelectionChanged(object sender, SelectionChangedEventArgs e)
  {
   if (lbNames.SelectedItem != null)
   {
    LoadData(_methods[(string)lbNames.SelectedItem], (Action<dynamic>)ShowData);
   }
  }
5. Ну и собственно изменить метод показа:

  private void ShowData(dynamic p_list)
  {
   Dispatcher.BeginInvoke((Action)(() => dgItems.ItemsSource = p_list));
  }

Собственно все.
Выглядеть это все будет вот так:
В чем плюсы? В том, что в первом случае мне придется создать 10+ кнопок, 10+ обработчиков клика, 10+ методов показа данных. А во втором случае в строке со звездочкой (из пункта 2) дописать добавление названий справочников и методов их загружающих.
Ну и в завершении, как это выглядит в реальном приложении:
Собственно все.
P.s. Если кто не очень понял, зачем тут все таки dynamic обратите внимание на метод ShowData, который получает списки разных объектов, и не жужжит. Для тех кто не в курсе, такие приведения:
не работают. А через dinamyc все даже вполне себе работает.

Комментариев нет:

Отправить комментарий