Monday, February 21, 2011

Делегаты

В C# нет указателей (про unsafe я молчу). И это хорошо и правильно – указатель он по сути своей не типизирован. Передали в метод ссылку, а как ее интерпретировать – на совести принимающей стороны. Можем передать int, а использовать как float. И т.д. Тоже самое касается и методов – передали указатель на метод с одной сигнатурой, а используем совсем по-другому…
Зато в C# есть делегаты. Я бы назвал делегаты “типизированные указатели на методы”. Покажу на примере как это работает.
Сначала нужно описать тип делегата, т.е. сигнатуру метода. Описание можно не привязывать к конкретному классу:
namespace DelegateTest
{
    public delegate decimal GetVarValue(string name);

Здесь метод имеет один параметр типа string и возвращает decimal.
Теперь этот делегат мы можем использовать при описании параметров и переменных:
public class Parser
{
    /// <summary>
    /// Описание переменной, сохращающей делегат
    /// </summary>
    private GetVarValue getVarValueHandler;

    /// <summary>
    /// Передаем делегат в конструктор
    /// </summary>
    /// <param name="getVarValueHandler"></param>
    public Parser(GetVarValue getVarValueHandler)
    {
        // Сохраняем делегат, т.к. хотим использовать его
        // в другом методе этого класса
        this.getVarValueHandler = getVarValueHandler;
    }

    /// <summary>
    /// Метод расчета
    /// </summary>
    /// <returns></returns>
    public decimal Calc()
    {
        decimal result = 0;

        // Вызываем делегат с параметром var1
        result += getVarValueHandler("var1");
        // Вызываем делегат с параметром var2
        result += getVarValueHandler("var2");

        return result;
    }

Описание переменной типа делегат выглядит совершенно так же как и обычной переменной. Переменная getVarValueHandler имеет тип GetVarValue, т.е. тип делегата с нашей сигнатурой. Отличие от переменной в том, что мы можем вызывать этот метод. Разумеется, вызывать его можно только так, как он описан, т.е. с одним параметром типа string:
                 getVarValueHandler("var1");
Прелесть  в том, что никак иначе вызвать метод по этой ссылке (а передали мы фактически ссылку на метод) нельзя. Только так, как метод описан в описании делегата.
Использовать этот класс тоже очень просто. Во-первых, нам потребуется метод, который мы будем передавать в конструктор нашего класса. Наверное, излишне говорить, что сигнатура этого метода должна совпадать с сигнатурой делегата и передать в конструктор что-то другое нельзя:
class Program
{
    static void Main(string[] args)
    {
        // Передаем в конструктор делегат, т.е. ссылку
        // на наш метод
        Parser parser = new Parser(GetVarValueMethod);
        // Вызываем метод Calc
        Console.WriteLine(parser.Calc());
    }

    /// <summary>
    /// Делегат. Имя у него может быть любое, главное
    /// чтобы сигнатура была той как описана в типе
    /// делегата
    /// </summary>
    static decimal GetVarValueMethod(string name)
    {
        Console.WriteLine("Value of {0}:", name);
        return decimal.Parse(Console.ReadLine());
    }

}

Имя метода может быть может быть любое, главное чтобы сигнатура совпадала с сигнатурой делегата.
Вот и все. Теперь при вызове метода Calc будет два раза вызван метод GetVarValueMethod.

Цитата

студенты: а что вы обычно говорите своим выпускникам, когда встречаете их?
преподаватель: картошку фри и большую колу пожалуйста

Про автомобили и заказчиков

Стал замечать, что очень часто перевожу объяснения нашей работы на примеры более понятые для заказчиков. Например – про автомобили. Объяснять про билды, сборки, тестирование – сложно, а про СТО, автомобили и креш-тесты – как-то ближе получается, проще…
Про олимпиадные задачи и водителей недавно писал… Не для заказчиков правда, для студентов, но все равно как-то доходчивей получается, чем про код и процесс разработки…
Как-то несколько дней просил заказчика прислать нам новые требования. Уже все старые почти сделали, а новых все нет, но точно обещано, что будут. Объясняю – вот если автомобиль разобрали на СТО, чтобы масло поменять в коробке, висит он на подъемнике, поменяли, собрали. И тут вы приходите и говорите что надо бы еще диск сцепления заменить – ну ведь это же все заново разбирать, снова собирать… И мастер ругается словами неприличными и вам потом сборка-разборка в счет пойдет… Другое дело, если заранее сказать, пока все разобрано. Делов-то пару гаек открутить… Так и у нас – пока билд не собрали, пока не оттестировали все – изменения еще можно вносить, а потом это уже двойная работа будет. Надо сказать, что такое объяснение прошло – требования прислали.

Monday, January 31, 2011

Цитата

40 музыкантов симфонического оркестра играют симфонию за 30 минут.
За какое время ту же симфонию сыграют 80 музыкантов?

Saturday, January 22, 2011

Еще раз про пользу правильного именования

В старой ветке RDSN форума обсуждалось как-то написание пословиц на C++. Вот так например:
if (flag == true)
  if (flag == true)
    if (flag == true)
      if (flag == true)
        if (flag == true)
          if (flag == true)
            if (flag == true)
              Cut();
Скороговорка:
greka.drive(river,moveType::Over);
if(greka.lookUpFirstItem(river) isinstanceof Cancer)
{
  river.insert(greka.hand);
  crayfish.grab(greka.hand);
}

И еще про рыбу:

bool IsFish(const Animal &animal)
{
    ...
    if(FishContainer.IsEmpty && IsCrawfish(animal))
        return true;
    ...
}
А вот довольно спорная по сути, но зато оформленная двумя классами:
class CBaba : public CHomoSapiens
{
public:
     bool   KonyaNaSkakuOstanovit();
     bool   VGoryazhuyIzbuVoidet();
}
class CKobila : public CAnimal
{
private:
     int    m_trudno;
public:
     bool   BabaSVozu( const CBaba&)
     {  
         m_trudno--; // Kobile legche
     }
}
Шутки-шутками, а если серьезно – подумайте, ведь правильное именование переменных и методов позволяет именно читать код, понимать его суть, т.е. бизнес-смысл! И в реальном коде должно быть тоже самое.

Про олимпиадные задачи

Нашел на форуме сообщение, что типа я говорил, что олимпиадные задачи это плохо. Не говорил я такого… Придется написать что я про это думаю, а то ж беда с помехами на каналах связи… Говоришь одно, понимают другое, а потом пишут вообще ерунду…
Олимпиадные задачи – это круто! Вот. Это сложно, это интересно. Научиться их решать  требует большой, каждодневной, постоянной работы. Но – это ДРУГОЙ навык, чем промышленное программирование. Не хуже, не лучше. Просто другой.
Для примера – есть обычный водитель, есть водитель такси, а есть Формула-1. Что у них общего? Общего у них машина, 4 колеса, руль и т.д. и водитель. Но у них разные цели, разные навыки. И одного водителя нельзя заменить другим. Попробуйте водителя формулы (для полноты картины назовем его так) посадить таксистом… Результат будет печальный. Как собственно и наоборот – таксист просто не проедет трассу. И цели у них разные. Таксисту зарабатывает на довольно монотонной работе – взял пассажира, отвез, ждем, взял, отвез… Конечно тут не все так просто – и дороги разные (то пробка, то труба где-то рванет, воды полно, то снегу по колено, а дороги не чищены…) и пассажиры разные… Но такая у него работа. А формула-1 - тут все по-другому. Тут водитель учится долго-долго, оттачивает мастерство и тот единственный заезд довольно редко, но зато как он едет и на какой скорости! И вся подготовка только ради этого одного, редкого “броска”. Это его заработок. Правда тут есть еще одна “засада” – реально зарабатывающих таких единицы. Остальные идут в таксисты J
Собственно я к чему  - олимпиадные задачи это здорово. Думаю что и заработать этим навыком можно – например, задачи оптимизации иногда возникают, обходы деревьев и т.д. Но это другой навык, чем писать коммерческий код и зарабатывать на жизнь таким способом. И тем кто привык и наработал навык мгновенного броска очень сложно потом монотонно “ездить”. Нужны конечно же и те и другие, но супер-задач обычно очень мало, а монотонной работы – очень много.

Friday, January 21, 2011

MVC и дизайн

Использование чистого MVC кажется очень не удобным в реальных проектах. Если у меня есть дизайнер/верстальщик, который делает прототип (включая jQuery и т.д.), то потом перепахать это вот в такой код:

<%
.RowAttributes(row =>
.Columns(column =>
{
column.AutonamedFor(m => m.Year).Visible(filter.Year ==
.Attributes(width =>
column.For(m => m.WeekNumber).Named(
.Attributes(style =>
column.AutonamedFor(m => m.Project)
.Attributes(width =>
column.AutonamedFor(m => m.Manager).Visible(Page.User.IsAdministrator())
.Attributes(@class =>
column.AutonamedFor(m => m.GeneralState);
column.AutonamedFor(m => m.ResourcePlans).HeaderAttributes(@class =>
.Attributes(cell =>
column.AutonamedFor(m => m.QuestionsToIT).HeaderAttributes(@class =>
.Attributes(cell =>
column.AutonamedFor(m => m.Risks).HeaderAttributes(@class =>
.Attributes(cell =>
}).RenderGrid(header)
%>
= Html.Grid(Model, header)new Hash(style => "height : 50px; background-color:" + row.Item.ProjectColor))null)"4%", style => "text-align: center").HeaderAttributes(width => "4%");"Week").Visible(filter.WeekNumber == null)"width: 5%; text-align: center").HeaderAttributes(width => "5%");"9%").HeaderAttributes(width => "9%");"width15").HeaderAttributes(@class => "width15");"width15")new Hash(@class => "width15"));"width15")new Hash(@class => "width15"));"width15")new Hash(@class => "width15"));
Весьма затруднительно. И наоборот - попробуйте дизайнера или верстальщика попросить пофиксить тут стили... В результитующем HTML он конечно пофиксит. А вот кто будет это обратно перетаскивать в код... В общем не нравится. Хочется чтобы код разметки был максимально близок коду HTML-прототипа. Может razor спасет...

Update: может это не MVC грид...