Все записи автора Андрей Бушман

Расширенная информация об ошибке в C#

Когда-то показывал способ, которым можно в C++ точно указывать место в файлах исходного кода, где произошло исключение. В этой заметке показываю код, делающий подобное на C#, при этом наличие PDB-файлов не требуется.


using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace ThreadsLearning {
    class Program {

        private static void Main(string[] args) {
            try {
                throw newException(Log("Oops... Something wrong!")); // Line 11
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine("Hit <Enter> for exit...");
            Console.ReadLine();
        }

        // Additional info: https://channel9.msdn.com/Events/Build/BUILD2011/TOOL-816T
        private static string Log(string text,
            [CallerFilePath] string file = "",
            [CallerMemberName] string member = "",
            [CallerLineNumber] int line = 0) {
            return string.Format("{0}, {1}, method {2}, Line: {3}. Error message: {4}",
                Path.GetFileName(Assembly.GetExecutingAssembly().Location),
                Path.GetFileName(file), member, line, text);
        }
    }
}


Консольный вывод следующий:

ThreadsLearning.exe, Program.cs, method Main, Line: 11. Error message: Oops... Something wrong!
Hit <Enter> for exit...



Расширенная информация об ошибке в C#

Когда-то показывал способ, которым можно в C++ точно указывать место в файлах исходного кода, где произошло исключение. В этой заметке показываю код, делающий подобное на C#, при этом наличие PDB-файлов не требуется.


using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace ThreadsLearning {
    class Program {

        private static void Main(string[] args) {
            try {
                throw newException(Log("Oops... Something wrong!")); // Line 11
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine("Hit <Enter> for exit...");
            Console.ReadLine();
        }

        // Additional info: https://channel9.msdn.com/Events/Build/BUILD2011/TOOL-816T
        private static string Log(string text,
            [CallerFilePath] string file = "",
            [CallerMemberName] string member = "",
            [CallerLineNumber] int line = 0) {
            return string.Format("{0}, {1}, method {2}, Line: {3}. Error message: {4}",
                Path.GetFileName(Assembly.GetExecutingAssembly().Location),
                Path.GetFileName(file), member, line, text);
        }
    }
}


Консольный вывод следующий:

ThreadsLearning.exe, Program.cs, method Main, Line: 11. Error message: Oops... Something wrong!
Hit <Enter> for exit...



Установка в Linux Mint 18.3 различных инструментов, используемых для программирования

Мне не нравится современный пользовательский интерфейс Linux Ubuntu, но в то же время мне нравится более классический интерфейс Linux Mint 18.3, построенной (согласно официальной информации) на базе Ubuntu 16.04. Поэтому на моём ноутбуке установлен Linux Mint 18.3.

Поскольку меня интересует программирование на языках C, C#, JavaScript и Java, то в данной заметке я размещаю краткую шпаргалку о том, какой софт можно установить в Linux Mint 18.3 для возможности разработки софта с использованием упомянутых выше языков программирования.

GCC

В качестве компилятора для языка C я конечно же предпочитаю использовать gcc. К сожалению, по умолчанию, в Linux Mint 18.3 установлена весьма старая (5-я) версия этого компилятора. Информацию о версии установленной у вас версии gcc всегда можно получить так:

gcc --version

Инструкцию о том, как можно обновить gcc можно найти здесь. Последовательно выполнив все обозначенные в ней действия, мне без труда удалось успешно обновить gcc до наиболее свежей на сегодняшний день версии (7-й).

На всякий случай дублирую содержимое ссылки, дабы в случае удаления кем-либо указанной выше информации она бы не была безвозвратно утеряна:

These commands are based on a askubuntu answer http://askubuntu.com/a/581497 and https://askubuntu.com/questions/26498/choose-gcc-and-g-version
To install gcc-7 (gcc-7.2.0), I had to do more stuff as shown below.
USE THOSE COMMANDS AT YOUR OWN RISK. I SHALL NOT BE RESPONSIBLE FOR ANYTHING.
ABSOLUTELY NO WARRANTY.

If you are still reading let's carry on with the code.

sudo apt-get update && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-snapshot -y && \
sudo apt-get update && \
sudo apt-get install gcc-7 g++-7 gcc-6 g++-6 gcc-multilib -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 && \
sudo apt-get install gcc-5 g++-5 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5;

When completed, you must change to the gcc you want to work with by default. Type in your terminal:
sudo update-alternatives --config gcc

To verify if it worked. Just type in your terminal
gcc -v

If everything went fine you should see gcc 7.2.0 by the time I am writing this gist

Happy coding!

See blog post at https://www.application2000.com

Помимо того, что gcc можно использовать непосредственно из консоли, его так же используют и различного рода IDE, например, используемый мною CLion компании JetBrains. Эта IDE позволяет писать на C90, С99 и даже С11, в отличии от  той жеVisual Studio, позволяющей писать только на  C90. 

VIM & IDE

В качестве текстового редактора я предпочитаю vim. Это приложение особенно полезно для тех, кто владеет слепой десятипальцевой печатью, которой можно обучиться, например, на сайте Владимира Шахиджаняна:  https://solo.nabiraem.ru/ - свои навыки я когда-то получил именно там.

Компания JetBrains предоставляет набор кроссплатформенных IDE для интересующих меня языков программирования:
Для каждого из этих редакторов присутствует возможность установить плагин IdeaVim, позволяющий эмулировать vim в качестве текстового редактора IDE. Т.о. навыки использования vim могут быть успешно использованы в используемых мною IDE.

Установка vim:

sudo apt-get install vim

Проверка установленной версии vim:

vim --version

Краткая обучающая инструкция по базовым основам использования vim:

vimtutor

По vim имеются хорошие книги, в т.ч. и на русском языке:
Установку софта из дистрибутивов, скачанных с сайта JetBrains, можно выполнять из консоли следующим образом:

sudo tar -xzf your.tar.gz -C /opt

В результате приложение будет установлено в каталог /opt.

Visual Studio Code

На официальном сайте Майкрософт можно скачать и установить самый свежий дистрибутив данного кроссплатформенного текстового редактора.

Среди доступных плагинов для этого текстового редактора так же имеется большой набор эмуляторов vim.

Atom

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

Для него так же можно скачать плагин эмуляции vim, как описано здесь:

apm install vim-mode-plus

Git

Вряд ли эта программа нуждается в представлении. На официальном сайте всегда можно скачать и установить самый свежий дистрибутив программы.

JDK

SDK для Java. На официальном сайте можно скачать и установить самую свежую версию JDK, необходимую для разработки кода на Java. Установленный JDK будет использоваться в IntelliJ Idea.

Node.js

Платформа, предоставляющая возможность разрабатывать приложения на JavaScript. С официального сайта устанавливать лучше LTS-версию. При установке Node.js автоматически будет установлен и менеджер пакетов NPM

NPM можно использовать не только для JavaScript, но и в проектах ASP.NET Core MVC 2.

.NET Core SDK

На официальном сайте Майкрософт присутствует подробная инструкция по установке самой свежей версии .NET Core SDK для Linux Ubuntu 16.04, на основе которой создан Linux Mint 18.3.

xUnit

Платформа для разработки модульных тестов для .NET и .NET Core. На официальном сайте даются ссылки на соответствующие NuGet и MyGet пакеты.

Хостинг ASP.NET Core 2 приложений в IIS

На тот случай, если своё web-приложение вы захотите хостить на IIS, компания Майкрософт опубликовала подробную инструкцию по данной теме. Особое внимание следует обратить на то, что на указанной странице, в разделе Установка пакета размещения .NET Core для Windows Server, указан пакет, который необходимо установить на сервере, чтобы IIS научился работать с вашим приложением.

Remmina

Это приложение удобно использовать в качестве RDP-клиента для удалённого подключения к компьютерам, работающим под управлением Windows.

VMWare Horizon Client

На официальном сайте присутствуют клиенты для различных операционных систем, в т.ч. и для Linux. Это приложение удобно использовать для удалённого подключения к различным виртуальным машинам, работающим под управлением VMWare.

MS SQL Server 2017

Всю необходимую информацию по теме можно найти на официальном сайте продукта.

PowerShell Core

Наличие возможности использовать PowerShell в Linux является весьма удобной  для тех, кто привык пользоваться этой командной оболочкой в Windows. Например, для тестирования контроллеров API в ASP.NET Core MVC 2 можно воспользоваться привычной командой Invoke-RestMethod.

Инструкция по установке - на официальном сайте здесь.

UPD
Ниже написал небольшой скрипт, с помощью которого оперативно установил интересующий меня набор приложений:

#!/bin/bash
# (c) Андрей Бушман, 2018
# Данный скрипт устанавливает набор необходимого мне программного обеспечения.

sudo apt-get update
sudo apt-get upgrade

# Remmina
sudo apt-add-repository ppa:remmina-ppa-team/remmina-next
sudo apt-get update
sudo apt-get install remmina remmina-plugin-rdp remmina-plugin-secret

# VMware Horizon Client v4.7.0-7395152
wget -O ./vmware.x64.bundle https://download3.vmware.com/software/view/viewclients/CART18FQ4/VMware-Horizon-Client-4.7.0-7395152.x64.bundle
chmod +x ./vmware.x64.bundle
sudo ./vmware.x64.bundle

# Skype
wget -O skype.deb https://go.skype.com/skypeforlinux-64.deb
sudo dpkg --install ./skype.deb

# Git
sudo add-apt-repository ppa:git-core/ppa
sudo apt update; apt install git

git config --global user.name "Andrey Bushman"
git config --global user.email bushman.andrey@gmail.com
git config --global core.editor "vim"

# Google Chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb

# GCC v7.3.0
sudo apt-get update && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-snapshot -y && \
sudo apt-get update && \
sudo apt-get install gcc-7 g++-7 gcc-6 g++-6 gcc-multilib -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 && \
sudo apt-get install gcc-5 g++-5 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5;
sudo update-alternatives --config gcc

# VIM
git clone https://github.com/vim/vim.git
cd vim
git pull
cd vim/src
sudo apt-get install libncurses5-dev libncursesw5-dev
# make distclean  # if you build Vim before
make
sudo make install

# .Net Core 2.1.200 SDK
wget -q packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1.200

# Visual Studio Code
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt-get update
sudo apt-get install code # or code-insiders

# Sublime Text 3
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
sudo apt-get install apt-transport-https
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt-get update
sudo apt-get install sublime-text


# Atom
curl -L https://packagecloud.io/AtomEditor/atom/gpgkey | sudo apt-key add -
sudo sh -c 'echo "deb [arch=amd64] https://packagecloud.io/AtomEditor/atom/any/ any main" > /etc/apt/sources.list.d/atom.list'
sudo apt-get update
sudo apt-get install atom

# JDK 10.0.1
wget https://download.java.net/java/GA/jdk10/10.0.1/fb4372174a714e6b8c52526dc134031e/10/openjdk-10.0.1_linux-x64_bin.tar.gz
sudo tar xvf openjdk-10.0.1_linux-x64_bin.tar.gz -C /opt

# NodeJS v8.11.1
wget -O node.tar.gz https://nodejs.org/dist/v8.11.1/node-v8.11.1-linux-x64.tar.xz
sudo tar xf ./node.tar.gz -C /opt

# JetBrains Clion 2018.1.2
wget -O ./CLion.tar.gz http://download.jetbrains.com/cpp/CLion-2018.1.2.tar.gz
sudo tar xvf CLion.tar.gz -C /opt

# JetBrains IntelliJ IDEA 2018.1.3
wget -O ./intellij.tar.gz http://download.jetbrains.com/idea/ideaIU-2018.1.3.tar.gz
sudo tar xfz ./intellij.tar.gz -C /opt

# JetBrains Rider 2018.1
wget -O ./rider.tar.gz http://download.jetbrains.com/rider/JetBrains.Rider-2018.1.tar.gz
sudo tar xfz ./rider.tar.gz -C /opt

# JetBrains WebStorm 2018.1.3
wget -O ./webstorm.tar.gz http://download.jetbrains.com/webstorm/WebStorm-2018.1.3.tar.gz
sudo tar xfz ./webstorm.tar.gz -C /opt

# JetBrains Pycharm 2018.1.2
wget -O ./pycharm.tar.gz http://download.jetbrains.com/python/pycharm-professional-2018.1.2.tar.gz
sudo tar xfz ./pycharm.tar.gz -C /opt

# JetBrains DataGrip 2018.1.2
wget -O ./datagrip.tar.gz http://download.jetbrains.com/datagrip/datagrip-2018.1.2.tar.gz
sudo tar xfz ./datagrip.tar.gz -C /opt

# JetBrains GoLand 2018.1.2
wget -O ./goland.tar.gz http://download.jetbrains.com/go/goland-2018.1.2.tar.gz
sudo tar xfz ./goland.tar.gz -C /opt

# Python v3.6.5
wget -O ./python.tar.xz https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz
tar xf ./python.tar.xz
cd ./Python-3.6.5
./configure
make
make test
sudo make install

# MS SQL Server 2017
sudo curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo add-apt-repository "$(curl https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list)"
sudo apt-get update
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list)"
sudo apt-get update
sudo apt-get install -y mssql-server
sudo /opt/mssql/bin/mssql-conf setup

systemctl status mssql-server 

# MS SQL Server Tools
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
sudo apt-get update 
sudo apt-get install mssql-tools unixodbc-dev

sudo apt-get update 
sudo apt-get install mssql-tools

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc

# Docker CE 17.03
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb https://download.docker.com/linux/$(. /etc/os-release; echo "") $(lsb_release -cs) stable"
sudo apt-get update && sudo apt-get install -y docker-ce=$(apt-cache madison docker-ce | sudo grep 17.03 | sudo head -1 | sudo awk '{print }')

# Kibernetes
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl

sudo docker info | sudo grep -i cgroup
cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

systemctl daemon-reload
systemctl restart kubelet

# PowerShell Core
sudo apt-get install -y powershell


Установка в Linux Mint 18.3 различных инструментов, используемых для программирования

Мне не нравится современный пользовательский интерфейс Linux Ubuntu, но в то же время мне нравится более классический интерфейс Linux Mint 18.3, построенной (согласно официальной информации) на базе Ubuntu 16.04. Поэтому на моём ноутбуке установлен Linux Mint 18.3.

Поскольку меня интересует программирование на языках C, C#, JavaScript и Java, то в данной заметке я размещаю краткую шпаргалку о том, какой софт можно установить в Linux Mint 18.3 для возможности разработки софта с использованием упомянутых выше языков программирования.

GCC

В качестве компилятора для языка C я конечно же предпочитаю использовать gcc. К сожалению, по умолчанию, в Linux Mint 18.3 установлена весьма старая (5-я) версия этого компилятора. Информацию о версии установленной у вас версии gcc всегда можно получить так:

gcc --version

Инструкцию о том, как можно обновить gcc можно найти здесь. Последовательно выполнив все обозначенные в ней действия, мне без труда удалось успешно обновить gcc до наиболее свежей на сегодняшний день версии (7-й).

На всякий случай дублирую содержимое ссылки, дабы в случае удаления кем-либо указанной выше информации она бы не была безвозвратно утеряна:

These commands are based on a askubuntu answer http://askubuntu.com/a/581497 and https://askubuntu.com/questions/26498/choose-gcc-and-g-version
To install gcc-7 (gcc-7.2.0), I had to do more stuff as shown below.
USE THOSE COMMANDS AT YOUR OWN RISK. I SHALL NOT BE RESPONSIBLE FOR ANYTHING.
ABSOLUTELY NO WARRANTY.

If you are still reading let's carry on with the code.

sudo apt-get update && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-snapshot -y && \
sudo apt-get update && \
sudo apt-get install gcc-7 g++-7 gcc-6 g++-6 gcc-multilib -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 && \
sudo apt-get install gcc-5 g++-5 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5;

When completed, you must change to the gcc you want to work with by default. Type in your terminal:
sudo update-alternatives --config gcc

To verify if it worked. Just type in your terminal
gcc -v

If everything went fine you should see gcc 7.2.0 by the time I am writing this gist

Happy coding!

See blog post at https://www.application2000.com

Помимо того, что gcc можно использовать непосредственно из консоли, его так же используют и различного рода IDE, например, используемый мною CLion компании JetBrains. Эта IDE позволяет писать на C90, С99 и даже С11, в отличии от  той жеVisual Studio, позволяющей писать только на  C90. 

VIM & IDE

В качестве текстового редактора я предпочитаю vim. Это приложение особенно полезно для тех, кто владеет слепой десятипальцевой печатью, которой можно обучиться, например, на сайте Владимира Шахиджаняна:  https://solo.nabiraem.ru/ - свои навыки я когда-то получил именно там.

Компания JetBrains предоставляет набор кроссплатформенных IDE для интересующих меня языков программирования:
Для каждого из этих редакторов присутствует возможность установить плагин IdeaVim, позволяющий эмулировать vim в качестве текстового редактора IDE. Т.о. навыки использования vim могут быть успешно использованы в используемых мною IDE.

Установка vim:

sudo apt-get install vim

Проверка установленной версии vim:

vim --version

Краткая обучающая инструкция по базовым основам использования vim:

vimtutor

По vim имеются хорошие книги, в т.ч. и на русском языке:
Установку софта из дистрибутивов, скачанных с сайта JetBrains, можно выполнять из консоли следующим образом:

sudo tar -xzf your.tar.gz -C /opt

В результате приложение будет установлено в каталог /opt.

Visual Studio Code

На официальном сайте Майкрософт можно скачать и установить самый свежий дистрибутив данного кроссплатформенного текстового редактора.

Среди доступных плагинов для этого текстового редактора так же имеется большой набор эмуляторов vim.

Atom

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

Для него так же можно скачать плагин эмуляции vim, как описано здесь:

apm install vim-mode-plus

Git

Вряд ли эта программа нуждается в представлении. На официальном сайте всегда можно скачать и установить самый свежий дистрибутив программы.

JDK

SDK для Java. На официальном сайте можно скачать и установить самую свежую версию JDK, необходимую для разработки кода на Java. Установленный JDK будет использоваться в IntelliJ Idea.

Node.js

Платформа, предоставляющая возможность разрабатывать приложения на JavaScript. С официального сайта устанавливать лучше LTS-версию. При установке Node.js автоматически будет установлен и менеджер пакетов NPM

NPM можно использовать не только для JavaScript, но и в проектах ASP.NET Core MVC 2.

.NET Core SDK

На официальном сайте Майкрософт присутствует подробная инструкция по установке самой свежей версии .NET Core SDK для Linux Ubuntu 16.04, на основе которой создан Linux Mint 18.3.

xUnit

Платформа для разработки модульных тестов для .NET и .NET Core. На официальном сайте даются ссылки на соответствующие NuGet и MyGet пакеты.

Хостинг ASP.NET Core 2 приложений в IIS

На тот случай, если своё web-приложение вы захотите хостить на IIS, компания Майкрософт опубликовала подробную инструкцию по данной теме. Особое внимание следует обратить на то, что на указанной странице, в разделе Установка пакета размещения .NET Core для Windows Server, указан пакет, который необходимо установить на сервере, чтобы IIS научился работать с вашим приложением.

Remmina

Это приложение удобно использовать в качестве RDP-клиента для удалённого подключения к компьютерам, работающим под управлением Windows.

VMWare Horizon Client

На официальном сайте присутствуют клиенты для различных операционных систем, в т.ч. и для Linux. Это приложение удобно использовать для удалённого подключения к различным виртуальным машинам, работающим под управлением VMWare.

MS SQL Server 2017

Всю необходимую информацию по теме можно найти на официальном сайте продукта.

PowerShell Core

Наличие возможности использовать PowerShell в Linux является весьма удобной  для тех, кто привык пользоваться этой командной оболочкой в Windows. Например, для тестирования контроллеров API в ASP.NET Core MVC 2 можно воспользоваться привычной командой Invoke-RestMethod.

Инструкция по установке - на официальном сайте здесь.

UPD
Ниже написал небольшой скрипт, с помощью которого оперативно установил интересующий меня набор приложений:

#!/bin/bash
# (c) Андрей Бушман, 2018
# Данный скрипт устанавливает набор необходимого мне программного обеспечения.

sudo apt-get update
sudo apt-get upgrade

# Remmina
sudo apt-add-repository ppa:remmina-ppa-team/remmina-next
sudo apt-get update
sudo apt-get install remmina remmina-plugin-rdp remmina-plugin-secret

# VMware Horizon Client v4.7.0-7395152
wget -O ./vmware.x64.bundle https://download3.vmware.com/software/view/viewclients/CART18FQ4/VMware-Horizon-Client-4.7.0-7395152.x64.bundle
chmod +x ./vmware.x64.bundle
sudo ./vmware.x64.bundle

# Skype
wget -O skype.deb https://go.skype.com/skypeforlinux-64.deb
sudo dpkg --install ./skype.deb

# Git
sudo add-apt-repository ppa:git-core/ppa
sudo apt update; apt install git

git config --global user.name "Andrey Bushman"
git config --global user.email bushman.andrey@gmail.com
git config --global core.editor "vim"

# Google Chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb

# GCC v7.3.0
sudo apt-get update && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update && \
sudo apt-get install gcc-snapshot -y && \
sudo apt-get update && \
sudo apt-get install gcc-7 g++-7 gcc-6 g++-6 gcc-multilib -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 && \
sudo apt-get install gcc-5 g++-5 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5;
sudo update-alternatives --config gcc

# VIM
git clone https://github.com/vim/vim.git
cd vim
git pull
cd vim/src
sudo apt-get install libncurses5-dev libncursesw5-dev
# make distclean  # if you build Vim before
make
sudo make install

# .Net Core 2.1.200 SDK
wget -q packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1.200

# Visual Studio Code
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt-get update
sudo apt-get install code # or code-insiders

# Sublime Text 3
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
sudo apt-get install apt-transport-https
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt-get update
sudo apt-get install sublime-text


# Atom
curl -L https://packagecloud.io/AtomEditor/atom/gpgkey | sudo apt-key add -
sudo sh -c 'echo "deb [arch=amd64] https://packagecloud.io/AtomEditor/atom/any/ any main" > /etc/apt/sources.list.d/atom.list'
sudo apt-get update
sudo apt-get install atom

# JDK 10.0.1
wget https://download.java.net/java/GA/jdk10/10.0.1/fb4372174a714e6b8c52526dc134031e/10/openjdk-10.0.1_linux-x64_bin.tar.gz
sudo tar xvf openjdk-10.0.1_linux-x64_bin.tar.gz -C /opt

# NodeJS v8.11.1
wget -O node.tar.gz https://nodejs.org/dist/v8.11.1/node-v8.11.1-linux-x64.tar.xz
sudo tar xf ./node.tar.gz -C /opt

# JetBrains Clion 2018.1.2
wget -O ./CLion.tar.gz http://download.jetbrains.com/cpp/CLion-2018.1.2.tar.gz
sudo tar xvf CLion.tar.gz -C /opt

# JetBrains IntelliJ IDEA 2018.1.3
wget -O ./intellij.tar.gz http://download.jetbrains.com/idea/ideaIU-2018.1.3.tar.gz
sudo tar xfz ./intellij.tar.gz -C /opt

# JetBrains Rider 2018.1
wget -O ./rider.tar.gz http://download.jetbrains.com/rider/JetBrains.Rider-2018.1.tar.gz
sudo tar xfz ./rider.tar.gz -C /opt

# JetBrains WebStorm 2018.1.3
wget -O ./webstorm.tar.gz http://download.jetbrains.com/webstorm/WebStorm-2018.1.3.tar.gz
sudo tar xfz ./webstorm.tar.gz -C /opt

# JetBrains Pycharm 2018.1.2
wget -O ./pycharm.tar.gz http://download.jetbrains.com/python/pycharm-professional-2018.1.2.tar.gz
sudo tar xfz ./pycharm.tar.gz -C /opt

# JetBrains DataGrip 2018.1.2
wget -O ./datagrip.tar.gz http://download.jetbrains.com/datagrip/datagrip-2018.1.2.tar.gz
sudo tar xfz ./datagrip.tar.gz -C /opt

# JetBrains GoLand 2018.1.2
wget -O ./goland.tar.gz http://download.jetbrains.com/go/goland-2018.1.2.tar.gz
sudo tar xfz ./goland.tar.gz -C /opt

# Python v3.6.5
wget -O ./python.tar.xz https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz
tar xf ./python.tar.xz
cd ./Python-3.6.5
./configure
make
make test
sudo make install

# MS SQL Server 2017
sudo curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo add-apt-repository "$(curl https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list)"
sudo apt-get update
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list)"
sudo apt-get update
sudo apt-get install -y mssql-server
sudo /opt/mssql/bin/mssql-conf setup

systemctl status mssql-server 

# MS SQL Server Tools
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
sudo apt-get update 
sudo apt-get install mssql-tools unixodbc-dev

sudo apt-get update 
sudo apt-get install mssql-tools

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc

# Docker CE 17.03
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb https://download.docker.com/linux/$(. /etc/os-release; echo "") $(lsb_release -cs) stable"
sudo apt-get update && sudo apt-get install -y docker-ce=$(apt-cache madison docker-ce | sudo grep 17.03 | sudo head -1 | sudo awk '{print }')

# Kibernetes
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl

sudo docker info | sudo grep -i cgroup
cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

systemctl daemon-reload
systemctl restart kubelet

# PowerShell Core
sudo apt-get install -y powershell


Приведение к нужному типу объекта, полученного из JSON

Комментарий в коде показывает место, в котором порой допускают ошибку в процессе решения обозначенной задачи.


// JS6
class Foo{
constructor(name,surname){
this.name=name;
this.surname=surname;
};
  fullName(){
  return this.name + ' ' + this.surname;
  };
};

let foo = new Foo('John', 'Smith');
console.log(foo.fullName());

let json = JSON.stringify(foo);
let _foo = JSON.parse(json);

/* ВНИМАНИЕ! Прототип экземпляров класса хранится в его свойстве prototype.
 * Т.е. пытаться получить нужный прототип через Object.getPrototypeOf(Foo) в данном
 * случае было бы неправильно. */
Object.setPrototypeOf(_foo, Foo.prototype);

console.log(_foo.fullName());

Приведение к нужному типу объекта, полученного из JSON

Комментарий в коде показывает место, в котором порой допускают ошибку в процессе решения обозначенной задачи.


// JS6
class Foo{
constructor(name,surname){
this.name=name;
this.surname=surname;
};
  fullName(){
  return this.name + ' ' + this.surname;
  };
};

let foo = new Foo('John', 'Smith');
console.log(foo.fullName());

let json = JSON.stringify(foo);
let _foo = JSON.parse(json);

/* ВНИМАНИЕ! Прототип экземпляров класса хранится в его свойстве prototype.
 * Т.е. пытаться получить нужный прототип через Object.getPrototypeOf(Foo) в данном
 * случае было бы неправильно. */
Object.setPrototypeOf(_foo, Foo.prototype);

console.log(_foo.fullName());

Многопоточность и GUI

Маленький пример использования многопоточности в приложениях, содержащих графический пользовательский интерфейс (GUI). Продемонстрировано два способа обращения к элементам пользовательского интерфейса из рабочего потока в UI-поток. Графический интерфейс при этом не "подвисает".

.Net Framework 4.5

 /* Sandbox.cs
 * © Andrey Bushman, 2017
 *
 * Небольшой пример создания дополнительного потока,
 * работающего параллельно с потоком пользовательского
 * интерфейса (UI) и обновляющего этот интерфейс по мере
 * необходимости. Дополнительных потоков можно создавать
 * сколько угодно. В данном примере для простоты создаётся
 * только один.
 *
 * В данном примере вместо прямого использоватия потока
 * (Thread) я использую задачу (Task), которая в свою очередь
 * использует пул потоков.
 *
 * Рабочий поток (т.е. задача) вычисляет текущую дату и время,
 * после чего записывает их в ListBox, находящийся в потоке UI.
 *
 * Снятие/установка галочки "Do work" управляет
 * стартом/завершением рабочего потока. Кнопка "Clear" очищает
 * ListBox и заголовок окна.
 *
 * В консоль выводятся идентификаторы текущих потоков и маркер
 * их принадлежности (или не принадлежности) к пулу потоков.
 *
 * В данном примере для обращения к потоку UI из рабочего
 * потока я использую два способа: диспетчер и контекст
 * синхронизации.
 *
 * В примере используется WPF, но всё то же самое применимо к
 * WinForms и ASP.NET.
 */
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace Bushman.Sandbox {

    class MyWindow : Window {

        SynchronizationContext context = null;

        public MyWindow() : base() {

            Console.Title = "Sandbox";
            string prefix = "Main Window";
            Title = prefix;
            Topmost = true;

            Width = 300;
            Height = 600;

            ResizeMode = ResizeMode.NoResize;

            WindowStartupLocation = WindowStartupLocation
                .CenterScreen;

            Grid grid = new Grid();
            grid.RowDefinitions.Add(new RowDefinition());
            grid.RowDefinitions.Add(new RowDefinition());
            grid.ColumnDefinitions.Add(new ColumnDefinition());
            grid.ColumnDefinitions.Add(new ColumnDefinition());

            grid.ColumnDefinitions[1].Width = new GridLength(0,
                GridUnitType.Auto);
            grid.RowDefinitions[1].Height = new GridLength(0,
                GridUnitType.Auto);

            Content = grid;

            Thickness margin = new Thickness(5, 5, 5, 5);

            ListBox listbox = new ListBox();
            listbox.Margin = margin;
            grid.Children.Add(listbox);
            listbox.SetValue(Grid.RowProperty, 0);
            listbox.SetValue(Grid.ColumnProperty, 0);
            listbox.SetValue(Grid.ColumnSpanProperty, 2);

            CheckBox chBox = new CheckBox();
            chBox.Margin = margin;
            chBox.Content = "Do work";
            grid.Children.Add(chBox);
            chBox.SetValue(Grid.ColumnProperty, 0);
            chBox.SetValue(Grid.RowProperty, 1);

            Task task = null;
            long i = 0;

            chBox.Checked += (s, e) => {

                task = new Task(() => {
                    using (task) {
                        i = 0;
                        while (Dispatcher.Invoke(
                            () => chBox.IsChecked == true) &&
                            i < long.MaxValue) {

                            // В рабочем потоке выполняем
                            // некоторую работу. Например -
                            // формируем строку текущих даты и
                            // времени.
                            string value = DateTime.Now
                                .ToString("yyyy-MM-dd hh:mm:ss"
                                );

                            // В данном примере мы мы можем
                            // имитировать длительную работу,
                            // либо заблокировать эту строку
                            // кода, если такая имитация нам не
                            // нужна:
                            //Thread.Sleep(TimeSpan.FromSeconds
                            //    (1));

                            Console.WriteLine(
                                "Current thread Id: {0}. " +
                                "Is pull thread: {1}",
                                Thread.CurrentThread
                                .ManagedThreadId.ToString(),
                                Thread.CurrentThread
                                .IsThreadPoolThread);

                            // Результат наших "вычислений"
                            //записываем в поток UI
                            context.Post(_ => {
                                listbox.Items.Add(value);
                                Title = string.Format(
                                    "{0}. Items Count: {1}",
                                prefix, i++.ToString());

                                Console.WriteLine(
                                "Current thread Id: {0}. " +
                                "Is pull thread: {1}",
                                Thread.CurrentThread
                                .ManagedThreadId.ToString(),
                                Thread.CurrentThread
                                .IsThreadPoolThread);
                            }, null);
                        }
                    }
                });

                task.Start();
            };

            chBox.IsChecked = false;

            Button button = new Button();
            button.Content = "Clear";
            button.Margin = margin;
            button.Padding = margin;
            grid.Children.Add(button);
            button.SetValue(Grid.ColumnProperty, 1);
            button.SetValue(Grid.RowProperty, 1);
            button.Click += (s, e) => {
                listbox.Items.Clear();
                Title = prefix;
                i = 0;
            };

            EventHandler action = null;

            action = (s, e) => {
                context = SynchronizationContext.Current;
                Activated -= action;
            };

            Activated += action;

            NameScope.SetNameScope(this, new NameScope());
            RegisterName(nameof(listbox), listbox);
            RegisterName(nameof(chBox), chBox);
            RegisterName(nameof(button), button);
        }
    }

    class Sandbox {
        [STAThread]
        static void Main(string[] args) {

            MyWindow win = new MyWindow();
            Application app = new Application();
            app.Run(win);
        }
    }
}


Результат работы кода выглядит следующим образом:

 

Многопоточность и GUI

Маленький пример использования многопоточности в приложениях, содержащих графический пользовательский интерфейс (GUI). Продемонстрировано два способа обращения к элементам пользовательского интерфейса из рабочего потока в UI-поток. Графический интерфейс при этом не "подвисает".

.Net Framework 4.5

 /* Sandbox.cs
 * © Andrey Bushman, 2017
 *
 * Небольшой пример создания дополнительного потока,
 * работающего параллельно с потоком пользовательского
 * интерфейса (UI) и обновляющего этот интерфейс по мере
 * необходимости. Дополнительных потоков можно создавать
 * сколько угодно. В данном примере для простоты создаётся
 * только один.
 *
 * В данном примере вместо прямого использоватия потока
 * (Thread) я использую задачу (Task), которая в свою очередь
 * использует пул потоков.
 *
 * Рабочий поток (т.е. задача) вычисляет текущую дату и время,
 * после чего записывает их в ListBox, находящийся в потоке UI.
 *
 * Снятие/установка галочки "Do work" управляет
 * стартом/завершением рабочего потока. Кнопка "Clear" очищает
 * ListBox и заголовок окна.
 *
 * В консоль выводятся идентификаторы текущих потоков и маркер
 * их принадлежности (или не принадлежности) к пулу потоков.
 *
 * В данном примере для обращения к потоку UI из рабочего
 * потока я использую два способа: диспетчер и контекст
 * синхронизации.
 *
 * В примере используется WPF, но всё то же самое применимо к
 * WinForms и ASP.NET.
 */
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace Bushman.Sandbox {

    class MyWindow : Window {

        SynchronizationContext context = null;

        public MyWindow() : base() {

            Console.Title = "Sandbox";
            string prefix = "Main Window";
            Title = prefix;
            Topmost = true;

            Width = 300;
            Height = 600;

            ResizeMode = ResizeMode.NoResize;

            WindowStartupLocation = WindowStartupLocation
                .CenterScreen;

            Grid grid = new Grid();
            grid.RowDefinitions.Add(new RowDefinition());
            grid.RowDefinitions.Add(new RowDefinition());
            grid.ColumnDefinitions.Add(new ColumnDefinition());
            grid.ColumnDefinitions.Add(new ColumnDefinition());

            grid.ColumnDefinitions[1].Width = new GridLength(0,
                GridUnitType.Auto);
            grid.RowDefinitions[1].Height = new GridLength(0,
                GridUnitType.Auto);

            Content = grid;

            Thickness margin = new Thickness(5, 5, 5, 5);

            ListBox listbox = new ListBox();
            listbox.Margin = margin;
            grid.Children.Add(listbox);
            listbox.SetValue(Grid.RowProperty, 0);
            listbox.SetValue(Grid.ColumnProperty, 0);
            listbox.SetValue(Grid.ColumnSpanProperty, 2);

            CheckBox chBox = new CheckBox();
            chBox.Margin = margin;
            chBox.Content = "Do work";
            grid.Children.Add(chBox);
            chBox.SetValue(Grid.ColumnProperty, 0);
            chBox.SetValue(Grid.RowProperty, 1);

            Task task = null;
            long i = 0;

            chBox.Checked += (s, e) => {

                task = new Task(() => {
                    using (task) {
                        i = 0;
                        while (Dispatcher.Invoke(
                            () => chBox.IsChecked == true) &&
                            i < long.MaxValue) {

                            // В рабочем потоке выполняем
                            // некоторую работу. Например -
                            // формируем строку текущих даты и
                            // времени.
                            string value = DateTime.Now
                                .ToString("yyyy-MM-dd hh:mm:ss"
                                );

                            // В данном примере мы мы можем
                            // имитировать длительную работу,
                            // либо заблокировать эту строку
                            // кода, если такая имитация нам не
                            // нужна:
                            //Thread.Sleep(TimeSpan.FromSeconds
                            //    (1));

                            Console.WriteLine(
                                "Current thread Id: {0}. " +
                                "Is pull thread: {1}",
                                Thread.CurrentThread
                                .ManagedThreadId.ToString(),
                                Thread.CurrentThread
                                .IsThreadPoolThread);

                            // Результат наших "вычислений"
                            //записываем в поток UI
                            context.Post(_ => {
                                listbox.Items.Add(value);
                                Title = string.Format(
                                    "{0}. Items Count: {1}",
                                prefix, i++.ToString());

                                Console.WriteLine(
                                "Current thread Id: {0}. " +
                                "Is pull thread: {1}",
                                Thread.CurrentThread
                                .ManagedThreadId.ToString(),
                                Thread.CurrentThread
                                .IsThreadPoolThread);
                            }, null);
                        }
                    }
                });

                task.Start();
            };

            chBox.IsChecked = false;

            Button button = new Button();
            button.Content = "Clear";
            button.Margin = margin;
            button.Padding = margin;
            grid.Children.Add(button);
            button.SetValue(Grid.ColumnProperty, 1);
            button.SetValue(Grid.RowProperty, 1);
            button.Click += (s, e) => {
                listbox.Items.Clear();
                Title = prefix;
                i = 0;
            };

            EventHandler action = null;

            action = (s, e) => {
                context = SynchronizationContext.Current;
                Activated -= action;
            };

            Activated += action;

            NameScope.SetNameScope(this, new NameScope());
            RegisterName(nameof(listbox), listbox);
            RegisterName(nameof(chBox), chBox);
            RegisterName(nameof(button), button);
        }
    }

    class Sandbox {
        [STAThread]
        static void Main(string[] args) {

            MyWindow win = new MyWindow();
            Application app = new Application();
            app.Run(win);
        }
    }
}


Результат работы кода выглядит следующим образом:

 

О том, как можно генерировать локализованные версии справки

Данная заметка рассказывает о том, как можно генерировать локализованные версии справочной системы на основе нашего программного кода (в данном случае написанном на C#). Если кто-то знает более удобный способ - с интересом почитаю о нём в комментариях к этой заметке.

 Введение
Как известно, исходными данными для генерации справки являются XML-файлы, генерируемые MS Visual Studio. Эти файлы создаются в том случае, если в настройках нашего проекта, на вкладке Build установлена галочка XML documentation file. Эти же файлы используются технологией IntelliSense для того, чтобы выдавать всплывающие подсказки в редакторе кода, являясь весьма удобной и всеми нами любимой особенностью. 

 Обозначенные выше XML-файлы генерируются на основе специально оформленных комментариев, присутствующих в нашем коде. Такими комментариями обычно помечаются классы, свойства, методы, делегаты, события, структуры и перечисления. Полный перечень допустимых XML-тегов с примерами их использования опубликован в MSDN.

Для того, чтобы иметь возможность генерировать документацию для разных локализаций (например для русской и английской), XML-комментарии следует выносить во внешние файлы, вместо того, чтобы размещать их непосредственно в наших файлах исходного кода. Более того, для каждой интересующей нас локализации в составе нашего решения следует создавать отдельный проект. Такой способ, помимо прочего, способствует разделению процесса разработки приложения от разработки документации. Т.е. в то время пока один разработчик пишет программный код, другой в это же время может параллельно заниматься составлением русскоязычной документации, а третий - англоязычной.

Например, если целевой проект имеет имя HelloDocs, то для русской и английской версии справочной системы мы добавим в наш проект два новых решения - HelloDocs.Ru и HelloDocs.En (на основе шаблона Class Library). В этих проектах мы будем размещать все XML-файлы: в HelloDocs.En - англоязычная версия, а в HelloDocs.Ru - русскоязычная.
 
Затем в настройках обоих проектов следует указать один и тот же(!!!) каталог вывода: в настройках проекта, на вкладке Build в свойстве Output path. А для всех добавляемых (в дальнейшем) нами в эти проекты XML-файлов свойству Copy to Output Directory следует обязательно назначать значение copy always.

Вот пример исходного кода, определённого в составе файла Magic.cs проекта HelloDocs:

namespace HelloDocs {



    /// <include file='doc/Magic.xml'

    /// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>

    public classMagic {

        /// <include file='doc/Magic.xml'

        /// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>

        public void Foo() { }

    }

}

Соответствующие ему XML-файлы с комментариями будут показаны ниже в данной заметке.
Обратите внимание на то, что в атрибутах file элементов include следует указывать файлы, размещённые не в каталогах локализованных проектов, созданных нами ранее, но файлы размещённые в директории, которую мы выше указали в качестве выходного каталога для локализованных проектов, добавленных нами. На данный момент в том каталоге ничего нет, но это временно, т.к. в процессе компиляции, нужные XML-файлы будут копироваться в тот каталог и Visual Studio найдёт их там в процессе сборки нашего проекта HelloDocs.

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


Затем в Менеджере Конфигураций для всех проектов локализованных справочных систем следует снять галочки, оставив только для одного из них. Поскольку в нашем примере локализованных версий будет две, то снимаем галочку только для одного проекта:


 
В проект HelloDocs.En добавим файл Magic.xml с таким содержимым:

<?xml version="1.0"encoding="utf-8" ?>

<Documentation>

  <Member Name="HelloDocs.Magic">

    <summary>

      The <c>HelloDocs.Magic</c> is super-class...

    </summary>

    <remarks>

      Late it will be very popularly :)

    </remarks>

  </Member>

  <Member Name="HelloDocs.Magic.Foo()">

    <summary>

      The super-method...

    </summary>

    <remarks>It does nothing still... :)</remarks>

  </Member>

</Documentation>

В проект HelloDocs.Ru добавим файл Magic.xml с таким содержимым:

<?xml version="1.0"encoding="utf-8" ?>

<Documentation>

  <Member Name="HelloDocs.Magic">

    <summary>

      Наш <c>HelloDocs.Magic</c> есть супер-класс...

    </summary>

    <remarks>

      Позднее он будет очень популярен! :)

    </remarks>

  </Member>

  <Member Name="HelloDocs.Magic.Foo()">

    <summary>

      Это супер-метод...

    </summary>

    <remarks>Пока он ничего не делает... :)</remarks>

  </Member>

</Documentation>

Не забываем для этих XML-файлов изменить значение свойства Copy to Output Directory, как было указано выше. Обратите внимание, что эти файлы имеют одинаковые имена и одинаковую XML-структуру. Различие состоит только в содержимом соответствующих XML-элементов (это важно!!!).

Теперь наше решение выглядит следующим образом:


XML-файлы, генерируемые Visual Studio на основе XML-файлов наших локализованных проектов теперь могут использоваться в Sandcastle для генерации конечной документации.

Если Sandcastle ещё не установлен на вашей машине, то теперь самое время это сделать. Для установки Sancastle требуются административные права. После установки, в перечне доступных типов проектов IDE появляется дополнительный тип проекта, при помощи которого можно генерировать справку для интересующего нас проекта. Добавим в наше решение новый проект, созданный на основе нужного нам шаблона и присвоим ему имя HelpDocs.Doc:




Создав проект справки, первым делом мы указываем в его настройках источник, откуда нужно будет получить данные для формирования документации:



Интересующий нас формат справки указывается в настройках нашего Sandcastle-проекта:

Теперь запустив сборку нашего решения мы получим не только наш DLL, но и сгенерированный файл справки:



На скрине красной линией подчёркнут английский текст нашего XML-файла. Теперь отключим сборку англоязычной справки и включим русскоязычную:




Затем в настройках проекта HelpDocs.Doc меняем свойство Help file language, выбрав в нём русскую локализацию (чтобы дополнительный текст, присутствующий в документации, был так же на русском языке):



Теперь снова запускаем сборку нашего решения и проверяем сгенерированный файл справки:



Как мы видим - теперь мы получили русский вариант справки. Т.о. при желании мы можем реализовать столько локализаций нашей справочной системы, сколько посчитаем нужным.

P.S.
В идеале, конечно же, было бы избавиться от необходимости переключения галочек и правки свойства нашего Sandcastle-проекта, путём создания такого проекта отдельно под каждую локализацию. Но проблема заключается в том, что в нашем исходном коде жёстко прописан каталог, в котором следует искать XML-файл.

Конечно, можно было бы решить эту проблему при помощи конструкций #if/#elif/#endif:

namespace HelloDocs {

#if ENU
/// <include file='Magic.doc.enu.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>
#elif RUS
/// <include file='Magic.doc.rus.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>
#endif
public class Magic {
#if ENU
/// <include file='Magic.doc.enu.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>
#elif RUS
/// <include file='Magic.doc.rus.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>
#endif
public void Foo() { }
}
}

Однако писать такие конструкции - процесс весьма трудоёмкий. К тому же, в случае добавления новой локализации придётся везде в коде добавлять дополнительный блок #elif, что может оказаться очень затратным по времени для больших и даже средних проектов.

Дополнительные ресурсы

О том, как можно генерировать локализованные версии справки

Данная заметка рассказывает о том, как можно генерировать локализованные версии справочной системы на основе нашего программного кода (в данном случае написанном на C#). Если кто-то знает более удобный способ - с интересом почитаю о нём в комментариях к этой заметке.

 Введение
Как известно, исходными данными для генерации справки являются XML-файлы, генерируемые MS Visual Studio. Эти файлы создаются в том случае, если в настройках нашего проекта, на вкладке Build установлена галочка XML documentation file. Эти же файлы используются технологией IntelliSense для того, чтобы выдавать всплывающие подсказки в редакторе кода, являясь весьма удобной и всеми нами любимой особенностью. 

 Обозначенные выше XML-файлы генерируются на основе специально оформленных комментариев, присутствующих в нашем коде. Такими комментариями обычно помечаются классы, свойства, методы, делегаты, события, структуры и перечисления. Полный перечень допустимых XML-тегов с примерами их использования опубликован в MSDN.

Для того, чтобы иметь возможность генерировать документацию для разных локализаций (например для русской и английской), XML-комментарии следует выносить во внешние файлы, вместо того, чтобы размещать их непосредственно в наших файлах исходного кода. Более того, для каждой интересующей нас локализации в составе нашего решения следует создавать отдельный проект. Такой способ, помимо прочего, способствует разделению процесса разработки приложения от разработки документации. Т.е. в то время пока один разработчик пишет программный код, другой в это же время может параллельно заниматься составлением русскоязычной документации, а третий - англоязычной.

Например, если целевой проект имеет имя HelloDocs, то для русской и английской версии справочной системы мы добавим в наш проект два новых решения - HelloDocs.Ru и HelloDocs.En (на основе шаблона Class Library). В этих проектах мы будем размещать все XML-файлы: в HelloDocs.En - англоязычная версия, а в HelloDocs.Ru - русскоязычная.
 
Затем в настройках обоих проектов следует указать один и тот же(!!!) каталог вывода: в настройках проекта, на вкладке Build в свойстве Output path. А для всех добавляемых (в дальнейшем) нами в эти проекты XML-файлов свойству Copy to Output Directory следует обязательно назначать значение copy always.

Вот пример исходного кода, определённого в составе файла Magic.cs проекта HelloDocs:

namespace HelloDocs {



    /// <include file='doc/Magic.xml'

    /// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>

    public classMagic {

        /// <include file='doc/Magic.xml'

        /// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>

        public void Foo() { }

    }

}

Соответствующие ему XML-файлы с комментариями будут показаны ниже в данной заметке.
Обратите внимание на то, что в атрибутах file элементов include следует указывать файлы, размещённые не в каталогах локализованных проектов, созданных нами ранее, но файлы размещённые в директории, которую мы выше указали в качестве выходного каталога для локализованных проектов, добавленных нами. На данный момент в том каталоге ничего нет, но это временно, т.к. в процессе компиляции, нужные XML-файлы будут копироваться в тот каталог и Visual Studio найдёт их там в процессе сборки нашего проекта HelloDocs.

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


Затем в Менеджере Конфигураций для всех проектов локализованных справочных систем следует снять галочки, оставив только для одного из них. Поскольку в нашем примере локализованных версий будет две, то снимаем галочку только для одного проекта:


 
В проект HelloDocs.En добавим файл Magic.xml с таким содержимым:

<?xml version="1.0"encoding="utf-8" ?>

<Documentation>

  <Member Name="HelloDocs.Magic">

    <summary>

      The <c>HelloDocs.Magic</c> is super-class...

    </summary>

    <remarks>

      Late it will be very popularly :)

    </remarks>

  </Member>

  <Member Name="HelloDocs.Magic.Foo()">

    <summary>

      The super-method...

    </summary>

    <remarks>It does nothing still... :)</remarks>

  </Member>

</Documentation>

В проект HelloDocs.Ru добавим файл Magic.xml с таким содержимым:

<?xml version="1.0"encoding="utf-8" ?>

<Documentation>

  <Member Name="HelloDocs.Magic">

    <summary>

      Наш <c>HelloDocs.Magic</c> есть супер-класс...

    </summary>

    <remarks>

      Позднее он будет очень популярен! :)

    </remarks>

  </Member>

  <Member Name="HelloDocs.Magic.Foo()">

    <summary>

      Это супер-метод...

    </summary>

    <remarks>Пока он ничего не делает... :)</remarks>

  </Member>

</Documentation>

Не забываем для этих XML-файлов изменить значение свойства Copy to Output Directory, как было указано выше. Обратите внимание, что эти файлы имеют одинаковые имена и одинаковую XML-структуру. Различие состоит только в содержимом соответствующих XML-элементов (это важно!!!).

Теперь наше решение выглядит следующим образом:


XML-файлы, генерируемые Visual Studio на основе XML-файлов наших локализованных проектов теперь могут использоваться в Sandcastle для генерации конечной документации.

Если Sandcastle ещё не установлен на вашей машине, то теперь самое время это сделать. Для установки Sancastle требуются административные права. После установки, в перечне доступных типов проектов IDE появляется дополнительный тип проекта, при помощи которого можно генерировать справку для интересующего нас проекта. Добавим в наше решение новый проект, созданный на основе нужного нам шаблона и присвоим ему имя HelpDocs.Doc:




Создав проект справки, первым делом мы указываем в его настройках источник, откуда нужно будет получить данные для формирования документации:



Интересующий нас формат справки указывается в настройках нашего Sandcastle-проекта:

Теперь запустив сборку нашего решения мы получим не только наш DLL, но и сгенерированный файл справки:



На скрине красной линией подчёркнут английский текст нашего XML-файла. Теперь отключим сборку англоязычной справки и включим русскоязычную:




Затем в настройках проекта HelpDocs.Doc меняем свойство Help file language, выбрав в нём русскую локализацию (чтобы дополнительный текст, присутствующий в документации, был так же на русском языке):



Теперь снова запускаем сборку нашего решения и проверяем сгенерированный файл справки:



Как мы видим - теперь мы получили русский вариант справки. Т.о. при желании мы можем реализовать столько локализаций нашей справочной системы, сколько посчитаем нужным.

P.S.
В идеале, конечно же, было бы избавиться от необходимости переключения галочек и правки свойства нашего Sandcastle-проекта, путём создания такого проекта отдельно под каждую локализацию. Но проблема заключается в том, что в нашем исходном коде жёстко прописан каталог, в котором следует искать XML-файл.

Конечно, можно было бы решить эту проблему при помощи конструкций #if/#elif/#endif:

namespace HelloDocs {

#if ENU
/// <include file='Magic.doc.enu.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>
#elif RUS
/// <include file='Magic.doc.rus.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic"]/*'/>
#endif
public class Magic {
#if ENU
/// <include file='Magic.doc.enu.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>
#elif RUS
/// <include file='Magic.doc.rus.xml'
/// path='Documentation/Member[@Name="HelloDocs.Magic.Foo()"]/*'/>
#endif
public void Foo() { }
}
}

Однако писать такие конструкции - процесс весьма трудоёмкий. К тому же, в случае добавления новой локализации придётся везде в коде добавлять дополнительный блок #elif, что может оказаться очень затратным по времени для больших и даже средних проектов.

Дополнительные ресурсы