Условная компиляция и цели платформы - PullRequest
119 голосов
/ 27 мая 2010

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

Что-то вроде:

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif

Любой из этих символов предоставляется бесплатно? Нужно ли вводить эти символы как часть конфигурации проекта? Кажется, это легко сделать, так как я буду знать, какой фреймворк предназначен для MSBuild.

/p:DefineConstants="NET40"

Обновление: Мой вопрос: как люди справляются с этой ситуацией? Вы создаете разные конфигурации? Вы передаете константы через командную строку?

Ответы [ 6 ]

117 голосов
/ 28 мая 2010

Один из лучших способов сделать это - создать различные конфигурации сборки в вашем проекте:

<PropertyGroup Condition="  '$(Framework)' == 'NET20' ">
  <DefineConstants>NET20</DefineConstants>
  <OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>


<PropertyGroup Condition="  '$(Framework)' == 'NET35' ">
  <DefineConstants>NET35</DefineConstants>
  <OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>

И в одной из ваших конфигураций по умолчанию:

<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>

Что бы установить значение по умолчанию, если оно не было определено где-либо еще. В вышеприведенном случае OutputPath будет давать вам отдельную сборку каждый раз, когда вы создаете каждую версию.

Затем создайте цель AfterBuild для компиляции ваших разных версий:

<Target Name="AfterBuild">
  <MSBuild Condition=" '$(Framework)' != 'NET20'"
    Projects="$(MSBuildProjectFile)"
    Properties="Framework=NET20"
    RunEachTargetSeparately="true"  />
</Target>

В этом примере будет перекомпилирован весь проект с переменной Framework, установленной в NET20 после первой сборки (компиляция обоих и при условии, что первая сборка была по умолчанию NET35 сверху). Каждая компиляция будет иметь правильно заданные значения условного определения.

Таким образом, вы можете даже исключить определенные файлы в файле проекта, если хотите, чтобы без них не было #ifdef:

<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />

или даже ссылки

<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
  <HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>
43 голосов
/ 16 декабря 2010

Альтернатива, которая работает для меня до сих пор, заключается в добавлении следующего к файлу проекта:

 <PropertyGroup>
    <DefineConstants Condition=" !$(DefineConstants.Contains(';NET')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
    <DefineConstants Condition=" $(DefineConstants.Contains(';NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
  </PropertyGroup>

Это принимает значение свойства TargetFrameworkVersion, которое похоже на «v3.5», заменяет «v» и «.» чтобы получить «NET35» (используя новую функцию Property Functions ). Затем он удаляет любое существующее значение «NETxx» и добавляет его в конец DefinedConstants. Может быть возможно упростить это, но у меня нет времени, чтобы возиться.

Глядя на вкладку Build свойств проекта в VS, вы увидите результирующее значение в разделе условных символов компиляции. После изменения версии целевой платформы на вкладке «Приложение» символ автоматически изменяется. Затем вы можете использовать директивы препроцессора #if NETxx обычным способом. Изменение проекта в VS не приводит к потере пользовательской PropertyGroup.

Обратите внимание, что это не дает вам ничего особенного для целевых параметров профиля клиента, но для меня это не проблема.

15 голосов
/ 05 октября 2012

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

<DefineConstants />
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>true</DebugSymbols>

Visual Studio 2010 также выдает ошибку из-за точек с запятой, утверждая, что они являются недопустимыми символами. Сообщение об ошибке дало мне подсказку, так как я мог видеть предварительно созданные константы, разделенные запятыми, за которыми в конце концов следовала моя «нелегальная» точка с запятой. После некоторого переформатирования и массирования я смог найти решение, которое работает для меня.

<PropertyGroup>
  <!-- Adding a custom constant will auto-magically append a comma and space to the pre-built constants.    -->
  <!-- Move the comma delimiter to the end of each constant and remove the trailing comma when we're done.  -->
  <DefineConstants Condition=" !$(DefineConstants.Contains(', NET')) ">$(DefineConstants)$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
  <DefineConstants Condition=" $(DefineConstants.Contains(', NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", NET"))))$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
  <DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 2.0 ">$(DefineConstants)NET_20_OR_GREATER, </DefineConstants>
  <DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 3.5 ">$(DefineConstants)NET_35_OR_GREATER</DefineConstants>
  <DefineConstants Condition=" $(DefineConstants.EndsWith(', ')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", "))))</DefineConstants>
</PropertyGroup>

Я бы опубликовал снимок экрана с диалоговым окном «Дополнительные параметры компилятора» (открывается при нажатии кнопки «Дополнительные параметры компиляции ...» на вкладке «Компиляция» вашего проекта). Но, как новый пользователь, мне не хватает представителя, чтобы сделать это. Если бы вы могли видеть скриншот, вы бы увидели пользовательские константы, автоматически заполненные группой свойств, а затем сказали бы: «Мне нужно кое-что из этого».


РЕДАКТИРОВАТЬ: Получил этот представитель на удивление быстро .. Спасибо, ребята! Вот этот скриншот:

Advanced Compiler Settings

4 голосов
/ 27 февраля 2015

Начните с очистки констант:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

Далее создайте отладку, трассировку и другие константы, такие как:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
    <DebugSymbols>true</DebugSymbols>
  <DebugType>full</DebugType>
  <Optimize>false</Optimize>
  <DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
</PropertyGroup>

Наконец, создайте свои константы каркаса:

<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">
  <DefineConstants>NET10;NET20;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.0' ">
  <DefineConstants>NET10;NET20;NET30;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.5' ">
  <DefineConstants>NET10;NET20;NET30;NET35;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
  <DefineConstants>NET10;NET20;NET30;NET35;NET40;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">
  <DefineConstants>NET10;NET20;NET30;NET35;NET40;NET45;$(DefineConstants)</DefineConstants>
</PropertyGroup>

Я думаю, что этот подход очень читабелен и понятен.

2 голосов
/ 02 ноября 2011

@ Azarien, ваш ответ может быть объединен с ответом Джереми, чтобы хранить его в одном месте, а не в Debug | Release и т. Д.

Для меня объединение обоих вариантов работает лучше всего, т.е. включает условия в коде с использованием #if NETXX, а также сборку для различных версий фреймворка за один раз.

У меня есть это в моем файле .csproj:

  <PropertyGroup>
    <DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' ">NET_40_OR_GREATER</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' == '3.5' ">
    <DefineConstants>NET35</DefineConstants>
    <OutputPath>bin\$(Configuration)\$(TargetFrameworkVersion)</OutputPath>
  </PropertyGroup>

и в целях:

  <Target Name="AfterBuild">
    <MSBuild Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' "
      Projects="$(MSBuildProjectFile)"
      Properties="TargetFrameworkVersion=v3.5"
      RunEachTargetSeparately="true"  />
  </Target>
2 голосов
/ 25 октября 2011

В файле .csproj после существующей строки <DefineConstants>DEBUG;TRACE</DefineConstants> добавьте:

<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' ">NET_40_OR_GREATER</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' == '4.0' ">NET_40_EXACTLY</DefineConstants>

Сделайте это для конфигураций сборки Debug и Release. Тогда используйте в своем коде:

#if NET_40_OR_GREATER
   // can use dynamic, default and named parameters
#endif
...