О сложностях программирования, или
C# нас не спасет?
Автор: Евгений Рыжков

Дата: 26.10.2010

Программирование это сложно. С этим никто, надеюсь, не спорит. Но вот тема новых языков
программирования, а точнее поиск "серебряной пули" всегда находит бурных отклик в умах
разработчиков программного обеспечения. Особенно "модной" является тема превосходства
одного языка программирования над другим. Ну, к примеру, что C# "круче", чем C++. И хотя holy
wars – это не та причина, по которой я пишу этот пост, тем не менее, что называется "наболело".
Ну не поможет C#/lisp/F#/Haskell/... написать изящное приложение, взаимодействующее с
внешним миром, и все тут. Вся изящность теряется, стоит захотеть написать что-то реальное, а не
пример "сам в себе".

В тексте несколько фрагментов на C#, взятые из модуля интеграции статического анализатора
кода PVS-Studio в популярную среду Microsoft Visual Studio. Этими фрагментами я хочу показать,
что писать, к примеру, на C# совсем не проще, чем на, C++.


Простой combobox
Этак, первый фрагмент кода – обработка выбора одной из трех строк в ОБЫЧНОМ combobox на
панели инструментов с картинки.




Рисунок 1 – Простой combobox на три строчки

И вот для обработки такой вот фитюльки требуется следующий код. К сожалению, пришлось
изменить форматирование и убрать комментарии. Так что прошу прощения за ужас.

private void OnMenuMyDropDownCombo(object sender, EventArgs e)

{

    if (e == EventArgs.Empty)

    {

        throw (new ArgumentException());
}



OleMenuCmdEventArgs eventArgs = e as OleMenuCmdEventArgs;



if (eventArgs != null)

{

    string newChoice = eventArgs.InValue as string;

    IntPtr vOut = eventArgs.OutValue;



    if (vOut != IntPtr.Zero && newChoice != null)

    {

        throw (new ArgumentException());

    }

    else if (vOut != IntPtr.Zero)

    {

        Marshal.GetNativeVariantForObject(

            this.currentDropDownComboChoice, vOut);

    }



    else if (newChoice != null)

    {

        bool validInput = false;

        int indexInput = -1;

        for (indexInput = 0;

              indexInput < dropDownComboChoices.Length;

              indexInput++)

        {

            if (String.Compare(

               dropDownComboChoices[indexInput], newChoice,

               true) == 0)
{

            validInput = true;

            break;

        }

    }



    if (validInput)

    {

        this.currentDropDownComboChoice =

             dropDownComboChoices[indexInput];

        if (currentDropDownComboChoice ==

             Resources.Viva64)

            UseViva64Analysis(null, null);

        else if (currentDropDownComboChoice ==

                     Resources.GeneralAnalysis)

            UseGeneralAnalysis(null, null);

        else if (currentDropDownComboChoice ==

                     Resources.VivaMP)

            UseVivaMPAnalysis(null, null);

        else

        {

            throw (new ArgumentException());

        }

    }

    else

    {

        throw (new ArgumentException());

    }

}

else
{

            throw (new ArgumentException());

        }

    }

    else

    {

        throw (new ArgumentException());

    }

}

Причем здесь IntPtr.Zero и Marshal.GetNativeVariantForObject()? Ну так надо... Простой combobox
обрабатывается совсем не просто.

Причем этого кода не достаточно. Есть еще рядом функция OnMenuMyDropDownComboGetList()
примерно такого же размера.

Здесь C# оказался ничем не лучше любого другого языка. Нет, конечно же, круто, что он
инкапсулировал от меня OLE, маршалинг. На Си все было бы намного печальней. Но вот только
как-то все равно все не то, как преподносится в книгах и эвангелистами. Простота-то где? Я всего
лишь хотел с выпадающим списком поработать.


Навигация по коду в Visual Studio
Когда в Visual Studio вы щелкаете по сообщению об ошибке, срабатывает примерно такой код для
открытия файла и перехода к строке с ошибкой.

public void OpenDocumentAndNavigateTo(string path, int line,

    int column)

{

            IVsUIShellOpenDocument openDoc =

                    Package.GetGlobalService(

                    typeof(IVsUIShellOpenDocument))

                    as IVsUIShellOpenDocument;

        if (openDoc == null)

             return;

        IVsWindowFrame frame;

        Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp;

        IVsUIHierarchy hier;
uint itemid;

  Guid logicalView = VSConstants.LOGVIEWID_Code;

  if (ErrorHandler.Failed(

      openDoc.OpenDocumentViaProject(path, ref logicalView,

        out sp, out hier, out itemid, out frame))

            || frame == null)

             return;

  object docData;

  frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData,

      out docData);



  VsTextBuffer buffer = docData as VsTextBuffer;

  if (buffer == null)

  {

        IVsTextBufferProvider bufferProvider =

             docData as IVsTextBufferProvider;

        if (bufferProvider != null)

        {

            IVsTextLines lines;

            ErrorHandler.ThrowOnFailure(

             bufferProvider.GetTextBuffer(out lines));

            buffer = lines as VsTextBuffer;

            if (buffer == null)

             return;

        }

  }

IVsTextManager mgr =

 Package.GetGlobalService(typeof(VsTextManagerClass))

 as IVsTextManager;

if (mgr == null)
return;

    mgr.NavigateToLineAndColumn(

      buffer, ref logicalView, line, column, line, column);

}

Ё-мое... Ужас. Набор магических заклинаний. Этот код, будучи написанным на C#, опять же ничем
не упростил жизнь своему разработчику. Кто скажет, что это будет лучше выглядеть на языке XYZ?
Язык здесь "перпендикулярен" к решаемой задаче и практически не оказывает влияния.


Работа с датой
Ну, уж работа с датами в C# должна быть точно на высоте! Ведь там столько разных удобных
форматов сделали. Думал я, до тех пор, пока из внешней программы не пришло время в формате
__time64_t, а в C# коде необходимо было использовать класс DateTime.

Конвертировать __time64_t в DataTime конечно не сложно, для этого всего лишь надо написать
функцию типа такой:

public static DateTime Time_T2DateTime(long time_t)

{

    //116444736000000000 - это 1600 год

    long win32FileTime = 10000000 * time_t + 116444736000000000;

    return DateTime.FromFileTime(win32FileTime);

}

И здесь C# оказался ничем не лучше... Возможно конечно я не нашел функцию конвертации. Ну,
неужели нельзя было у DateTime нужный конструктор сделать? Ну почему, как взаимодействие с
окружающей средой, так опять все по старинке "ручками" делать приходится?


Перебор всех проектов одного решения (solution)
Для некоторой задачи необходимо перебрать все проекты, которые есть в решении (Visual Studio
solution).

Вместо простого и элегантного foreach код выглядит так:

Solution2 solution = PVSStudio.DTE.Solution as Solution2;

SolutionBuild2 solutionBuild =

     (SolutionBuild2)solution.SolutionBuild;

SolutionContexts projectContexts =

     solutionBuild.ActiveConfiguration.SolutionContexts;
int prjCount = projectContexts.Count;

for (int i = 1; i <= prjCount; i++)

{

      SolutionContext projectContext = null;

      try

      {

            projectContext = projectContexts.Item(i);

      }

      catch (Exception)

      {

            // try/catch block is a workaround.

            // It's needed for correct working on solution

            // with unloaded projects.

            continue;

      }

...

Во-первых, оказывается, что foreach для этого класса недоступен. Но ладно, for-ом пользоваться
еще не разучились. Во-вторых, если обратиться к элементу, который в наборе есть, но у него "не
очень корректное" состояние – то летит исключение. В результате код заметно усложняется. А код
на C# опять ничем не отличается от кода на другом языке.


Выводы
Данным постом я хотел показать, что далеко не всегда код на C# (или другом языке) проще, чем
код на C/C++. И поэтому слепо верить в то, что "надо все переписать на C#" не нужно. Тем не
менее, я совершенно не считаю, что "C# - отстой", поскольку во многих местах он действительно
упрощает жизнь.

В чем причины того, что указанные в посте фрагменты кода выглядят также сложно, как и на C++?

    1. Взаимодействие с различными API. Например, как здесь было взаимодействие с Visual
       Studio API.
    2. Взаимодействие с программами на других языках. Например, тип __time64_t конечно же
       пришел от C++-приложения.
    3. Взаимодействие с операционной системой. Далеко не всегда удается состыковать
       красивый и правильный C#-код с реальностью в лице Windows.
    4. Несовершенство алгоритмов обработки данных. Если вы работаете со строками, то от "+1"
       в коде вы никуда не денетесь, на каком бы языке вы не писали.
Оправдания по использованному в примерах коду:

   1. Комментарии вырезаны, код сокращен до минимально необходимого в статье.
   2. Да, авторы кода не умеют писать на C#, но от этого C# не становится волшебнее.

О сложностях программирования, или C# нас не спасет?

  • 1.
    О сложностях программирования,или C# нас не спасет? Автор: Евгений Рыжков Дата: 26.10.2010 Программирование это сложно. С этим никто, надеюсь, не спорит. Но вот тема новых языков программирования, а точнее поиск "серебряной пули" всегда находит бурных отклик в умах разработчиков программного обеспечения. Особенно "модной" является тема превосходства одного языка программирования над другим. Ну, к примеру, что C# "круче", чем C++. И хотя holy wars – это не та причина, по которой я пишу этот пост, тем не менее, что называется "наболело". Ну не поможет C#/lisp/F#/Haskell/... написать изящное приложение, взаимодействующее с внешним миром, и все тут. Вся изящность теряется, стоит захотеть написать что-то реальное, а не пример "сам в себе". В тексте несколько фрагментов на C#, взятые из модуля интеграции статического анализатора кода PVS-Studio в популярную среду Microsoft Visual Studio. Этими фрагментами я хочу показать, что писать, к примеру, на C# совсем не проще, чем на, C++. Простой combobox Этак, первый фрагмент кода – обработка выбора одной из трех строк в ОБЫЧНОМ combobox на панели инструментов с картинки. Рисунок 1 – Простой combobox на три строчки И вот для обработки такой вот фитюльки требуется следующий код. К сожалению, пришлось изменить форматирование и убрать комментарии. Так что прошу прощения за ужас. private void OnMenuMyDropDownCombo(object sender, EventArgs e) { if (e == EventArgs.Empty) { throw (new ArgumentException());
  • 2.
    } OleMenuCmdEventArgs eventArgs =e as OleMenuCmdEventArgs; if (eventArgs != null) { string newChoice = eventArgs.InValue as string; IntPtr vOut = eventArgs.OutValue; if (vOut != IntPtr.Zero && newChoice != null) { throw (new ArgumentException()); } else if (vOut != IntPtr.Zero) { Marshal.GetNativeVariantForObject( this.currentDropDownComboChoice, vOut); } else if (newChoice != null) { bool validInput = false; int indexInput = -1; for (indexInput = 0; indexInput < dropDownComboChoices.Length; indexInput++) { if (String.Compare( dropDownComboChoices[indexInput], newChoice, true) == 0)
  • 3.
    { validInput = true; break; } } if (validInput) { this.currentDropDownComboChoice = dropDownComboChoices[indexInput]; if (currentDropDownComboChoice == Resources.Viva64) UseViva64Analysis(null, null); else if (currentDropDownComboChoice == Resources.GeneralAnalysis) UseGeneralAnalysis(null, null); else if (currentDropDownComboChoice == Resources.VivaMP) UseVivaMPAnalysis(null, null); else { throw (new ArgumentException()); } } else { throw (new ArgumentException()); } } else
  • 4.
    { throw (new ArgumentException()); } } else { throw (new ArgumentException()); } } Причем здесь IntPtr.Zero и Marshal.GetNativeVariantForObject()? Ну так надо... Простой combobox обрабатывается совсем не просто. Причем этого кода не достаточно. Есть еще рядом функция OnMenuMyDropDownComboGetList() примерно такого же размера. Здесь C# оказался ничем не лучше любого другого языка. Нет, конечно же, круто, что он инкапсулировал от меня OLE, маршалинг. На Си все было бы намного печальней. Но вот только как-то все равно все не то, как преподносится в книгах и эвангелистами. Простота-то где? Я всего лишь хотел с выпадающим списком поработать. Навигация по коду в Visual Studio Когда в Visual Studio вы щелкаете по сообщению об ошибке, срабатывает примерно такой код для открытия файла и перехода к строке с ошибкой. public void OpenDocumentAndNavigateTo(string path, int line, int column) { IVsUIShellOpenDocument openDoc = Package.GetGlobalService( typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument; if (openDoc == null) return; IVsWindowFrame frame; Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp; IVsUIHierarchy hier;
  • 5.
    uint itemid; Guid logicalView = VSConstants.LOGVIEWID_Code; if (ErrorHandler.Failed( openDoc.OpenDocumentViaProject(path, ref logicalView, out sp, out hier, out itemid, out frame)) || frame == null) return; object docData; frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData); VsTextBuffer buffer = docData as VsTextBuffer; if (buffer == null) { IVsTextBufferProvider bufferProvider = docData as IVsTextBufferProvider; if (bufferProvider != null) { IVsTextLines lines; ErrorHandler.ThrowOnFailure( bufferProvider.GetTextBuffer(out lines)); buffer = lines as VsTextBuffer; if (buffer == null) return; } } IVsTextManager mgr = Package.GetGlobalService(typeof(VsTextManagerClass)) as IVsTextManager; if (mgr == null)
  • 6.
    return; mgr.NavigateToLineAndColumn( buffer, ref logicalView, line, column, line, column); } Ё-мое... Ужас. Набор магических заклинаний. Этот код, будучи написанным на C#, опять же ничем не упростил жизнь своему разработчику. Кто скажет, что это будет лучше выглядеть на языке XYZ? Язык здесь "перпендикулярен" к решаемой задаче и практически не оказывает влияния. Работа с датой Ну, уж работа с датами в C# должна быть точно на высоте! Ведь там столько разных удобных форматов сделали. Думал я, до тех пор, пока из внешней программы не пришло время в формате __time64_t, а в C# коде необходимо было использовать класс DateTime. Конвертировать __time64_t в DataTime конечно не сложно, для этого всего лишь надо написать функцию типа такой: public static DateTime Time_T2DateTime(long time_t) { //116444736000000000 - это 1600 год long win32FileTime = 10000000 * time_t + 116444736000000000; return DateTime.FromFileTime(win32FileTime); } И здесь C# оказался ничем не лучше... Возможно конечно я не нашел функцию конвертации. Ну, неужели нельзя было у DateTime нужный конструктор сделать? Ну почему, как взаимодействие с окружающей средой, так опять все по старинке "ручками" делать приходится? Перебор всех проектов одного решения (solution) Для некоторой задачи необходимо перебрать все проекты, которые есть в решении (Visual Studio solution). Вместо простого и элегантного foreach код выглядит так: Solution2 solution = PVSStudio.DTE.Solution as Solution2; SolutionBuild2 solutionBuild = (SolutionBuild2)solution.SolutionBuild; SolutionContexts projectContexts = solutionBuild.ActiveConfiguration.SolutionContexts;
  • 7.
    int prjCount =projectContexts.Count; for (int i = 1; i <= prjCount; i++) { SolutionContext projectContext = null; try { projectContext = projectContexts.Item(i); } catch (Exception) { // try/catch block is a workaround. // It's needed for correct working on solution // with unloaded projects. continue; } ... Во-первых, оказывается, что foreach для этого класса недоступен. Но ладно, for-ом пользоваться еще не разучились. Во-вторых, если обратиться к элементу, который в наборе есть, но у него "не очень корректное" состояние – то летит исключение. В результате код заметно усложняется. А код на C# опять ничем не отличается от кода на другом языке. Выводы Данным постом я хотел показать, что далеко не всегда код на C# (или другом языке) проще, чем код на C/C++. И поэтому слепо верить в то, что "надо все переписать на C#" не нужно. Тем не менее, я совершенно не считаю, что "C# - отстой", поскольку во многих местах он действительно упрощает жизнь. В чем причины того, что указанные в посте фрагменты кода выглядят также сложно, как и на C++? 1. Взаимодействие с различными API. Например, как здесь было взаимодействие с Visual Studio API. 2. Взаимодействие с программами на других языках. Например, тип __time64_t конечно же пришел от C++-приложения. 3. Взаимодействие с операционной системой. Далеко не всегда удается состыковать красивый и правильный C#-код с реальностью в лице Windows. 4. Несовершенство алгоритмов обработки данных. Если вы работаете со строками, то от "+1" в коде вы никуда не денетесь, на каком бы языке вы не писали.
  • 8.
    Оправдания по использованномув примерах коду: 1. Комментарии вырезаны, код сокращен до минимально необходимого в статье. 2. Да, авторы кода не умеют писать на C#, но от этого C# не становится волшебнее.