Как установить переменную условной компиляции?

В C/C++ вы можете определять макросы в коде так:

#define OLD_WAY  1

Хотя я никогда не делал этого, я предполагаю, что то же самое доступно в C#. Более того, в C/C++ можно затем сделать некоторую логику условной компиляции, выполнив что-то вроде этого:

#if OLD_WAY == 1
 int i = 0;
#else
 int i = 1;
#endif

ОК, это все круто и все такое. И снова я предполагаю, что такая логика возможна в C#. Что я хотел бы знать, так это как определить константы на уровне проекта, чтобы я мог использовать логику, которая позволит мне условно компилировать один блок кода, если я определю константу в одну сторону, или другой блок кода если я не определю это так? Я предполагаю, что это сделано где-то в свойствах проекта, но как и где я могу это определить?

3 ответа

Решение

Компилятор C# csc.exe и сам язык C# не предоставляет никаких предопределенных констант для условной компиляции. Visual Studio только добавляет DEBUG а также TRACE значения, которые можно настроить через IDE. Среда IDE также позволяет добавлять собственные произвольные символы, но, поскольку они являются по существу фиксированными (инвариантными) значениями, возможности ограничены.

Более мощные пользовательские параметры можно настроить, отредактировав вручную .csproj файл проекта. Здесь вы можете настроить условия для выборочного распространения символов условной компиляции в C# на основе огромного количества информации о среде и конфигурации, доступной в MSBuild (см. Здесь и здесь, но в принципе не может быть полного списка, поскольку разрозненные компоненты произвольно предоставляют метаданные ad-hoc).

Давайте рассмотрим рабочий пример. Один из случаев, когда полезно условно компилировать, - это если вы хотите написать код, который адаптируется к любым инструментам, обнаруженным во время сборки. Таким образом, вы можете использовать новейшие языковые функции, сохраняя при этом возможность компилирования на машинах с более старыми инструментами, которые, как и ожидалось, отклонят инопланетный синтаксис и / или ключевые слова. Для конкретного случая C# 7.0 в Visual Studio 2017 мы можем изменить .csproj следующее:

Файл.csproj (выдержка):

Вы также можете идентифицировать каждый из старых компиляторов C#, постепенно снижая качество. То же самое касается обнаружения версии .NET Framework (часто запрашивается в Stackru [1] [2] [3] [4]) и любых других окружающих условий сборки. Это оставлено в качестве упражнений для читателя, но в случае, если вы хотите скопировать / вставить выделенные строки сверху, вот текстовая версия. Как обновление скриншота, я добавил здесь условные выражения в одинарные кавычки (хотя казалось, что все работает без них)

<DefineConstants Condition="'$(VisualStudioVersion)'=='15'">CSHARP7</DefineConstants>
<!-- ... -->
<DefineConstants>DEBUG;TRACE;$(DefineConstants)</DefineConstants>
<!-- ... -->
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>

В любом случае, теперь вы можете написать условный код C#, используя #if… #elif… #else… #endif, Продолжая пример, в приведенном ниже коде используется новый синтаксис кортежа - доступный только в C# 7- для замены элементов массива. Кстати, версия кортежа не только более лаконична и / или элегантна; он также производит отличный код IL:

#if CSHARP7
    (rg[i], rg[j]) = (rg[j], rg[i]);  // swap elements: tuple syntax
#else
    var t = rg[i];                    // swap elements: clunky
    rg[i] = rg[j];
    rg[j] = t;
#endif

Обратите внимание, что Visual Studio IDE правильно обрабатывает ваше руководство .csproj настройки во всех отношениях. Учитывая .csproj Как было показано ранее, редактор кода IDE правильно распознает и оценивает условную компиляцию для целей IntelliSense, refactoring неактивные блоки кода и т. д.

Я также упомянул, что MSBuild имеет сокровищницу информации, из которых $(VisualStudioVersion) был только один пример. К сожалению, нелегко узнать, какие значения доступны и какие они могут иметь во время сборки. Хитрость заключается в том, чтобы временно поставить C++ проект в ваш Visual Studio решение (если у вас его еще нет) вместе с вашим проектом C#. Если вы щелкните правой кнопкой мыши свойства проекта для этого .vcxproj а затем посмотрите (например) "Дополнительные каталоги включения" на C/C++ страницы, в правом нижнем углу появится выпадающий список, когда вы нажмете для редактирования:

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

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


[edit:] Другой способ узнать, какие значения свойств доступны во время и во время процесса сборки, - установить для MSBuild "подробность вывода" значение "Подробно", а затем перестроить.

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

В C# вы можете сделать #define но вы не можете использовать значения на них, как в C++.
Каждое определение может иметь 2 состояния: определено или не определено

В свойствах проекта в Build вы можете задать определения, которые должны быть определены.
Все, что вы укажете здесь, будет определено во всех ваших файлах проекта.

Так, например, я могу определить 2 условных символа компиляции в этом поле как:
MY_DEFINE1, MY_DEFINE2

Тогда в моем коде я могу сделать такие вещи:

#if MY_DEFINE1
//do something conditionally
#endif

#if MY_DEFINE2
//do something else conditionally
#endif

В качестве альтернативы вы можете делать свои определения для каждого файла, но в отличие от C++ они должны быть в верхней части вашего файла.

В верхней части вашего файла вы можете использовать:

#define MY_DEFINE2

или вы можете в верхней части вашего файла использовать:

#undef MY_DEFINE2

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

Откройте свойства вашего проекта и посмотрите на страницу Build. Есть поле под названием Условные символы компиляции.

Другие вопросы по тегам