• Стоит ли сразу учить TypeScript не зная javascript?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Нет, учить TypeScript до JavaScript - плохая идея. Это все равно что учить слова, не изучив алфавит.

    Лучше заняться JavaScript, чтобы иметь представление об особенностях работы этого языка, о подводных камнях, с которыми придется столкнуться.

    TypeScript облегчит процесс написания кода, позволит избежать опечаток и синтаксических ошибок, поможет лучше организовать структуру кода. Однако если не учитывать JavaScript, то на выходе может получиться большой объем кода с множеством логических ошибок.

    Впрочем, можно использовать их вместе. Но JavaScript уделять больше внимания.

    TypeScript после JavaScript в общем-то и не нужно учить, только особенности языка освоить. Любой программист JavaScript уже и так знает TypeScript, даже если не слышал о последнем. А вот в обратную сторону будет сложнее.
    Ответ написан
    Комментировать
  • Ассоциативный массив как свойство объекта в PHP?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Вроде все работает:
    80adf4323aff4915b5bf94ebcd3438c1.png

    В каком состоянии находится $domains_ip на момент добавления данных?
    function get_ip($domain)
    {
      $ip = gethostbyname($domain);
    
      var_dump($this->domains_ip);
    
      if (!is_array($this->domains_ip))
      {
        echo 'domains_ip не является массивом!';
      }
    
      $this->domains_ip[$ip] = $domain;
      return $ip;   
    }

    Что происходит после добавления?
    function get_ip($domain)
    {
      $ip = gethostbyname($domain);		
      $this->domains_ip[$ip] = $domain;
    
      var_dump($ip);
      var_dump($this->domains_ip);
    
      return $ip;      
    }

    Что будет, если массив объявить вот так:
    private $domains_ip = [];
    Ответ написан
    7 комментариев
  • Использование переменных Sass в Media Queries?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Да, можно:
    $padding: 8px;
    
    div {
      padding: $padding; // 8px
    }
    
    
    @media (min-width: $screen-tablet) {
      $padding: 4px;
    
      div {
        padding: $padding; // 4px
      }
    }

    Главное не запутаться :-)

    Возможно лучшим решением будет не менять исходное значение, а производить вычисления:
    $padding: 8px;
    
    div {
      padding: $padding; // 8px
    }
    
    
    @media (min-width: $screen-tablet) {
      div {
        padding: ($padding - 4); // 8 - 4 = 4px
      }
    }
    Ответ написан
    Комментировать
  • Как php ssh2 диалоги команд?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    sudo можно настроить, чтобы он не требовал пароль для определенных пользователей или групп пользователей.

    sudo visudo

    либо:

    sudo nano /etc/sudoers

    где nano - текстовой редактор, можно использовать любой. (через visudo безопасней, т.к. программа проверяет наличие ошибок, перед заменой основного файла конфигурации)

    В конец файла добавить строку:

    имяПользователя ALL=(ALL) NOPASSWD:ALL

    Для групп, перед именем следует указывать символ процента (%). Например, %admin - правило для группы admin.

    Затем перезапустить sudo:

    sudo service sudo restart

    После этого, для указанного пользователя пароль запрашиваться не будет.

    Запись лучше добавлять в конец файла, чтобы разрешающее правило не пересеклось с запрещающими/ограничивающими (иначе не будет работать).
    Ответ написан
    Комментировать
  • Кнопка поделиться в соц сетях для каждой записи?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Share42 - неплохой готовый скрипт.

    <h2>Запись #1</h2>
    <div class="share42init" 
      data-url="http://ссылка на запись" 
      data-title="Заголовок записи" 
      data-description="Описание записи">
    </div>
    
    <h2>Запись #2</h2>
    <div class="share42init" 
      data-url="http://ссылка на запись 2"
      data-title="Заголовок записи 2">
    </div>
    
    <h2>Запись #3</h2>
    <div class="share42init" 
      data-url="http://ссылка на запись 3" 
      data-title="Заголовок записи 3">
    </div>
    
    <script type="text/javascript" src="http://site.name/share42/share42.js"></script>
    Ответ написан
    Комментировать
  • Стоит ли учиться писать скрипты на bash?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Конечно стоит изучать bash. В системах Linux многое на нем сделано, так что в любом случае придется с ним сталкиваться.

    Я не учил bash намеренно. В какой-то момент просто взял и начал на нем писать код :-) Могу сказать, что язык ужасный, привередливый, местами крайне неудобный, перенасыщен подводными камнями (точнее морским ежами и минами). Но в то же время bash достаточно мощный язык.

    Для небольших скриптов будет проще использовать bash. Для чего-то сложного лучше использовать нормальный язык программирования.

    Для понимания основ будет достаточно хороших знаний и опыта работы с любым другим языком программирования; это поможет задавать правильные вопросы и быстро находить ответы.

    В основе идеологии Unix-подобных систем лежит простота. Так что с чем-то сложным скорее всего придется сталкиваться редко. Знаний программирования или даже основ программирования, должно вполне хватить для понимания скриптов bash, с которыми придется сталкиваться. Но пару недель его хорошенько поковырять, лишним не будет, главное мотивацию найти.
    Ответ написан
    Комментировать
  • Как в родительской форме перехватить закрытие дочерней?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Просто обработчик события закрытия формы добавить:
    var f = new Form2();
    f.Owner = this;
    
    f.FormClosed += (object s, FormClosedEventArgs args) =>
    {
      MessageBox.Show("Форма закрыта!");
    };
    
    f.Show();

    Либо так:
    private void button1_Click(object sender, EventArgs e)
    {
      var f = new Form2();
      f.Owner = this;
    
      f.FormClosed += Form2_FormClosed;
    
      f.Show();
    }
    
    private void Form2_FormClosed(object sender, FormClosedEventArgs e)
    {
      MessageBox.Show("Форма закрыта!");
    }

    Еще можно показать дочернюю форму как диалог (для контейнера Mdi не подойдет):
    var f = new Form2();
    f.Owner = this;
    // главная форма будет недоступна
    f.ShowDialog();
    // после закрытия диалоговой формы, выполнение продолжится
    MessageBox.Show("Форма закрыта!");
    Ответ написан
    Комментировать
  • Как вызвать хранимую процедуру из приложения?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    dataAdapter = new SqlDataAdapter("Имя процедуры", connectionString);
    dataAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;

    Либо с использованием SqlCommand:
    var connection = new SqlConnection(connectionString);
    // connection.Open();
    var cmd = new SqlCommand("Имя процедуры", connection);
    cmd.CommandType = CommandType.StoredProcedure;
    dataAdapter = new SqlDataAdapter(cmd);
    Ответ написан
    Комментировать
  • Как указать корень локального сайта?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Указать mysite.ru.local в файле конфигурации сайта:
    <VirtualHost 127.0.0.1:80>
      DocumentRoot /home/mysite.ru/www
      ServerName mysite.ru.local
      # ServerAlias mysite.ru
      # ...все остальное...
    </VirtualHost>

    Чтобы в браузере сайт открывался по указанному адресу, можно прописать его в файле hosts.

    Под Linux см. файл /etc/hosts.
    Под Windows см. C:\Windows\System32\drivers\etc\hosts.
    (с правами администратора)

    В файл hosts нужно указать имя хоста и IP сервера. Например:

    127.0.0.1 mysite.ru.local

    Вместо .local лучше использовать другую зону, т.к. с .local могут быть проблемы. Можно просто mysite, либо mysite.serverName.
    Ответ написан
    Комментировать
  • Как переписать данный алгоритм на C#?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Подводя итоги обсуждения в комментариях.

    bytearray - не содержит текст. Это тело файла Flash.

    В строке:
    s = s + (base64[i + 1] + base64[i]);

    Следует работать с символами как со строками, а не кодами символов:
    s = s + (base64[i + 1].ToString() + base64[i].ToString());


    В остальном вроде ошибок нет. Код использования примерно такой:
    byte[] data;
    data = Convert.FromBase64String(DecryptBFF(book.Content));
    File.WriteAllBytes("output.swf", data);

    Файл output.swf можно открыть в проигрывателе Flash или браузере.

    Будем надеяться, что все законно :-)
    Ответ написан
    1 комментарий
  • Как настроить работу linkedin api на локальном сервере?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    LinkedIn вполне нормально поддерживает работу с localhost, в том числе допустимы адреса с указанием номера порта. У себя подобных ошибок не наблюдаю, даже с OAuth 1.0a.

    OAuth v1.0 адово сложный. Ко всему прочему уже давно устаревший и скорее всего в будущем будет отключен. Лучше использовать вторую версию.

    Сообщение об ошибке странное, прям как в окне консоли браузера :-) Если проблема в работе сайта, то можно попробовать кеш обновить, антивирус пнуть или браузер поменять (отключить какие-нибудь расширения).

    Как вариант, можно использовать промежуточный сервер с нормальным доменом, который перенаправит все куда нужно. Пример можно посмотреть тут (в коде страницы).
    Ответ написан
    4 комментария
  • Пустой результат mysqli_query($link,$query), где искать?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Получить информацию об ошибках при работе с MySql можно при помощи функции mysqli_error.

    printf("Errormessage: %s\n", mysqli_error($link));

    $query = "SELECT * FROM users WHERE `username`= '$username_login' LIMIT 1";

    Что будет, если убрать условие и сделать такой запрос:

    $query = "SELECT * FROM users LIMIT 1";

    Если запрос успешно выполнится, то вероятно проблема в значении переменной $username_login.

    Передавать значения из переменных прямо в текст запроса не рекомендуется. Лучше передавать через параметры, или как минимум экранировать потенциально опасные символы.

    Если результатов не будет, то необходимо проверить строку соединения и успешность соединения с базой данных. Проверить наличие ошибок при соединении можно с помощью функции mysqli_connect_error:
    $link = mysqli_connect("localhost", "имя пользователя", "пароль", "имя базы");
    
    if (!$link)
    {
        echo mysqli_connect_error();
    }
    Ответ написан
    3 комментария
  • Как сделать вывод данных не с первого значени?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    www.mysql.ru/docs/man/SELECT.html
    Выражение LIMIT может использоваться для ограничения количества строк, возвращенных командой SELECT. LIMIT принимает один или два числовых аргумента. Эти аргументы должны быть целочисленными константами. Если заданы два аргумента, то первый указывает на начало первой возвращаемой строки, а второй задает максимальное количество возвращаемых строк. При этом смещение начальной строки равно 0 (не 1):
    mysql> SELECT * FROM table LIMIT 5,10; # возвращает строки 6-15

    Если задан один аргумент, то он показывает максимальное количество возвращаемых строк:
    mysql> SELECT * FROM table LIMIT 5; # возвращает первых 5 строк


    Т.е. в вашем случае будет запрос вида:
    SELECT * FROM  `users` ORDER BY `money` DESC LIMIT 1, 2
    Ответ написан
    Комментировать
  • Как записать данных из формы в txt файл?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Использовать режим w:
    $file = fopen('mes.txt', 'w+');

    modeОписание
    'r'Открывает файл только для чтения; помещает указатель в начало файла.
    'r+'Открывает файл для чтения и записи; помещает указатель в начало файла.
    'w'Открывает файл только для записи; помещает указатель в начало файла и обрезает файл до нулевой длины. Если файл не существует - пробует его создать.
    'w+' Открывает файл для чтения и записи; помещает указатель в начало файла и обрезает файл до нулевой длины. Если файл не существует - пытается его создать.
    'a' Открывает файл только для записи; помещает указатель в конец файла. Если файл не существует - пытается его создать.
    'a+' Открывает файл для чтения и записи; помещает указатель в конец файла. Если файл не существует - пытается его создать.
    'x' Создаёт и открывает только для записи; помещает указатель в начало файла. Если файл уже существует, вызов fopen() закончится неудачей, вернёт FALSE и выдаст ошибку уровня E_WARNING. Если файл не существует, попытается его создать. Это эквивалентно указанию флагов O_EXCL|O_CREAT для внутреннего системного вызова open(2).
    'x+' Создаёт и открывает для чтения и записи; иначе имеет то же поведение что и'x'.
    'c' Открывает файл только для записи. Если файл не существует, то он создается. Если же файл существует, то он не обрезается (в отличии от 'w'), и вызов к этой функции не вызывает ошибку (также как и в случае с 'x'). Указатель на файл будет установлен на начало файла. Это может быть полезно при желании заблокировать файл (смотри flock()) перед изменением, так как использование 'w' может обрезать файл еще до того как была получена блокировка (если вы желаете обрезать файл, можно использовать функцию ftruncate() после запроса на блокировку).
    'c+'Открывает файл для чтения и записи; иначе имеет то же поведение, что и 'c'.

    php.net/manual/ru/function.fopen.php
    Ответ написан
    1 комментарий
  • Как восстановить в окне конструктора случайно удаленный контрол в winforms?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    С Windows Forms нужно быть готовым к разного рода фокусам со стороны Visual Studio.
    Даже при использовании систем контроля версий, порой приходится повозиться с подлянками в виде пропавших картинок или дочерних элементов в каком-нибудь меню или ToolStrip. Многие подобные случаи можно поправить руками, путем ручного исправления файлов Designer, но не всегда. Чем сложнее форма, тем выше риск того, что дизайнер студии что-нибудь сломает; всегда нужно быть к этому готовым.

    1. Раз появился такой вопрос, то видимо Ctrl + Z точно уже не поможет. Это первое, что нужно было сделать.

    2. Если форма не была сохранения, то для отката изменений можно было бы её просто закрыть без сохранения. Но опять же, это нужно было делать сразу, при возникновении проблемы и если обычный откат назад (Ctrl + Z) не помог.

    3. Если не используется система контроля версий, то нужно регулярно делать резервные копии исходного кода. Использование системам контроля версий будет удобней. Но даже при использовании систем контроля версий, следует регулярно делать резервные копии, и резервные копии резервных копий.

    4. Если есть сборка нужного состояния программы (или приблизительно этому состоянию) и над пропавшим интерфейсом велась работа в поте лица, без перерывов на обед и сон, и все очень сложно и не хочется повторять этот героический поступок, то можно декомпилирвать предыдущую сборку и выдернуть код описания интерфейса из нее. Например, при помощи dotPeek.

    Описание элементов формы, по умолчанию, находятся в файлах Designer. Например, элементы Form1 можно найти в файле Form1.Designer.cs. Иногда у элементов могут просто потеряться ссылки на родителей. Случай случайного удаления элемента и отката назад (Ctrl + Z) как раз-таки может привести к подобному поведению. Код в файле Designer обычно остается и ссылки можно поправить руками. Как правило, речь идет о добавлении строчек кода вида: this.родительскийЭлемент.Controls.Add(this.дочернийЭлемент). Главное понимать, что от чего отвалилось и что к чему нужно привязать. Если не понимать, то можно сделать хуже :-) (не стоит забывать про создание резервных копий). После внесения изменений в код дизайнера, следует переоткрыть конструктор форм (в идеале, перед внесением изменений в *.Designer.cs, конструктор форм (визуальный редактор форм) лучше закрыть).

    Если элемент удален полностью и использовать восстановление из предыдущей сборки, то в декомпилированном коде нужно будет найти класс формы, а в нем метод InitializeComponent. У DataGridView, каждая колонка и строка (при наличии) будут являться отдельными элементами, нужно найти и перенести весь код описания этих элементов. Как правило, для каждого отдельного элемента, установка значений свойствам в коде идет последовательно. Дочерние элементы могут идти в разброс, по именам элементов можно более ли менее понять, что к чему (если стандартные имена у элементов менялись на собственные, то проблем с поиском возникнуть не должно).
    Ответ написан
    Комментировать
  • Как локализовать проект при помощи mo файлов?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Проводить локализацию проектов PHP, и тем более WordPress, силами gettext не приходилось, но на днях делал это с некоторыми bash-скриптами. Возможно мой опыт будет полезен.

    1. Необходимо определить домен и область поиска файлов сообщений.
    Домен задается функцией textdomain, а путь к ресурсам - bindtextdomain.
    bindtextdomain('example', './local');
    textdomain('example');

    2. В коде, весь локализуемый текст следует выводить через функцию gettext. Например:
    echo gettext("Hello world!");
    echo gettext("Test123");

    Можно использовать фразы, либо ключи.

    Если использовать фразы, то не нужно будет создавать ресурсы для языка по умолчанию. Из минусов такого подхода, то что некоторые фразы могут иметь разное значение в зависимости от контекста использования, но эту проблему в принципе можно решить перефразированием.

    Если использовать ключи (например, MSG_ABOUT_TEXT1), то придется создавать ресурсы для языка по умолчанию. При отсутствии ресурсов, или если что-то сломается, пользователю будут выводиться ключи, что не очень хорошо.

    Какой подход выбрать, зависит от сложности проекта и предпочтений. Фразы проще и проблем с ними в небольших проектах точно не будет.

    3. Необходимо извлечь строки из кода и создать файлы po. Делается это при помощи набора инструментов gettext. Если ранее этим заниматься не приходилось, то скорее всего придется выполнить установку необходимых компонентов.

    Под Debian/Ubuntu установить пакет gettext можно следующей командой:

    sudo apt-get install gettext

    Под остальными разновидностями Linux систем в общем-то примерно также, только диспетчер пакетов будет другим.

    Под Windows мне не доводилось этим заниматься. Думаю, можно что-нибудь найти. Например, sourceforge.net/projects/gettext

    Я опишу только порядок действий под Linux (конкретно Debian).

    Для создания po-файла необходимо выполнить следующую команду:

    xgettext --extract-all --default-domain=example --from-code=utf-8 /путь к проекту/*.php


    Параметры:

    --default-domain - имя домена, должно соответствовать домену, который используется в коде PHP (textdomain). Не путать с доменом сайта, хотя обычно используется название проекта;
    --from-code=имя кодировки текста. Например: utf-8;
    --extract-all - указывает на необходимость извлечения всех строк.

    Дополнительно можно указать параметры:

    --output=имяФайла.po - по умолчанию строки будут извлечены в файл messages.po, этот параметр позволяет указать любое имя файла;
    --no-wrap - запрет разбития длинных строк;
    --join-existing - если есть предыдущие извлеченные строки, то эта опция указывает на необходимость склейки новых найденных строк с существующими (использовать, только если po-файл существует);
    --copyright-holder="Вася Пупкаидзе" - кому принадлежат авторские права.

    Параметр --help позволяет посмотреть список всех параметров :-)

    Программа создаст po-файл примерно следующего содержания:
    # SOME DESCRIPTIVE TITLE.
    # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
    # This file is distributed under the same license as the PACKAGE package.
    # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
    #
    #, fuzzy
    msgid ""
    msgstr ""
    "Project-Id-Version: PACKAGE VERSION\n"
    "Report-Msgid-Bugs-To: \n"
    "POT-Creation-Date: 2016-01-15 14:55+0300\n"
    "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    "Language-Team: LANGUAGE <LL@li.org>\n"
    "Language: \n"
    "MIME-Version: 1.0\n"
    "Content-Type: text/plain; charset=CHARSET\n"
    "Content-Transfer-Encoding: 8bit\n"
    
    msgid "Hello world!"
    msgstr ""
    
    msgid "Test123"
    msgstr ""


    msgid - это идентификатор сообщения.
    msgstr - перевод.

    После указания перевода и сохранения изменений в файле po:
    msgid "Hello world!"
    msgstr "Привет, мир!"
    
    msgid "Test123"
    msgstr "Тест123"

    Его необходимо компилировать в файл mo.
    Делается это при помощи команды msgfmt:
    msgfmt --output-file="/путь к файлу.mo" "/путь к файлу.po"

    Для себя я написал следующий bash-скрипт, который компилирует все файлы po в каталоге (включая все подкаталоги), в котором был запущен скрипт:
    #!/bin/bash
    
    lang="$1"
    path="$(cd "$(dirname "$0")" && pwd)"
    
    if [[ -n "$lang" ]]; then
      path="$path/$lang"
      if [[ ! -d "$path" ]]; then
        mkdir "$path"
      fi
    fi
    
    cd "$path"
    
    find "$path" -name "*.po" | while read -r f; do
      po_dir="$(dirname $f)"
      po_file="$(basename $f)"
      po_name="$(echo $po_file | cut -d'.' -f1)"
      msgfmt --output-file="$po_dir/$po_name.mo" "$f" && \
      printf "Created: $po_dir/$po_name.mo\n" || \
      printf "ERROR: Could not create mo-file from: $f\n"
    done

    Если все пройдет без эксцессов, файлы mo следует разместить по подпапкам LC_MESSAGES нужных культур, которые должны располагаться в папке указанной в коде PHP функцией bindtextdomain. Запутано вышло, проще показать :-)

    Если:
    bindtextdomain('example', './local');
    textdomain('example');

    , то структуру размещения файлов mo:
    /local/ru/LC_MESSAGES/messages.mo
    /local/en/LC_MESSAGES/messages.mo
    /local/en_US/LC_MESSAGES/messages.mo
    /local/ru_GB/LC_MESSAGES/messages.mo
    /local/[Код культуры]/LC_MESSAGES/messages.mo

    /local - часть пути, указанная в коде PHP: bindtextdomain('example', './local');
    Код культуры - это стандартный (ISO 639) двузначный код языка, или код языка с указанием кода региона (ISO 639 + ISO 3166): ru (русский язык), ru_RU (русский язык, Россия), en (английский язык), en_US (американский английский) и т.п.

    Честно говоря, я не вижу пока никаких особых преимуществ от использования gettext. Слишком сложный процесс генерирования всех этих po и mo. Проще и удобней текстовой файл с нужными ресурсами создать, или использовать базу. Хотя работу с gettext можно автоматизировать, но придется потратить на это время.
    Ответ написан
    Комментировать
  • C# checkbox .Checked xaml?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Checked - это событие.

    Состояние можно задать свойством IsChecked:
    CheckBox cb = (CheckBox)sender;
    cb.IsChecked = true;
    Ответ написан
    1 комментарий
  • Как отписаться от события, если в качестве подписки лямбда выражение?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Проще всего запомнить ссылку и не извращаться:
    // создаем переменную типа EventHandler
    EventHandler handler = null;
    // создаем анонимный метод для обработки события и передаем его переменной
    handler = (sender, e) =>
    {
      MessageBox.Show("Hello!");
      // удаляем обработчик
      ((Button)sender).Click -= handler;
    };
    // добавляем обработчик
    button1.Click += handler;

    Плохое решение может выглядеть так:
    // добавляем обработчик
    button1.Click += (sender, e) =>
    {
      MessageBox.Show("Hello!");
    
      // получаем поле EventClick
      var f = typeof(Control).GetField
      (
        "EventClick", 
        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static
      );
    
      // получаем значение EventClick для sender
      var k = f.GetValue(sender);
    
      // получаем свойство 
      var p = sender.GetType().GetProperty
      (
        "Events", BindingFlags.NonPublic | BindingFlags.Instance
      );
    
      // получаем значение свойства Events для sender
      var events = (EventHandlerList)p.GetValue(sender, null);
    
      // удаляем обработчик из списка событий
      events.RemoveHandler(k, events[k]);
    };

    В собственных классах проще получить список делегатов при помощи GetInvocationList (внутри класса):
    public class Toster
    {
    
      public event EventHandler AnyEvent;
    
      public void AnyEventHarakiri()
      {
        foreach (Delegate d in this.AnyEvent.GetInvocationList())
        {
          this.AnyEvent -= (EventHandler)d;
        }
      }
    
    }

    var t = new Toster();
    t.AnyEvent += (sender, e) => { };
    t.AnyEventHarakiri();

    Но вариант со ссылками лучше.
    Ответ написан
    3 комментария
  • C# После загрузки картинки в picturebox удалить её с диска?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Работать с файлами лучше через FileStream, чтобы полностью контролировать весь процесс:
    string filePath = "123.jpg";
    
    using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Inheritable))
    {
      pictureBox1.Image = Image.FromStream(file);
    }
    
    File.Delete(filePath);

    Многие упрощенные методы работы с файлами нередко блокируют доступ к файлам. Такие методы имеет смысл использовать только для одноразовых файловых операций (когда нужно один раз прочитать или записать данные и не более этого).
    Ответ написан
    Комментировать
  • В чём польза readonly полей в C#?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Значения полей readonly можно менять динамически, в конструкторе класса. Последующие изменения невозможны.

    public class MyClass
    {
    
      public readonly int Value = 0;
    
      public MyClass()
      {
        this.Value = 123;
      }
    
    }


    Для чего это использовать - зависит от фантазии.

    Например, можно создать класс User и сделать readonly поле, содержащее идентификатор пользователя. Таким образом, идентификатор пользователя можно будет указать только при инициализации класса и, в последующем, изменить его будет невозможно. Это позволит избежать проблем в логике, которые еще предстоит придумать :-)

    Если сравнивать со свойствами и константами, то значения readonly свойств могут меняться в процессе жизненного цикла класса. Значения readonly полей, как уже было сказано ранее, можно указывать только при объявлении и в конструкторе класса.

    Значения констант являются фиксированными и изменять их программно невозможно. Количество типов для констант ограничено, в то время как поля могут иметь любой тип. Константы являются статическими, а поля не обязаны быть таковыми. В этом плане, readonly поля более гибкие, чем константы.

    В дополнение, немного кода (Console Application):
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net;
    using System.Timers;
    using System.Threading;
    
    namespace ConsoleApplication20
    {
    
      class Program
      {
        static void Main(string[] args)
        {
          // создаем экземпляр класса
          var t = new Toster(199065);
          // выводим значение константы ProjectName
          // доступ только через Toster
          Console.WriteLine(Toster.ProjectName);
          // доступа к локальным константам здесь нет
          // Toster.CounterPattern
          // t.CounterPattern
    
          // выводим заголовок вопроса
          Console.WriteLine(t.Title);
    
          // бесконечность - не предел!
          while (true)
          {
            // t.Id = 123;
            // мы не можем поменять идентификатор
            // следовательно, этот код, логически, будет работать правильно
    
            // выводим число ответов
            Console.WriteLine("Ответов: {0}", t.AnswersCount);
    
            // t.AnswersCount = 123
            // мы не можем менять значение readonly свойства,
            // но оно может меняться внутри экземпляра класса
    
            // пауза 10 сек.
            Thread.Sleep(10000);
          }
        }
      }
    
      public class Toster
      {
    
        // публичная константа, доступ через Toster
        public const string ProjectName = "Тостер";
    
        // локальная константа, доступна только в рамках этого класса
        const string CounterPattern = "<span class=\"section-header__counter\" role=\"answers_counter\">";
    
        // публичные поля только для чтения
        // значение может быть установлено в конструкторе
        public readonly string Title = "Нет данных";
        public readonly int Id = 0;
        
        // свойство только для чтения
        // значение может меняться в процессе жизни экземпляра класса
        // при помощи локальной переменной
        private int _AnswersCount = 0;
        public int AnswersCount
        {
          get
          {
            return _AnswersCount;
          }
        }
    
        // это просто таймер
        private System.Timers.Timer Timer = null;
    
        // а это конструктор<s>, но не Lego</s>
        public Toster(int id)
        {
          if (id <= 0) { return; }
          
          // получаем вопрос
          var web = new WebClient();
          web.Encoding = Encoding.UTF8;
          var result = web.DownloadString(String.Format("https://toster.ru/q/{0}", id));
          // из шаблона url тоже можно сделать константу
          // либо в классе можно сделать readonly свойство, 
          // которое будет на лету формировать итоговый адрес:
          // return String.Format("https://toster.ru/q/{0}", this.Id)
    
          // устанавливаем значения для полей
          this.Id = id;
          this.Title = WebUtility.HtmlDecode
          ( 
            result.Substring
            (
              result.IndexOf("<title>") + "<title>".Length,
              result.IndexOf("</title>") - result.IndexOf("<title>") - "<title>".Length
            )
          );
          // выдергиваем число ответов на вопрос
          this.ParseAnswersCount(result);
    
          // запускаем периодическую проверку <s>Менделеева</s>
          Timer = new System.Timers.Timer(10000);
          Timer.Elapsed += Timer_Elapsed;
        }
        
        // обработчик истечения интервала времени 
        private void Timer_Elapsed(object sender, EventArgs e)
        {
          var web = new WebClient();
          web.Encoding = Encoding.UTF8;
          var result = web.DownloadString(String.Format("https://toster.ru/q/{0}", this.Id));
          this.ParseAnswersCount(result);
        }
    
        // выдергиватель количества ответов
        private void ParseAnswersCount(string value)
        {
          int startstart = value.IndexOf("Ответы на вопрос");
          int start = value.IndexOf(CounterPattern, startstart) + CounterPattern.Length;
          int len = value.IndexOf("</span>", start) - start;
          _AnswersCount = Convert.ToInt32(value.Substring(start, len));
        }
        
      }
    
    }
    Ответ написан
    Комментировать