Wednesday, September 14, 2011

О вреде params object[]


Грабли на которые наступил по невнимательности, но которых можно было бы избежать, будь интерфейс не такой "всеобъемлющий" :).

Итак, есть некий "суперкласс" (не мой, но пользоваться пришлось) Loader. Этот класс имеет несколько перегруженных методов:
  1. public void Load(LoadType loadType, Type formType, params object[] constructorParameters)
  2. {
  3.       Load(loadType, null, formType, null, null, constructorParameters);
  4. }
  5. public void Load(LoadType loadType, Type formType, LoginValidationHandler loginValidationHandler, params object[] constructorParameters)
  6. {
  7.       Load(loadType, null, formType, null, loginValidationHandler, constructorParameters);
  8.  } 



    public delegate string LoginValidationHandler(Trader trader);
    Это делегат, который использовался ранее - как видим - он принимает Trader и возвращает строку

Основное внимание на на последний параметр методов, точнее его тип - params object[]
Далее в основном коде  вызов метода этого класса -
  1. loader.Load(new FullLoad(), typeof(UtilitiesLauncherNotifyForm), CheckSupportedGroupFunc());
Ну и функция CheckSupportedGroupFunc:
  1. private static LoginValidationHandler CheckSupportedGroupFunc()
  2.         {
  3.             return
  4.                 trader =>
  5.                 trader.GroupsMap.Any(group => group.Key != null && group.Key.Trim().ToUpper().StartsWith("IT"))
  6.                     ? null
  7.                     : "You do not have correct access to launch the support utilitites.\r\n\r\n.";
  8.         }
Возвращаясь к loader.Load - я решил оптимизировать код применив паттерн Inline Method.
Получилось вот так:

  1. loader.Load(new FullLoad(), typeof(UtilitiesLauncherNotifyForm),
  2.                trader => trader.GroupsMap.Any(group => group.Key != null && group.Key.Trim().ToUpper().StartsWith("IT"))
  3.                     ? null
  4.                     : "You do not have correct access to launch the support utilitites.\r\n\r\n";
  5.         );
    Замена простая - заменили делегат через лямбду.
На самом деле - здесь очевидная ошибка.
public delegate string TraderValidationHandler(Trader trader) и Func<Trader, string> анонимный ни разу не один тип. Однако, код написан, он компилируется. Что получаем. А получаем проблему и жесткую... Возвращаясь к перегруженным методам Load:
В случае с делегатом будет вызван метод из строки 5(т.е. с делегатом в конструкторе). А что же будет с вызовом loader.Load через лямбду - будет вызван метод из строки 1 (т.е без делегата). Хуже того - вызов будет такой :
loader.Load(loadType, formType, object[]{Func<Trader, string>})
Т.е параметры конструктора получат анонимную функцию, которую совсем там не ждут. Хорошо, что все просто падало и проблема вылезла сразу. Однако не всегда проблема может проявиться сразу и тогда это может стать реальным злом.
Вывод: params object[] - может быть ЧЕМ УГОДНО и использовать их надо с максимальной осторожностью (а может лучше и не использовать). Универсальный загрузчик - как демисезонное пальто - и зимой холодно и летом - жарко. Вполне можно было обойтись и частными загрузками. На мой взгляд "premature generalization" такое же зло, как и "premature optimization".

P.S.
       Don't be evil!

Sunday, January 14, 2007

Крым 2003. Мне казалось это прикольно

. Хотя и сейчас так кажется
 

Мне сегодня 30 лет

Мне сегодня 30 лет. Не знаю радоваться или огорчаться. С одной стороны - здорово, что дожил, 
с другой - все-таки наступил 4 десяток - это я считаю много.